Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / tests / callbacks.py @ 326c3ec8

History | View | Annotate | Download (29.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, IPAddress, 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 django.conf import settings
48
from synnefo.logic.rapi import GanetiApiError
49

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

    
54

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

    
84
    def test_wrong_type(self, client):
85
        msg = self.create_msg(type="WRONG_TYPE")
86
        update_db(client, msg)
87
        self.assertTrue(client.basic_nack.called)
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
        self.assertTrue(client.basic_ack.called)
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
        with mocked_quotaholder():
112
            update_db(client, msg)
113
        self.assertTrue(client.basic_ack.called)
114
        db_vm = VirtualMachine.objects.get(id=vm.id)
115
        self.assertEqual(db_vm.operstate, 'STARTED')
116

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

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

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

    
179
    @patch("synnefo.logic.rapi_pool.GanetiRapiClient")
180
    def test_remove_error(self, rapi, client):
181
        vm = mfactory.VirtualMachineFactory()
182
        # Also create a NIC
183
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
184
                              status="error",
185
                              instance=vm.backend_vm_id)
186
        rapi().GetInstance.return_value = {}
187
        update_db(client, msg)
188
        db_vm = VirtualMachine.objects.get(id=vm.id)
189
        self.assertFalse(db_vm.deleted)
190

    
191
        rapi().GetInstance.side_effect = GanetiApiError(msg="msg",
192
                                                        code=503)
193
        update_db(client, msg)
194
        db_vm = VirtualMachine.objects.get(id=vm.id)
195
        self.assertFalse(db_vm.deleted)
196

    
197
        rapi().GetInstance.side_effect = GanetiApiError(msg="msg",
198
                                                        code=404)
199
        with mocked_quotaholder():
200
            update_db(client, msg)
201
        db_vm = VirtualMachine.objects.get(id=vm.id)
202
        self.assertTrue(db_vm.deleted)
203

    
204
    def test_create(self, client):
205
        vm = mfactory.VirtualMachineFactory()
206
        msg = self.create_msg(operation='OP_INSTANCE_CREATE',
207
                              instance=vm.backend_vm_id)
208
        update_db(client, msg)
209
        self.assertTrue(client.basic_ack.called)
210
        db_vm = VirtualMachine.objects.get(id=vm.id)
211
        self.assertEqual(db_vm.operstate, 'STARTED')
212

    
213
    def test_create_error(self, client):
214
        """Test that error create sets vm to ERROR state"""
215
        vm = mfactory.VirtualMachineFactory()
216
        msg = self.create_msg(operation='OP_INSTANCE_CREATE',
217
                              instance=vm.backend_vm_id,
218
                              status='error')
219
        update_db(client, msg)
220
        self.assertTrue(client.basic_ack.called)
221
        db_vm = VirtualMachine.objects.get(id=vm.id)
222
        self.assertEqual(db_vm.operstate, 'ERROR')
223

    
224
    def test_remove_from_error(self, client):
225
        """Test that error removes delete error builds"""
226
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
227
        # Also create a NIC
228
        mfactory.NetworkInterfaceFactory(machine=vm)
229
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
230
                              instance=vm.backend_vm_id)
231
        with mocked_quotaholder():
232
            update_db(client, msg)
233
        self.assertTrue(client.basic_ack.called)
234
        db_vm = VirtualMachine.objects.get(id=vm.id)
235
        self.assertEqual(db_vm.operstate, 'DESTROYED')
236
        self.assertTrue(db_vm.deleted)
237
        # Check that nics are deleted
238
        self.assertFalse(db_vm.nics.all())
239

    
240
    def test_other_error(self, client):
241
        """Test that other error messages do no affect the VM"""
242
        vm = mfactory.VirtualMachineFactory()
243
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
244
                              instance=vm.backend_vm_id,
245
                              status='error')
246
        update_db(client, msg)
247
        self.assertTrue(client.basic_ack.called)
248
        db_vm = VirtualMachine.objects.get(id=vm.id)
249
        self.assertEqual(db_vm.operstate, vm.operstate)
