Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (12.1 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
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

    
45
@patch("synnefo.logic.rapi_pool.GanetiRapiClient")
46
class ServerCreationTest(TransactionTestCase):
47
    def test_create(self, mrapi):
48
        flavor = mfactory.FlavorFactory()
49
        kwargs = {
50
            "userid": "test",
51
            "name": "test_vm",
52
            "password": "1234",
53
            "flavor": flavor,
54
            "image": {"id": "foo", "backend_id": "foo", "format": "diskdump",
55
                      "checksum": "test_checksum",
56
                      "metadata": "{}"},
57
            "networks": [],
58
            "metadata": {"foo": "bar"},
59
            "personality": [],
60
        }
61
        # no backend!
62
        mfactory.BackendFactory(offline=True)
63
        self.assertRaises(faults.ServiceUnavailable, servers.create, **kwargs)
64
        self.assertEqual(models.VirtualMachine.objects.count(), 0)
65

    
66
        mfactory.IPv4SubnetFactory(network__public=True)
67
        mfactory.IPv6SubnetFactory(network__public=True)
68
        backend = mfactory.BackendFactory()
69

    
70
        # error in nics
71
        req = deepcopy(kwargs)
72
        req["networks"] = [{"uuid": 42}]
73
        self.assertRaises(faults.ItemNotFound, servers.create, **req)
74
        self.assertEqual(models.VirtualMachine.objects.count(), 0)
75

    
76
        # error in enqueue. check the vm is deleted and resources released
77
        mrapi().CreateInstance.side_effect = Exception("ganeti is down")
78
        with mocked_quotaholder():
79
            servers.create(**kwargs)
80
        vm = models.VirtualMachine.objects.get()
81
        self.assertFalse(vm.deleted)
82
        self.assertEqual(vm.operstate, "ERROR")
83
        for nic in vm.nics.all():
84
            self.assertEqual(nic.state, "ERROR")
85

    
86
        # test ext settings:
87
        req = deepcopy(kwargs)
88
        ext_flavor = mfactory.FlavorFactory(disk_template="ext_archipelago",
89
                                            disk=1)
90
        req["flavor"] = ext_flavor
91
        mrapi().CreateInstance.return_value = 42
92
        backend.disk_templates = ["ext"]
93
        backend.save()
94
        osettings = {
95
            "GANETI_DISK_PROVIDER_KWARGS": {
96
                "archipelago": {
97
                    "foo": "mpaz",
98
                    "lala": "lolo"
99
                }
100
            }
101
        }
102
        with mocked_quotaholder():
103
            with override_settings(settings, **osettings):
104
                vm = servers.create(**req)
105
        name, args, kwargs = mrapi().CreateInstance.mock_calls[-1]
106
        self.assertEqual(kwargs["disks"][0], {"provider": "archipelago",
107
                                              "origin": "test_checksum",
108
                                              "foo": "mpaz",
109
                                              "lala": "lolo",
110
                                              "size": 1024})
111

    
112

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

    
138
        # Test connect to IPv6 only network
139
        vm = mfactory.VirtualMachineFactory(operstate="STARTED")
140
        subnet = mfactory.IPv6SubnetFactory(cidr="2000::/64",
141
                                            gateway="2000::1")
142
        net = subnet.network
143
        mfactory.BackendNetworkFactory(network=net, backend=vm.backend)
144
        with override_settings(settings, GANETI_USE_HOTPLUG=True):
145
            servers.connect(vm, net)
146
        args, kwargs = mrapi().ModifyInstance.call_args
147
        nics = kwargs["nics"][0]
148
        self.assertEqual(kwargs["instance"], vm.backend_vm_id)
149
        self.assertEqual(nics[0], "add")
150
        self.assertEqual(nics[1], "-1")
151
        self.assertEqual(nics[2]["ip"], None)
152
        self.assertEqual(nics[2]["network"], net.backend_id)
153

    
154

    
155
@patch("synnefo.logic.rapi_pool.GanetiRapiClient")
156
class ServerCommandTest(TransactionTestCase):
157
    def test_pending_task(self, mrapi):
158
        vm = mfactory.VirtualMachineFactory(task="REBOOT", task_job_id=1)
159
        self.assertRaises(faults.BadRequest, servers.start, vm)
160
        vm = mfactory.VirtualMachineFactory(task="BUILD", task_job_id=1)
161
        self.assertRaises(faults.BuildInProgress, servers.start, vm)
162
        # Assert always succeeds
163
        vm = mfactory.VirtualMachineFactory(task="BUILD", task_job_id=1)
164
        mrapi().DeleteInstance.return_value = 1
165
        with mocked_quotaholder():
166
            servers.destroy(vm)
167
        vm = mfactory.VirtualMachineFactory(task="REBOOT", task_job_id=1)
168
        with mocked_quotaholder():
169
            servers.destroy(vm)
170

    
171
    def test_deleted_vm(self, mrapi):
172
        vm = mfactory.VirtualMachineFactory(deleted=True)
173
        self.assertRaises(faults.BadRequest, servers.start, vm)
174

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

    
205
    def test_commission(self, mrapi):
206
        vm = mfactory.VirtualMachineFactory(operstate="STOPPED")
207
        # Still pending
208
        vm.serial = mfactory.QuotaHolderSerialFactory(serial=200,
209
                                                      resolved=False,
210
                                                      pending=True)
211
        serial = vm.serial
212
        mrapi().StartupInstance.return_value = 1
213
        with mocked_quotaholder() as m:
214
            with self.assertRaises(quotas.ResolveError):
215
                servers.start(vm)
216
        # Not pending, rejct
217
        vm.task = None
218
        vm.serial = mfactory.QuotaHolderSerialFactory(serial=400,
219
                                                      resolved=False,
220
                                                      pending=False,
221
                                                      accept=False)
222
        serial = vm.serial
223
        mrapi().StartupInstance.return_value = 1
224
        with mocked_quotaholder() as m:
225
            servers.start(vm)
226
            m.resolve_commissions.assert_called_once_with([],
227
                                                          [serial.serial])
228
            self.assertTrue(m.issue_one_commission.called)
229
        # Not pending, accept
230
        vm.task = None
231
        vm.serial = mfactory.QuotaHolderSerialFactory(serial=600,
232
                                                      resolved=False,
233
                                                      pending=False,
234
                                                      accept=True)
235
        serial = vm.serial
236
        mrapi().StartupInstance.return_value = 1
237
        with mocked_quotaholder() as m:
238
            servers.start(vm)
239
            m.resolve_commissions.assert_called_once_with([serial.serial],
240
                                                          [])
241
            self.assertTrue(m.issue_one_commission.called)
242

    
243
        mrapi().StartupInstance.side_effect = ValueError
244
        vm.task = None
245
        vm.serial = None
246
        # Test reject if Ganeti erro
247
        with mocked_quotaholder() as m:
248
            try:
249
                servers.start(vm)
250
            except:
251
                m.resolve_commissions\
252
                 .assert_called_once_with([], [vm.serial.serial])
253

    
254
    def test_task_after(self, mrapi):
255
        return
256
        vm = mfactory.VirtualMachineFactory()
257
        mrapi().StartupInstance.return_value = 1
258
        mrapi().ShutdownInstance.return_value = 2
259
        mrapi().RebootInstance.return_value = 2
260
        with mocked_quotaholder():
261
            vm.task = None
262
            vm.operstate = "STOPPED"
263
            servers.start(vm)
264
            self.assertEqual(vm.task, "START")
265
            self.assertEqual(vm.task_job_id, 1)
266
        with mocked_quotaholder():
267
            vm.task = None
268
            vm.operstate = "STARTED"
269
            servers.stop(vm)
270
            self.assertEqual(vm.task, "STOP")
271
            self.assertEqual(vm.task_job_id, 2)
272
        with mocked_quotaholder():
273
            vm.task = None
274
            servers.reboot(vm)
275
            self.assertEqual(vm.task, "REBOOT")
276
            self.assertEqual(vm.task_job_id, 3)