Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / tests / callbacks.py @ 3b2984dc

History | View | Annotate | Download (28.8 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 synnefo.logic.rapi import GanetiApiError
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
        ip = mfactory.IPv4AddressFactory(nic__machine=vm)
139
        nic = ip.nic
140
        nic.network.get_ip_pools()[0].reserve(nic.ipv4_address)
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_ip_pools()[0].is_available(ip.address))
152
        vm2 = mfactory.VirtualMachineFactory()
153
        fp1 = mfactory.IPv4AddressFactory(nic__machine=vm2, floating_ip=True,
154
                                          network__floating_ip_pool=True)
155
        network = fp1.network
156
        nic1 = mfactory.NetworkInterfaceFactory(machine=vm2)
157
        fp1.nic = nic1
158
        fp1.save()
159
        pool = network.get_ip_pools()[0]
160
        pool.reserve(fp1.address)
161
        pool.save()
162
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
163
                              instance=vm2.backend_vm_id)
164
        with mocked_quotaholder():
165
            update_db(client, msg)
166
        client.basic_ack.assert_called_once()
167
        db_vm = VirtualMachine.objects.get(id=vm.id)
168
        self.assertEqual(db_vm.operstate, 'DESTROYED')
169
        self.assertTrue(db_vm.deleted)
170
        self.assertEqual(IPAddress.objects.get(id=fp1.id).nic, None)
171
        pool = network.get_ip_pools()[0]
172
        # Test that floating ips are not released
173
        self.assertFalse(pool.is_available(fp1.address))
174

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

    
187
        rapi().GetInstance.side_effect = GanetiApiError(msg="msg",
188
                                                        code=503)
189
        update_db(client, msg)
190
        db_vm = VirtualMachine.objects.get(id=vm.id)
191
        self.assertFalse(db_vm.deleted)
192

    
193
        rapi().GetInstance.side_effect = GanetiApiError(msg="msg",
194
                                                        code=404)
195
        with mocked_quotaholder():
196
            update_db(client, msg)
197
        db_vm = VirtualMachine.objects.get(id=vm.id)
198
        self.assertTrue(db_vm.deleted)
199

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

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

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

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

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

    
316

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

    
332
    def test_missing_attribute(self, client):
333
        update_db(client, json.dumps({'body': {}}))
334
        self.assertTrue(client.basic_reject.called)
335

    
336
    def test_unhandled_exception(self, client):
337
        update_db(client, {})
338
        client.basic_reject.assert_called_once()
339

    
340
    def test_wrong_type(self, client):
341
        msg = self.create_msg(type="WRONG_TYPE")
342
        update_db(client, msg)
343
        self.assertTrue(client.basic_nack.called)
344

    
345
    def test_missing_instance(self, client):
346
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
347
                              instance='foo')
348
        update_db(client, msg)
349
        self.assertTrue(client.basic_ack.called)
350

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

    
364
    def test_changed_nic(self, client):
365
        ip = mfactory.IPv4AddressFactory(subnet__cidr="10.0.0.0/24",
366
                                         address="10.0.0.2")
367
        network = ip.network
368
        subnet = ip.subnet
369
        vm = ip.nic.machine
370
        pool = subnet.get_ip_pools()[0]
371
        pool.reserve("10.0.0.2")
372
        pool.save()
373

    
374
        msg = self.create_msg(instance_nics=[{'network': network.backend_id,
375
                                              'ip': '10.0.0.3',
376
                                              'mac': 'aa:bb:cc:00:11:22',
377
                                              'name': ip.nic.backend_uuid}],
378
                              instance=vm.backend_vm_id)
379
        update_db(client, msg)
380
        self.assertTrue(client.basic_ack.called)
381
        db_vm = VirtualMachine.objects.get(id=vm.id)
382
        nics = db_vm.nics.all()
383
        self.assertEqual(len(nics), 1)
384
        self.assertEqual(nics[0].index, 0)
385
        self.assertEqual(nics[0].ipv4_address, '10.0.0.3')
386
        self.assertEqual(nics[0].mac, 'aa:bb:cc:00:11:22')
387
        pool = subnet.get_ip_pools()[0]
388
        self.assertTrue(pool.is_available('10.0.0.2'))
389
        self.assertFalse(pool.is_available('10.0.0.3'))
390
        pool.save()
391

    
392

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

    
407
    def test_missing_attribute(self, client):
408
        update_network(client, json.dumps({'body': {}}))
409
        self.assertTrue(client.basic_reject.called)