250
        self.assertEqual(db_vm.backendtime, vm.backendtime)
251

    
252
    def test_resize_msg(self, client):
253
        vm = mfactory.VirtualMachineFactory()
254
        # Test empty beparams
255
        for status in ["success", "error"]:
256
            msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
257
                                  instance=vm.backend_vm_id,
258
                                  beparams={},
259
                                  status=status)
260
            client.reset_mock()
261
            with mocked_quotaholder():
262
                update_db(client, msg)
263
            self.assertTrue(client.basic_ack.called)
264
            db_vm = VirtualMachine.objects.get(id=vm.id)
265
            self.assertEqual(db_vm.operstate, vm.operstate)
266
        # Test intermediate states
267
        vm.operstate = "STOPPED"
268
        vm.save()
269
        for status in ["queued", "waiting", "running"]:
270
            msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
271
                                  instance=vm.backend_vm_id,
272
                                  beparams={"vcpus": 4, "minmem": 2048,
273
                                            "maxmem": 2048},
274
                                  status=status)
275
            client.reset_mock()
276
            update_db(client, msg)
277
            self.assertTrue(client.basic_ack.called)
278
            db_vm = VirtualMachine.objects.get(id=vm.id)
279
            self.assertEqual(db_vm.operstate, "STOPPED")
280
        # Test operstate after error
281
        msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
282
                              instance=vm.backend_vm_id,
283
                              beparams={"vcpus": 4},
284
                              status="error")
285
        client.reset_mock()
286
        with mocked_quotaholder():
287
            update_db(client, msg)
288
        self.assertTrue(client.basic_ack.called)
289
        db_vm = VirtualMachine.objects.get(id=vm.id)
290
        self.assertEqual(db_vm.operstate, "STOPPED")
291
        # Test success
292
        f1 = mfactory.FlavorFactory(cpu=4, ram=1024, disk_template="drbd",
293
                                    disk=1024)
294
        vm.flavor = f1
295
        vm.save()
296
        f2 = mfactory.FlavorFactory(cpu=8, ram=2048, disk_template="drbd",
297
                                    disk=1024)
298
        msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
299
                              instance=vm.backend_vm_id,
300
                              beparams={"vcpus": 8, "minmem": 2048,
301
                                        "maxmem": 2048},
302
                              status="success")
303
        client.reset_mock()
304
        with mocked_quotaholder():
305
            update_db(client, msg)
306
        self.assertTrue(client.basic_ack.called)
307
        db_vm = VirtualMachine.objects.get(id=vm.id)
308
        self.assertEqual(db_vm.operstate, "STOPPED")
309
        self.assertEqual(db_vm.flavor, f2)
310
        msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
311
                              instance=vm.backend_vm_id,
312
                              beparams={"vcpus": 100, "minmem": 2048,
313
                                        "maxmem": 2048},
314
                              status="success")
315
        client.reset_mock()
316
        with mocked_quotaholder():
317
            update_db(client, msg)
318
        self.assertTrue(client.basic_reject.called)
319

    
320

    
321
@patch('synnefo.lib.amqp.AMQPClient')
322
class UpdateNetTest(TestCase):
323
    def create_msg(self, **kwargs):
324
        """Create snf-ganeti-hook message"""
325
        msg = {'event_time': split_time(time())}
326
        msg['type'] = 'ganeti-op-status'
327
        msg['operation'] = 'OP_INSTANCE_SET_PARAMS'
328
        msg['status'] = 'success'
329
        msg['jobId'] = 1
330
        msg['logmsg'] = 'Dummy Log'
331
        for key, val in kwargs.items():
332
            msg[key] = val
333
        message = {'body': json.dumps(msg)}
334
        return message
335

    
336
    def test_missing_attribute(self, client):
337
        update_db(client, json.dumps({'body': {}}))
338
        self.assertTrue(client.basic_reject.called)
339

    
340
    def test_unhandled_exception(self, client):
341
        update_db(client, {})
342
        client.basic_reject.assert_called_once()
343

    
344
    def test_wrong_type(self, client):
