Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / tests / servers.py @ 3768eb67

History | View | Annotate | Download (12.4 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
from django.test import TransactionTestCase
33
#from snf_django.utils.testing import mocked_quotaholder
34
from synnefo.logic import servers
35
from synnefo import quotas
36
from synnefo.db import models_factory as mfactory, models
37
from mock import patch, Mock
38

    
39
from snf_django.lib.api import faults
40
from snf_django.utils.testing import mocked_quotaholder, override_settings
41
from django.conf import settings
42
from copy import deepcopy
43

    
44
fixed_image = Mock()
45
fixed_image.return_value = {'location': 'pithos://foo',
46
                            'checksum': 'test_checksum',
47
                            "id": 1,
48
                            "name": "test_image",
49
                            "size": 1024,
50
                            "is_snapshot": False,
51
                            'disk_format': 'diskdump'}
52

    
53

    
54
@patch('synnefo.api.util.get_image', fixed_image)
55
@patch("synnefo.logic.rapi_pool.GanetiRapiClient")
56
class ServerCreationTest(TransactionTestCase):
57
    def test_create(self, mrapi):
58
        flavor = mfactory.FlavorFactory()
59
        kwargs = {
60
            "userid": "test",
61
            "name": "test_vm",
62
            "password": "1234",
63
            "flavor": flavor,
64
            "image_id": "safs",
65
            "networks": [],
66
            "metadata": {"foo": "bar"},
67
            "personality": [],
68
        }
69
        # no backend!
70
        mfactory.BackendFactory(offline=True)
71
        self.assertRaises(faults.ServiceUnavailable, servers.create, **kwargs)
72
        self.assertEqual(models.VirtualMachine.objects.count(), 0)
73

    
74
        mfactory.IPv4SubnetFactory(network__public=True)
75
        mfactory.IPv6SubnetFactory(network__public=True)
76
        backend = mfactory.BackendFactory()
77

    
78
        # error in nics
79
        req = deepcopy(kwargs)
80
        req["networks"] = [{"uuid": 42}]
81
        self.assertRaises(faults.ItemNotFound, servers.create, **req)
82
        self.assertEqual(models.VirtualMachine.objects.count(), 0)
83

    
84
        # error in enqueue. check the vm is deleted and resources released
85
        mrapi().CreateInstance.side_effect = Exception("ganeti is down")
86
        with mocked_quotaholder():
87
            servers.create(**kwargs)
88
        vm = models.VirtualMachine.objects.get()
89
        self.assertFalse(vm.deleted)
90
        self.assertEqual(vm.operstate, "ERROR")
91
        for nic in vm.nics.all():
92
            self.assertEqual(nic.state, "ERROR")
93

    
94
        # test ext settings:
95
        req = deepcopy(kwargs)
96
        ext_flavor = mfactory.FlavorFactory(disk_template="ext_archipelago",
97
                                            disk=1)
98
        req["flavor"] = ext_flavor
99
        mrapi().CreateInstance.return_value = 42
100
        backend.disk_templates = ["ext"]
101
        backend.save()
102
        osettings = {
103
            "GANETI_DISK_PROVIDER_KWARGS": {
104
                "archipelago": {
105
                    "foo": "mpaz",
106
                    "lala": "lolo"
107
                }
108
            }
109
        }
110
        with mocked_quotaholder():
111
            with override_settings(settings, **osettings):
112
                vm = servers.create(**req)
113
        name, args, kwargs = mrapi().CreateInstance.mock_calls[-1]
114
        self.assertEqual(kwargs["disks"][0],
115
                         {"provider": "archipelago",
116
                          "origin": "pithos:test_checksum",
117
                          "name": vm.volumes.all()[0].backend_volume_uuid,
118
                          "foo": "mpaz",
119
                          "lala": "lolo",
120
                          "size": 1024})
121

    
122

    
123
@patch("synnefo.logic.rapi_pool.GanetiRapiClient")
124
class ServerTest(TransactionTestCase):
125
    def test_connect_network(self, mrapi):
126
        # Common connect
127
        for dhcp in [True, False]:
128
            subnet = mfactory.IPv4SubnetFactory(network__flavor="CUSTOM",
129
                                                cidr="192.168.2.0/24",
130
                                                gateway="192.168.2.1",
131
                                                dhcp=dhcp)
132
            net = subnet.network
133
            vm = mfactory.VirtualMachineFactory(operstate="STARTED")
134
            mfactory.BackendNetworkFactory(network=net, backend=vm.backend)
135
            mrapi().ModifyInstance.return_value = 42
136
            with override_settings(settings, GANETI_USE_HOTPLUG=True):
137
                servers.connect(vm, net)
138
            pool = net.get_ip_pools(locked=False)[0]
139
            self.assertFalse(pool.is_available("192.168.2.2"))
140
            args, kwargs = mrapi().ModifyInstance.call_args
141
            nics = kwargs["nics"][0]
142
            self.assertEqual(kwargs["instance"], vm.backend_vm_id)
143
            self.assertEqual(nics[0], "add")
144
            self.assertEqual(nics[1], "-1")
145
            self.assertEqual(nics[2]["ip"], "192.168.2.2")
146
            self.assertEqual(nics[2]["network"], net.backend_id)
147

    
148
        # Test connect to IPv6 only network
149
        vm = mfactory.VirtualMachineFactory(operstate="STARTED")
150
        subnet = mfactory.IPv6SubnetFactory(cidr="2000::/64",
151
                                            gateway="2000::1")
152
        net = subnet.network
153
        mfactory.BackendNetworkFactory(network=net, backend=vm.backend)
154
        with override_settings(settings, GANETI_USE_HOTPLUG=True):
155
            servers.connect(vm, net)
156
        args, kwargs = mrapi().ModifyInstance.call_args
157
        nics = kwargs["nics"][0]
158
        self.assertEqual(kwargs["instance"], vm.backend_vm_id)
159
        self.assertEqual(nics[0], "add")
160
        self.assertEqual(nics[1], "-1")
161
        self.assertEqual(nics[2]["ip"], None)
162
        self.assertEqual(nics[2]["network"], net.backend_id)
163

    
164

    
165
@patch("synnefo.logic.rapi_pool.GanetiRapiClient")
166
class ServerCommandTest(TransactionTestCase):
167
    def test_pending_task(self, mrapi):
168
        vm = mfactory.VirtualMachineFactory(task="REBOOT", task_job_id=1)
169
        self.assertRaises(faults.BadRequest, servers.start, vm)
170
        vm = mfactory.VirtualMachineFactory(task="BUILD", task_job_id=1)
171
        self.assertRaises(faults.BuildInProgress, servers.start, vm)
172
        # Assert always succeeds
173
        vm = mfactory.VirtualMachineFactory(task="BUILD", task_job_id=1)
174
        mrapi().DeleteInstance.return_value = 1
175
        with mocked_quotaholder():
176
            servers.destroy(vm)
177
        vm = mfactory.VirtualMachineFactory(task="REBOOT", task_job_id=1)
178
        with mocked_quotaholder():
179
            servers.destroy(vm)
180

    
181
    def test_deleted_vm(self, mrapi):
182
        vm = mfactory.VirtualMachineFactory(deleted=True)
183
        self.assertRaises(faults.BadRequest, servers.start, vm)
184

    
185
    def test_invalid_operstate_for_action(self, mrapi):
186
        vm = mfactory.VirtualMachineFactory(operstate="STARTED")
187
        self.assertRaises(faults.BadRequest, servers.start, vm)
188
        vm = mfactory.VirtualMachineFactory(operstate="STOPPED")
189
        self.assertRaises(faults.BadRequest, servers.stop, vm)
190
        vm = mfactory.VirtualMachineFactory(operstate="STARTED")
191
        flavor = mfactory.FlavorFactory()
192
        self.assertRaises(faults.BadRequest, servers.resize, vm, flavor)
193
        # Check that connect/disconnect is allowed only in STOPPED vms
194
        # if hotplug is disabled.
195
        vm = mfactory.VirtualMachineFactory(operstate="STARTED")
196
        network = mfactory.NetworkFactory(state="ACTIVE")
197
        with override_settings(settings, GANETI_USE_HOTPLUG=False):
198
            self.assertRaises(faults.BadRequest, servers.connect, vm, network)
199
            self.assertRaises(faults.BadRequest, servers.disconnect, vm,
200
                              network)
201
        #test valid
202
        vm = mfactory.VirtualMachineFactory(operstate="STOPPED")
203
        mrapi().StartupInstance.return_value = 1
204
        with mocked_quotaholder():
205
            servers.start(vm)
206
        vm.task = None
207
        vm.task_job_id = None
208
        vm.save()
209
        with mocked_quotaholder():
210
            quotas.accept_resource_serial(vm)
211
        mrapi().RebootInstance.return_value = 1
212
        with mocked_quotaholder():
213
            servers.reboot(vm, "HARD")
214

    
215
    def test_commission(self, mrapi):
216
        vm = mfactory.VirtualMachineFactory(operstate="STOPPED")
217
        # Still pending
218
        vm.serial = mfactory.QuotaHolderSerialFactory(serial=200,
219
                                                      resolved=False,
220
                                                      pending=True)
221
        serial = vm.serial
222
        mrapi().StartupInstance.return_value = 1
223
        with mocked_quotaholder() as m:
224
            with self.assertRaises(quotas.ResolveError):
225
                servers.start(vm)
226
        # Not pending, rejct
227
        vm.task = None
228
        vm.serial = mfactory.QuotaHolderSerialFactory(serial=400,
229
                                                      resolved=False,
230
                                                      pending=False,
231
                                                      accept=False)
232
        serial = vm.serial
233
        mrapi().StartupInstance.return_value = 1
234
        with mocked_quotaholder() as m:
235
            servers.start(vm)
236
            m.resolve_commissions.assert_called_once_with([],
237
                                                          [serial.serial])
238
            self.assertTrue(m.issue_one_commission.called)
239
        # Not pending, accept
240
        vm.task = None
241
        vm.serial = mfactory.QuotaHolderSerialFactory(serial=600,
242
                                                      resolved=False,
243
                                                      pending=False,
244
                                                      accept=True)
245
        serial = vm.serial
246
        mrapi().StartupInstance.return_value = 1
247
        with mocked_quotaholder() as m:
248
            servers.start(vm)
249
            m.resolve_commissions.assert_called_once_with([serial.serial],
250
                                                          [])
251
            self.assertTrue(m.issue_one_commission.called)
252

    
253
        mrapi().StartupInstance.side_effect = ValueError
254
        vm.task = None
255
        vm.serial = None
256
        # Test reject if Ganeti erro
257
        with mocked_quotaholder() as m:
258
            try:
259
                servers.start(vm)
260
            except:
261
                m.resolve_commissions\
262
                 .assert_called_once_with([], [vm.serial.serial])
263

    
264
    def test_task_after(self, mrapi):
265
        return
266
        vm = mfactory.VirtualMachineFactory()
267
        mrapi().StartupInstance.return_value = 1
268
        mrapi().ShutdownInstance.return_value = 2
269
        mrapi().RebootInstance.return_value = 2
270
        with mocked_quotaholder():
271
            vm.task = None
272
            vm.operstate = "STOPPED"
273
            servers.start(vm)
274
            self.assertEqual(vm.task, "START")
275
            self.assertEqual(vm.task_job_id, 1)
276
        with mocked_quotaholder():
277
            vm.task = None
278
            vm.operstate = "STARTED"
279
            servers.stop(vm)
280
            self.assertEqual(vm.task, "STOP")
281
            self.assertEqual(vm.task_job_id, 2)
282
        with mocked_quotaholder():
283
            vm.task = None
284
            servers.reboot(vm)
285
            self.assertEqual(vm.task, "REBOOT")
286
            self.assertEqual(vm.task_job_id, 3)