Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (29.2 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
                self.assertFalse(BackendNetwork.objects.filter(id=bn.id)
513
                                 .exists())
514
                db_net = Network.objects.get(id=net.id)
515
                self.assertEqual(db_net.state, 'DELETED', flavor)
516
                self.assertTrue(db_net.deleted)
517
                if flavor == 'PHYSICAL_VLAN':
518
                    pool = BridgePoolTable.get_pool()
519
                    self.assertTrue(pool.is_available(net.link))
520
                if flavor == 'MAC_FILTERED':
521
                    pool = MacPrefixPoolTable.get_pool()
522
                    self.assertTrue(pool.is_available(net.mac_prefix))
523

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

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

    
563
    @patch("synnefo.logic.rapi_pool.GanetiRapiClient")
564
    def test_error_opcode(self, rapi, client):
565
        # Mock getting network, because code will lookup if network exists
566
        # in backend
567
        rapi().GetNetwork.return_value = {}
568
        mfactory.MacPrefixPoolTableFactory()
569
        mfactory.BridgePoolTableFactory()
570
        network = mfactory.NetworkFactory()
571
        mfactory.BackendNetworkFactory(network=network,
572
                                       operstate="ACTIVE")
573
        for state, _ in Network.OPER_STATES:
574
            bn = mfactory.BackendNetworkFactory(operstate="ACTIVE",
575
                                                network=network)
576
            bn.operstate = state
577
            bn.save()
578
            network = bn.network
579
            network.state = state
580
            network.save()
581
            for opcode, _ in BackendNetwork.BACKEND_OPCODES:
582
                if opcode in ['OP_NETWORK_REMOVE', 'OP_NETWORK_ADD']:
583
                    continue
584
                msg = self.create_msg(operation=opcode,
585
                                      network=bn.network.backend_id,
586
                                      status='error',
587
                                      add_reserved_ips=[],
588
                                      remove_reserved_ips=[],
589
                                      cluster=bn.backend.clustername)
590
                with mocked_quotaholder():
591
                    update_network(client, msg)
592
                self.assertTrue(client.basic_ack.called)
593
                db_bnet = BackendNetwork.objects.get(id=bn.id)
594
                self.assertEqual(bn.operstate, db_bnet.operstate)
595
                self.assertEqual(bn.network.state, db_bnet.network.state)
596

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

    
626

    
627
@patch('synnefo.lib.amqp.AMQPClient')
628
class UpdateBuildProgressTest(TestCase):
629
    def setUp(self):
630
        self.vm = mfactory.VirtualMachineFactory()
631

    
632
    def get_db_vm(self):
633
        return VirtualMachine.objects.get(id=self.vm.id)
634

    
635
    def create_msg(self, **kwargs):
636
        """Create snf-progress-monitor message"""
637
        msg = {'event_time': split_time(time())}
638
        msg['type'] = 'image-copy-progress'
639
        msg['progress'] = 0
640
        for key, val in kwargs.items():
641
            msg[key] = val
642
        message = {'body': json.dumps(msg)}
643
        return message
644

    
645
    def test_missing_attribute(self, client):
646
        update_build_progress(client, json.dumps({'body': {}}))
647
        self.assertTrue(client.basic_reject.called)
648

    
649
    def test_unhandled_exception(self, client):
650
        update_build_progress(client, {})
651
        client.basic_reject.assert_called_once()
652

    
653
    def test_missing_instance(self, client):
654
        msg = self.create_msg(instance='foo')
655
        update_build_progress(client, msg)
656
        self.assertTrue(client.basic_ack.called)
657

    
658
    def test_wrong_type(self, client):
659
        msg = self.create_msg(type="WRONG_TYPE")
660
        update_build_progress(client, msg)
661
        self.assertTrue(client.basic_nack.called)
662

    
663
    def test_progress_update(self, client):
664
        rprogress = randint(10, 100)
665
        msg = self.create_msg(progress=rprogress,
666
                              instance=self.vm.backend_vm_id)
667
        update_build_progress(client, msg)
668
        self.assertTrue(client.basic_ack.called)
669
        vm = self.get_db_vm()
670
        self.assertEqual(vm.buildpercentage, rprogress)
671

    
672
    def test_invalid_value(self, client):
673
        old = self.vm.buildpercentage
674
        for rprogress in [0, -1, 'a']:
675
            msg = self.create_msg(progress=rprogress,
676
                                  instance=self.vm.backend_vm_id)
677
            update_build_progress(client, msg)
678
            self.assertTrue(client.basic_ack.called)
679
            vm = self.get_db_vm()
680
            self.assertEqual(vm.buildpercentage, old)