345
        msg = self.create_msg(type="WRONG_TYPE")
346
        update_db(client, msg)
347
        self.assertTrue(client.basic_nack.called)
348

    
349
    def test_missing_instance(self, client):
350
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
351
                              instance='foo')
352
        update_db(client, msg)
353
        self.assertTrue(client.basic_ack.called)
354

    
355
    def test_no_nics(self, client):
356
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
357
        mfactory.NetworkInterfaceFactory(machine=vm)
358
        mfactory.NetworkInterfaceFactory(machine=vm)
359
        mfactory.NetworkInterfaceFactory(machine=vm)
360
        self.assertEqual(len(vm.nics.all()), 3)
361
        msg = self.create_msg(nics=[],
362
                              instance=vm.backend_vm_id)
363
        update_db(client, msg)
364
        self.assertTrue(client.basic_ack.called)
365
        db_vm = VirtualMachine.objects.get(id=vm.id)
366
        self.assertEqual(len(db_vm.nics.all()), 0)
367

    
368
    def test_empty_nic(self, client):
369
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
370
        for public in [True, False]:
371
            net = mfactory.NetworkFactory(public=public, subnet6=None)
372
            msg = self.create_msg(nics=[{'network': net.backend_id}],
373
                                  instance=vm.backend_vm_id)
374
            update_db(client, msg)
375
            self.assertTrue(client.basic_ack.called)
376
            db_vm = VirtualMachine.objects.get(id=vm.id)
377
            nics = db_vm.nics.all()
378
            self.assertEqual(len(nics), 1)
379
            self.assertEqual(nics[0].index, 0)
380
            self.assertEqual(nics[0].ipv4, None)
381
            self.assertEqual(nics[0].ipv6, None)
382
            self.assertEqual(nics[0].mac, None)
383
            if public:
384
                self.assertEqual(nics[0].firewall_profile,
385
                                 settings.DEFAULT_FIREWALL_PROFILE)
386
            else:
387
                self.assertEqual(nics[0].firewall_profile, None)
388

    
389
    def test_full_nic(self, client):
390
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
391
        net = mfactory.NetworkFactory(subnet='10.0.0.0/24', subnet6=None)
392
        pool = net.get_pool()
393
        self.assertTrue(pool.is_available('10.0.0.22'))
394
        pool.save()
395
        msg = self.create_msg(nics=[{'network': net.backend_id,
396
                                     'ip': '10.0.0.22',
397
                                     'mac': 'aa:bb:cc:00:11:22'}],
398
                              instance=vm.backend_vm_id)
399
        update_db(client, msg)
400
        self.assertTrue(client.basic_ack.called)
401
        db_vm = VirtualMachine.objects.get(id=vm.id)
402
        nics = db_vm.nics.all()
403
        self.assertEqual(len(nics), 1)
404
        self.assertEqual(nics[0].index, 0)
405
        self.assertEqual(nics[0].ipv4, '10.0.0.22')
406
        self.assertEqual(nics[0].ipv6, None)
407
        self.assertEqual(nics[0].mac, 'aa:bb:cc:00:11:22')
408
        pool = net.get_pool()
409
        self.assertFalse(pool.is_available('10.0.0.22'))
410
        pool.save()
411

    
412

    
413
@patch('synnefo.lib.amqp.AMQPClient')
414
class UpdateNetworkTest(TestCase):
415
    def create_msg(self, **kwargs):
416
        """Create snf-ganeti-eventd message"""
417
        msg = {'event_time': split_time(time())}
418
        msg['type'] = 'ganeti-network-status'
419
        msg['status'] = 'success'
420
        msg['jobId'] = 1
421
        msg['logmsg'] = 'Dummy Log'
422
        for key, val in kwargs.items():
423
            msg[key] = val
424
        message = {'body': json.dumps(msg)}
425
        return message
426

    
427
    def test_missing_attribute(self, client):
428
        update_network(client, json.dumps({'body': {}}))
429
        self.assertTrue(client.basic_reject.called)