410

    
411
    def test_unhandled_exception(self, client):
412
        update_network(client, {})
413
        client.basic_reject.assert_called_once()
414

    
415
    def test_wrong_type(self, client):
416
        msg = self.create_msg(type="WRONG_TYPE")
417
        update_network(client, msg)
418
        self.assertTrue(client.basic_nack.called)
419

    
420
    def test_missing_network(self, client):
421
        msg = self.create_msg(operation='OP_NETWORK_CREATE',
422
                              network='foo')
423
        update_network(client, msg)
424
        self.assertTrue(client.basic_ack.called)
425

    
426
    def test_create(self, client):
427
        back_network = mfactory.BackendNetworkFactory(operstate='PENDING')
428
        net = back_network.network
429
        net.state = 'ACTIVE'
430
        net.save()
431
        back1 = back_network.backend
432

    
433
        back_network2 = mfactory.BackendNetworkFactory(operstate='PENDING',
434
                                                       network=net)
435
        back2 = back_network2.backend
436
        # Message from first backend network
437
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
438
                              network=net.backend_id,
439
                              cluster=back1.clustername)
440
        update_network(client, msg)
441
        self.assertTrue(client.basic_ack.called)
442

    
443
        back_net = BackendNetwork.objects.get(id=back_network.id)
444
        self.assertEqual(back_net.operstate, 'ACTIVE')
445
        db_net = Network.objects.get(id=net.id)
446
        self.assertEqual(db_net.state, 'ACTIVE')
447
        # msg from second backend network
448
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
449
                              network=net.backend_id,
450
                              cluster=back2.clustername)
451
        update_network(client, msg)
452
        self.assertTrue(client.basic_ack.called)
453

    
454
        db_net = Network.objects.get(id=net.id)
455
        self.assertEqual(db_net.state, 'ACTIVE')
456
        back_net = BackendNetwork.objects.get(id=back_network.id)
457
        self.assertEqual(back_net.operstate, 'ACTIVE')
458

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

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

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

    
525
    @patch("synnefo.logic.rapi_pool.GanetiRapiClient")
526
    def test_remove_error(self, rapi, client):
527
        mfactory.MacPrefixPoolTableFactory()
528
        mfactory.BridgePoolTableFactory()
529
        bn = mfactory.BackendNetworkFactory(operstate='ACTIVE')
530
        network = bn.network
531
        msg = self.create_msg(operation='OP_NETWORK_REMOVE',
532
                              network=network.backend_id,
533
                              status="error",
534
                              cluster=bn.backend.clustername)
535
        rapi().GetNetwork.return_value = {}
536
        update_network(client, msg)
537
        bn = BackendNetwork.objects.get(id=bn.id)
538
        self.assertNotEqual(bn.operstate, "DELETED")
539
        rapi().GetNetwork.side_effect = GanetiApiError(msg="foo", code=404)
540
        with mocked_quotaholder():
541
            update_network(client, msg)
542
        bn = BackendNetwork.objects.get(id=bn.id)
543
        self.assertEqual(bn.operstate, "DELETED")
544

    
545
    def test_remove_offline_backend(self, client):
546
        """Test network removing when a backend is offline"""
547
        mfactory.BridgePoolTableFactory()
548
        net = mfactory.NetworkFactory(flavor='PHYSICAL_VLAN',
549
                                      state='ACTIVE',
550
                                      link='prv12')
551
        bn1 = mfactory.BackendNetworkFactory(network=net)
552
        mfactory.BackendNetworkFactory(network=net,
553
                                       operstate="ACTIVE",
554
                                       backend__offline=True)
555
        msg = self.create_msg(operation='OP_NETWORK_REMOVE',
556
                              network=net.backend_id,
557
                              cluster=bn1.backend.clustername)
558
        with mocked_quotaholder():
559
            update_network(client, msg)
560
        self.assertTrue(client.basic_ack.called)
561
        new_net = Network.objects.get(id=net.id)
562
        self.assertEqual(new_net.state, 'ACTIVE')
563
        self.assertFalse(new_net.deleted)
564

    
565
    def test_error_opcode(self, client):
566
        mfactory.MacPrefixPoolTableFactory()
567
        mfactory.BridgePoolTableFactory()
568
        for state, _ in Network.OPER_STATES:
569
            bn = mfactory.BackendNetworkFactory(operstate="ACTIVE")
570
            bn.operstate = state
571
            bn.save()
572
            network = bn.network
573
            network.state = state
574
            network.save()