430

    
431
    def test_unhandled_exception(self, client):
432
        update_network(client, {})
433
        client.basic_reject.assert_called_once()
434

    
435
    def test_wrong_type(self, client):
436
        msg = self.create_msg(type="WRONG_TYPE")
437
        update_network(client, msg)
438
        self.assertTrue(client.basic_nack.called)
439

    
440
    def test_missing_network(self, client):
441
        msg = self.create_msg(operation='OP_NETWORK_CREATE',
442
                              network='foo')
443
        update_network(client, msg)
444
        self.assertTrue(client.basic_ack.called)
445

    
446
    def test_create(self, client):
447
        back_network = mfactory.BackendNetworkFactory(operstate='PENDING')
448
        net = back_network.network
449
        net.state = 'ACTIVE'
450
        net.save()
451
        back1 = back_network.backend
452

    
453
        back_network2 = mfactory.BackendNetworkFactory(operstate='PENDING',
454
                                                       network=net)
455
        back2 = back_network2.backend
456
        # Message from first backend network
457
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
458
                              network=net.backend_id,
459
                              cluster=back1.clustername)
460
        update_network(client, msg)
461
        self.assertTrue(client.basic_ack.called)
462

    
463
        back_net = BackendNetwork.objects.get(id=back_network.id)
464
        self.assertEqual(back_net.operstate, 'ACTIVE')
465
        db_net = Network.objects.get(id=net.id)
466
        self.assertEqual(db_net.state, 'ACTIVE')
467
        # msg from second backend network
468
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
469
                              network=net.backend_id,
470
                              cluster=back2.clustername)
471
        update_network(client, msg)
472
        self.assertTrue(client.basic_ack.called)
473

    
474
        db_net = Network.objects.get(id=net.id)
475
        self.assertEqual(db_net.state, 'ACTIVE')
476
        back_net = BackendNetwork.objects.get(id=back_network.id)
477
        self.assertEqual(back_net.operstate, 'ACTIVE')
478

    
479
    def test_create_offline_backend(self, client):
480
        """Test network creation when a backend is offline"""
481
        net = mfactory.NetworkFactory(state='ACTIVE')
482
        bn1 = mfactory.BackendNetworkFactory(network=net)
483
        mfactory.BackendNetworkFactory(network=net,
484
                                       backend__offline=True)
485
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
486
                              network=net.backend_id,
487
                              cluster=bn1.backend.clustername)
488
        update_network(client, msg)
489
        self.assertTrue(client.basic_ack.called)
490
        new_net = Network.objects.get(id=net.id)
491
        self.assertEqual(new_net.state, 'ACTIVE')
492

    
493
    def test_disconnect(self, client):
494
        bn1 = mfactory.BackendNetworkFactory(operstate='ACTIVE')
495
        net1 = bn1.network
496
        net1.state = "ACTIVE"
497
        net1.state = 'ACTIVE'
498
        net1.save()
499
        bn2 = mfactory.BackendNetworkFactory(operstate='ACTIVE',
500
                                             network=net1)
501
        msg = self.create_msg(operation='OP_NETWORK_DISCONNECT',
502
                              network=net1.backend_id,
503
                              cluster=bn2.backend.clustername)
504
        update_network(client, msg)
505
        self.assertTrue(client.basic_ack.called)
506
        self.assertEqual(Network.objects.get(id=net1.id).state, 'ACTIVE')
507
        self.assertEqual(BackendNetwork.objects.get(id=bn2.id).operstate,
508
                         'PENDING')
509

    
510
    def test_remove(self, client):
511
        mfactory.MacPrefixPoolTableFactory()
512
        mfactory.BridgePoolTableFactory()
513
        bn = mfactory.BackendNetworkFactory(operstate='ACTIVE')
514
        for old_state in ['success', 'canceled', 'error']:
515
            for flavor in Network.FLAVORS.keys():
516
                bn.operstate = old_state
517
                bn.save()
518
                net = bn.network
519
                net.state = 'ACTIVE'
520
                net.flavor = flavor
521
                if flavor == 'PHYSICAL_VLAN':
522
                    net.link = allocate_resource('bridge')
523
                if flavor == 'MAC_FILTERED':
524
                    net.mac_prefix = allocate_resource('mac_prefix')
525
                net.save()
526
                msg = self.create_msg(operation='OP_NETWORK_REMOVE',
527
                                      network=net.backend_id,
528
                                      cluster=bn.backend.clustername)
529
                with mocked_quotaholder():
530
                    update_network(client, msg)
531
                self.assertTrue(client.basic_ack.called)
532
                db_bnet = BackendNetwork.objects.get(id=bn.id)
533
                self.assertEqual(db_bnet.operstate,
534
                                 'DELETED')
535
                db_net = Network.objects.get(id=net.id)
536
                self.assertEqual(db_net.state, 'DELETED', flavor)
537
                self.assertTrue(db_net.deleted)
538
                if flavor == 'PHYSICAL_VLAN':
539
                    pool = BridgePoolTable.get_pool()
540
                    self.assertTrue(pool.is_available(net.link))
541
                if flavor == 'MAC_FILTERED':
542
                    pool = MacPrefixPoolTable.get_pool()
543
                    self.assertTrue(pool.is_available(net.mac_prefix))
544

    
545
    @patch("synnefo.logic.rapi_pool.GanetiRapiClient")
546
    def test_remove_error(self, rapi, client):
547
        mfactory.MacPrefixPoolTableFactory()
548
        mfactory.BridgePoolTableFactory()
549
        bn = mfactory.BackendNetworkFactory(operstate='ACTIVE')
550
        network = bn.network
551
        msg = self.create_msg(operation='OP_NETWORK_REMOVE',
552
                              network=network.backend_id,
553
                              status="error",
554
                              cluster=bn.backend.clustername)
555
        rapi().GetNetwork.return_value = {}
556
        update_network(client, msg)
557
        bn = BackendNetwork.objects.get(id=bn.id)
558
        self.assertNotEqual(bn.operstate, "DELETED")
559
        rapi().GetNetwork.side_effect = GanetiApiError(msg="foo", code=404)
560
        with mocked_quotaholder():
561
            update_network(client, msg)
562
        bn = BackendNetwork.objects.get(id=bn.id)
563
        self.assertEqual(bn.operstate, "DELETED")
564

    
565
    def test_remove_offline_backend(self, client):
566
        """Test network removing when a backend is offline"""
567
        mfactory.BridgePoolTableFactory()
568
        net = mfactory.NetworkFactory(flavor='PHYSICAL_VLAN',
569
                                      state='ACTIVE',
570
                                      link='prv12')
571
        bn1 = mfactory.BackendNetworkFactory(network=net)
572
        mfactory.BackendNetworkFactory(network=net,
573
                                       operstate="ACTIVE",
574
                                       backend__offline=True)
575
        msg = self.create_msg(operation='OP_NETWORK_REMOVE',
576
                              network=net.backend_id,
577
                              cluster=bn1.backend.clustername)
578
        with mocked_quotaholder():
579
            update_network(client, msg)
580
        self.assertTrue(client.basic_ack.called)
581
        new_net = Network.objects.get(id=net.id)
582
        self.assertEqual(new_net.state, 'ACTIVE')
583
        self.assertFalse(new_net.deleted)
584

    
585
    def test_error_opcode(self, client):
586
        mfactory.MacPrefixPoolTableFactory()
587
        mfactory.BridgePoolTableFactory()
588
        for state, _ in Network.OPER_STATES:
589
            bn = mfactory.BackendNetworkFactory(operstate="ACTIVE")
590
            bn.operstate = state
591
            bn.save()
592
            network = bn.network
593
            network.state = state
594
            network.save()
595
            for opcode, _ in BackendNetwork.BACKEND_OPCODES:
596
                if opcode in ['OP_NETWORK_REMOVE', 'OP_NETWORK_ADD']:
597
                    continue
598
                msg = self.create_msg(operation=opcode,
599
                                      network=bn.network.backend_id,
600
                                      status='error',
601
                                      add_reserved_ips=[],
602
                                      remove_reserved_ips=[],
603
                                      cluster=bn.backend.clustername)