575
            for opcode, _ in BackendNetwork.BACKEND_OPCODES:
576
                if opcode in ['OP_NETWORK_REMOVE', 'OP_NETWORK_ADD']:
577
                    continue
578
                msg = self.create_msg(operation=opcode,
579
                                      network=bn.network.backend_id,
580
                                      status='error',
581
                                      add_reserved_ips=[],
582
                                      remove_reserved_ips=[],
583
                                      cluster=bn.backend.clustername)
584
                with mocked_quotaholder():
585
                    update_network(client, msg)
586
                self.assertTrue(client.basic_ack.called)
587
                db_bnet = BackendNetwork.objects.get(id=bn.id)
588
                self.assertEqual(bn.operstate, db_bnet.operstate)
589
                self.assertEqual(bn.network.state, db_bnet.network.state)
590

    
591
    def test_ips(self, client):
592
        network = mfactory.NetworkWithSubnetFactory(subnet__cidr='10.0.0.0/24',
593
                                                    subnet__gateway="10.0.0.1")
594
        bn = mfactory.BackendNetworkFactory(network=network)
595
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
596
                              network=network.backend_id,
597
                              cluster=bn.backend.clustername,
598
                              status='success',
599
                              job_fields={"add_reserved_ips": ["10.0.0.10",
600
                                                               "10.0.0.20"]})
601
        update_network(client, msg)
602
        self.assertTrue(client.basic_ack.called)
603
        pool = network.get_ip_pools()[0]
604
        self.assertTrue(pool.is_reserved('10.0.0.10'))
605
        self.assertTrue(pool.is_reserved('10.0.0.20'))
606
        pool.save()
607
        # Check that they are not released
608
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
609
                              network=network.backend_id,
610
                              cluster=bn.backend.clustername,
611
                              job_fields={
612
                                  "remove_reserved_ips": ["10.0.0.10",
613
                                                          "10.0.0.20"]})
614
        update_network(client, msg)
615
        #self.assertTrue(client.basic_ack.called)
616
        pool = network.get_ip_pools()[0]
617
        self.assertTrue(pool.is_reserved('10.0.0.10'))
618
        self.assertTrue(pool.is_reserved('10.0.0.20'))
619

    
620

    
621
@patch('synnefo.lib.amqp.AMQPClient')
622
class UpdateBuildProgressTest(TestCase):
623
    def setUp(self):
624
        self.vm = mfactory.VirtualMachineFactory()
625

    
626
    def get_db_vm(self):
627
        return VirtualMachine.objects.get(id=self.vm.id)
628

    
629
    def create_msg(self, **kwargs):
630
        """Create snf-progress-monitor message"""
631
        msg = {'event_time': split_time(time())}
632
        msg['type'] = 'image-copy-progress'
633
        msg['progress'] = 0
634
        for key, val in kwargs.items():
635
            msg[key] = val
636
        message = {'body': json.dumps(msg)}
637
        return message
638

    
639
    def test_missing_attribute(self, client):
640
        update_build_progress(client, json.dumps({'body': {}}))
641
        self.assertTrue(client.basic_reject.called)
642

    
643
    def test_unhandled_exception(self, client):
644
        update_build_progress(client, {})
645
        client.basic_reject.assert_called_once()
646

    
647
    def test_missing_instance(self, client):
648
        msg = self.create_msg(instance='foo')
649
        update_build_progress(client, msg)
650
        self.assertTrue(client.basic_ack.called)
651

    
652
    def test_wrong_type(self, client):
653
        msg = self.create_msg(type="WRONG_TYPE")
654
        update_build_progress(client, msg)
655
        self.assertTrue(client.basic_nack.called)
656

    
657
    def test_progress_update(self, client):
658
        rprogress = randint(10, 100)
659
        msg = self.create_msg(progress=rprogress,
660
                              instance=self.vm.backend_vm_id)
661
        update_build_progress(client, msg)
662
        self.assertTrue(client.basic_ack.called)
663
        vm = self.get_db_vm()
664
        self.assertEqual(vm.buildpercentage, rprogress)
665

    
666
    def test_invalid_value(self, client):
667
        old = self.vm.buildpercentage
668
        for rprogress in [0, -1, 'a']:
669
            msg = self.create_msg(progress=rprogress,
670
                                  instance=self.vm.backend_vm_id)
671
            update_build_progress(client, msg)
672
            self.assertTrue(client.basic_ack.called)
673
            vm = self.get_db_vm()
674
            self.assertEqual(vm.buildpercentage, old)