604
                with mocked_quotaholder():
605
                    update_network(client, msg)
606
                self.assertTrue(client.basic_ack.called)
607
                db_bnet = BackendNetwork.objects.get(id=bn.id)
608
                self.assertEqual(bn.operstate, db_bnet.operstate)
609
                self.assertEqual(bn.network.state, db_bnet.network.state)
610

    
611
    def test_ips(self, client):
612
        network = mfactory.NetworkFactory(subnet='10.0.0.0/24')
613
        bn = mfactory.BackendNetworkFactory(network=network)
614
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
615
                              network=network.backend_id,
616
                              cluster=bn.backend.clustername,
617
                              status='success',
618
                              add_reserved_ips=['10.0.0.10', '10.0.0.20'],
619
                              remove_reserved_ips=[])
620
        update_network(client, msg)
621
        self.assertTrue(client.basic_ack.called)
622
        pool = network.get_pool()
623
        self.assertTrue(pool.is_reserved('10.0.0.10'))
624
        self.assertTrue(pool.is_reserved('10.0.0.20'))
625
        pool.save()
626
        # Release them
627
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
628
                              network=network.backend_id,
629
                              cluster=bn.backend.clustername,
630
                              add_reserved_ips=[],
631
                              remove_reserved_ips=['10.0.0.10', '10.0.0.20'])
632
        update_network(client, msg)
633
        self.assertTrue(client.basic_ack.called)
634
        pool = network.get_pool()
635
        self.assertFalse(pool.is_reserved('10.0.0.10'))
636
        self.assertFalse(pool.is_reserved('10.0.0.20'))
637

    
638

    
639
@patch('synnefo.lib.amqp.AMQPClient')
640
class UpdateBuildProgressTest(TestCase):
641
    def setUp(self):
642
        self.vm = mfactory.VirtualMachineFactory()
643

    
644
    def get_db_vm(self):
645
        return VirtualMachine.objects.get(id=self.vm.id)
646

    
647
    def create_msg(self, **kwargs):
648
        """Create snf-progress-monitor message"""
649
        msg = {'event_time': split_time(time())}
650
        msg['type'] = 'image-copy-progress'
651
        msg['progress'] = 0
652
        for key, val in kwargs.items():
653
            msg[key] = val
654
        message = {'body': json.dumps(msg)}
655
        return message
656

    
657
    def test_missing_attribute(self, client):
658
        update_build_progress(client, json.dumps({'body': {}}))
659
        self.assertTrue(client.basic_reject.called)
660

    
661
    def test_unhandled_exception(self, client):
662
        update_build_progress(client, {})
663
        client.basic_reject.assert_called_once()
664

    
665
    def test_missing_instance(self, client):
666
        msg = self.create_msg(instance='foo')
667
        update_build_progress(client, msg)
668
        self.assertTrue(client.basic_ack.called)
669

    
670
    def test_wrong_type(self, client):
671
        msg = self.create_msg(type="WRONG_TYPE")
672
        update_build_progress(client, msg)
673
        self.assertTrue(client.basic_nack.called)
674

    
675
    def test_progress_update(self, client):
676
        rprogress = randint(10, 100)
677
        msg = self.create_msg(progress=rprogress,
678
                              instance=self.vm.backend_vm_id)
679
        update_build_progress(client, msg)
680
        self.assertTrue(client.basic_ack.called)
681
        vm = self.get_db_vm()
682
        self.assertEqual(vm.buildpercentage, rprogress)
683

    
684
    def test_invalid_value(self, client):
685
        old = self.vm.buildpercentage
686
        for rprogress in [0, -1, 'a']:
687
            msg = self.create_msg(progress=rprogress,
688
                                  instance=self.vm.backend_vm_id)
689
            update_build_progress(client, msg)
690
            self.assertTrue(client.basic_ack.called)
691
            vm = self.get_db_vm()
692
            self.assertEqual(vm.buildpercentage, old)