Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / test / servers.py @ e13aeb09

History | View | Annotate | Download (17.3 kB)

1
# Copyright 2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import json
35

    
36
from snf_django.utils.testing import BaseAPITest, mocked_quotaholder
37
from synnefo.db.models import VirtualMachine, VirtualMachineMetadata
38
from synnefo.db import models_factory as mfactory
39
from synnefo.logic.utils import get_rsapi_state
40

    
41
from mock import patch
42

    
43

    
44
class ServerAPITest(BaseAPITest):
45
    def setUp(self):
46
        self.user1 = 'user1'
47
        self.user2 = 'user2'
48
        self.vm1 = mfactory.VirtualMachineFactory(userid=self.user1)
49
        self.vm2 = mfactory.VirtualMachineFactory(userid=self.user2)
50
        self.vm3 = mfactory.VirtualMachineFactory(deleted=True,
51
                                                  userid=self.user1)
52
        self.vm4 = mfactory.VirtualMachineFactory(userid=self.user2)
53

    
54
    def test_server_list_1(self):
55
        """Test if the expected list of servers is returned."""
56
        response = self.get('/api/v1.1/servers')
57
        self.assertSuccess(response)
58
        servers = json.loads(response.content)['servers']['values']
59
        self.assertEqual(servers, [])
60

    
61
    def test_server_list_2(self):
62
        """Test if the expected list of servers is returned."""
63
        response = self.get('/api/v1.1/servers', self.user1)
64
        self.assertSuccess(response)
65
        servers = json.loads(response.content)['servers']['values']
66
        db_server = self.vm1
67
        self.assertEqual(servers, [{'name': db_server.name,
68
                                    'id': db_server.id}])
69

    
70
    def test_server_list_detail(self):
71
        """Test if the servers list details are returned."""
72
        user = self.user2
73
        user_vms = {self.vm2.id: self.vm2,
74
                    self.vm4.id: self.vm4}
75

    
76
        response = self.get('/api/v1.1/servers/detail', user)
77
        servers = json.loads(response.content)['servers']['values']
78
        self.assertEqual(len(servers), len(user_vms))
79
        for api_vm in servers:
80
            db_vm = user_vms[api_vm['id']]
81
            self.assertEqual(api_vm['flavor'], db_vm.flavor.id)
82
            self.assertEqual(api_vm['hostId'], db_vm.hostid)
83
            self.assertEqual(api_vm['id'], db_vm.id)
84
            self.assertEqual(api_vm['image'], db_vm.imageid)
85
            self.assertEqual(api_vm['name'], db_vm.name)
86
            self.assertEqual(api_vm['status'], get_rsapi_state(db_vm))
87
            self.assertSuccess(response)
88

    
89
    def test_server_detail(self):
90
        """Test if a server details are returned."""
91
        db_vm = self.vm2
92
        user = self.vm2.userid
93
        net = mfactory.NetworkFactory()
94
        nic = mfactory.NetworkInterfaceFactory(machine=self.vm2, network=net)
95

    
96
        db_vm_meta = mfactory.VirtualMachineMetadataFactory(vm=db_vm)
97

    
98
        response = self.get('/api/v1.1/servers/%d' % db_vm.id, user)
99
        server = json.loads(response.content)['server']
100

    
101
        self.assertEqual(server['flavor'], db_vm.flavor.id)
102
        self.assertEqual(server['hostId'], db_vm.hostid)
103
        self.assertEqual(server['id'], db_vm.id)
104
        self.assertEqual(server['image'], db_vm.imageid)
105
        self.assertEqual(server['name'], db_vm.name)
106
        self.assertEqual(server['status'], get_rsapi_state(db_vm))
107
        api_nic = server['attachments']['values'][0]
108
        self.assertEqual(api_nic['network_id'], str(net.id))
109
        self.assertEqual(api_nic['mac_address'], nic.mac)
110
        self.assertEqual(api_nic['firewallProfile'], nic.firewall_profile)
111
        self.assertEqual(api_nic['ipv4'], nic.ipv4)
112
        self.assertEqual(api_nic['ipv6'], nic.ipv6)
113
        self.assertEqual(api_nic['id'], 'nic-%s-%s' % (db_vm.id, nic.index))
114

    
115
        metadata = server['metadata']['values']
116
        self.assertEqual(len(metadata), 1)
117
        self.assertEqual(metadata[db_vm_meta.meta_key], db_vm_meta.meta_value)
118
        self.assertSuccess(response)
119

    
120
    def test_server_building_nics(self):
121
        db_vm = self.vm2
122
        user = self.vm2.userid
123
        net1 = mfactory.NetworkFactory()
124
        net2 = mfactory.NetworkFactory()
125
        net3 = mfactory.NetworkFactory()
126
        mfactory.NetworkInterfaceFactory(machine=self.vm2, network=net1,
127
                                         state="BUILDING")
128
        nic2 = mfactory.NetworkInterfaceFactory(machine=self.vm2, network=net2,
129
                                                state="ACTIVE")
130
        mfactory.NetworkInterfaceFactory(machine=self.vm2, network=net3,
131
                                         state="BUILDING")
132

    
133
        response = self.get('/api/v1.1/servers/%d' % db_vm.id, user)
134
        server = json.loads(response.content)['server']
135
        nics = server["attachments"]["values"]
136
        self.assertEqual(len(nics), 1)
137
        self.assertEqual(nics[0]["network_id"], str(nic2.network_id))
138

    
139
    def test_noauthorized(self):
140
        """Test 404 for detail of other user vm"""
141
        db_vm = self.vm2
142

    
143
        response = self.get('/api/v1.1/servers/%d' % db_vm.id, 'wrong_user')
144
        self.assertItemNotFound(response)
145

    
146
    def test_wrong_server(self):
147
        """Test 404 response if server does not exist."""
148
        response = self.get('/api/v1.1/servers/%d' % 5000)
149
        self.assertItemNotFound(response)
150

    
151
    def test_create_server_empty(self):
152
        """Test if the create server call returns a 400 badRequest if
153
           no attributes are specified."""
154

    
155
        response = self.post('/api/v1.1/servers', params={})
156
        self.assertBadRequest(response)
157

    
158
    def test_rename_server(self):
159
        vm = self.vm2
160
        request = {'server': {'name': 'new_name'}}
161
        response = self.put('/api/v1.1/servers/%d' % vm.id, vm.userid,
162
                            json.dumps(request), 'json')
163
        self.assertSuccess(response)
164
        self.assertEqual(VirtualMachine.objects.get(id=vm.id).name, "new_name")
165

    
166

    
167
@patch('synnefo.api.util.get_image')
168
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
169
class ServerCreateAPITest(BaseAPITest):
170
    def test_create_server(self, mrapi, mimage):
171
        """Test if the create server call returns the expected response
172
           if a valid request has been speficied."""
173
        mimage.return_value = {'location': 'pithos://foo',
174
                               'checksum': '1234',
175
                               "id": 1,
176
                               "name": "test_image",
177
                               'disk_format': 'diskdump'}
178
        mrapi().CreateInstance.return_value = 12
179
        flavor = mfactory.FlavorFactory()
180
        # Create public network and backend
181
        network = mfactory.NetworkFactory(public=True)
182
        backend = mfactory.BackendFactory()
183
        mfactory.BackendNetworkFactory(network=network, backend=backend)
184

    
185
        request = {
186
                    "server": {
187
                        "name": "new-server-test",
188
                        "userid": "test_user",
189
                        "imageRef": 1,
190
                        "flavorRef": flavor.id,
191
                        "metadata": {
192
                            "My Server Name": "Apache1"
193
                        },
194
                        "personality": []
195
                    }
196
        }
197
        with mocked_quotaholder():
198
            response = self.post('/api/v1.1/servers', 'test_user',
199
                                     json.dumps(request), 'json')
200
        self.assertEqual(response.status_code, 202)
201
        mrapi().CreateInstance.assert_called_once()
202

    
203
        api_server = json.loads(response.content)['server']
204
        self.assertEqual(api_server['status'], "BUILD")
205
        self.assertEqual(api_server['progress'], 0)
206
        self.assertEqual(api_server['metadata']['values'],
207
                        {"My Server Name":  "Apache1"})
208
        self.assertTrue('adminPass' in api_server)
209

    
210
        db_vm = VirtualMachine.objects.get(userid='test_user')
211
        self.assertEqual(api_server['name'], db_vm.name)
212
        self.assertEqual(api_server['status'], db_vm.operstate)
213

    
214
    def test_create_server_no_flavor(self, mrapi, mimage):
215
        request = {
216
                    "server": {
217
                        "name": "new-server-test",
218
                        "userid": "test_user",
219
                        "imageRef": 1,
220
                        "flavorRef": 42,
221
                        "metadata": {
222
                            "My Server Name": "Apache1"
223
                        },
224
                        "personality": []
225
                    }
226
        }
227
        response = self.post('/api/v1.1/servers', 'test_user',
228
                                 json.dumps(request), 'json')
229
        self.assertItemNotFound(response)
230

    
231

    
232
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
233
class ServerDestroyAPITest(BaseAPITest):
234
    def test_delete_server(self, mrapi):
235
        vm = mfactory.VirtualMachineFactory()
236
        response = self.delete('/api/v1.1/servers/%d' % vm.id, vm.userid)
237
        self.assertEqual(response.status_code, 204)
238
        mrapi().DeleteInstance.assert_called_once()
239

    
240
    def test_non_existing_delete_server(self, mrapi):
241
        vm = mfactory.VirtualMachineFactory()
242
        response = self.delete('/api/v1.1/servers/%d' % 42, vm.userid)
243
        self.assertItemNotFound(response)
244
        self.assertFalse(mrapi.mock_calls)
245

    
246

    
247
class ServerMetadataAPITest(BaseAPITest):
248
    def setUp(self):
249
        self.vm = mfactory.VirtualMachineFactory()
250
        self.metadata = mfactory.VirtualMachineMetadataFactory(vm=self.vm)
251

    
252
    def test_get_metadata(self):
253
        vm = self.vm
254
        create_meta = lambda: mfactory.VirtualMachineMetadataFactory(vm=vm)
255
        metadata = [create_meta(), create_meta(), create_meta()]
256
        response = self.get('/api/v1.1/servers/%d/meta' % vm.id, vm.userid)
257
        self.assertTrue(response.status_code in [200, 203])
258
        api_metadata = json.loads(response.content)['metadata']['values']
259
        self.assertEqual(len(api_metadata), len(metadata) + 1)
260
        for db_m in metadata:
261
            self.assertEqual(api_metadata[db_m.meta_key], db_m.meta_value)
262

    
263
        request = {'metadata':
264
                        {'foo': 'bar'},
265
                        metadata[0].meta_key: 'bar2'
266
                  }
267
        response = self.post('/api/v1.1/servers/%d/meta' % vm.id, vm.userid,
268
                             json.dumps(request), 'json')
269
        metadata2 = VirtualMachineMetadata.objects.filter(vm=vm)
270
        response = self.get('/api/v1.1/servers/%d/meta' % vm.id, vm.userid)
271
        self.assertTrue(response.status_code in [200, 203])
272
        api_metadata2 = json.loads(response.content)['metadata']['values']
273
        self.assertTrue('foo' in api_metadata2.keys())
274
        self.assertTrue(api_metadata2[metadata[0].meta_key], 'bar2')
275
        self.assertEqual(len(api_metadata2), len(metadata2))
276
        for db_m in metadata2:
277
            self.assertEqual(api_metadata2[db_m.meta_key], db_m.meta_value)
278

    
279
        # Create new meta
280
        request = {'meta': {'foo2': 'bar2'}}
281
        response = self.put('/api/v1.1/servers/%d/meta/foo2' % vm.id,
282
                            vm.userid, json.dumps(request), 'json')
283

    
284
        # Get the new meta
285
        response = self.get('/api/v1.1/servers/%d/meta/foo2' % vm.id,
286
                            vm.userid)
287
        meta = json.loads(response.content)['meta']
288
        self.assertEqual(meta['foo2'], 'bar2')
289

    
290
        # Delete the new meta
291
        response = self.delete('/api/v1.1/servers/%d/meta/foo2' % vm.id,
292
                               vm.userid)
293
        self.assertEqual(response.status_code, 204)
294

    
295
        # Try to get the deleted meta: should raise 404
296
        response = self.get('/api/v1.1/servers/%d/meta/foo2' % vm.id,
297
                            vm.userid)
298
        self.assertEqual(response.status_code, 404)
299

    
300
    def test_invalid_metadata(self):
301
        vm = self.vm
302
        response = self.post('/api/v1.1/servers/%d/meta' % vm.id, vm.userid)
303
        self.assertBadRequest(response)
304
        self.assertEqual(len(vm.metadata.all()), 1)
305

    
306
    def test_invalid_metadata_server(self):
307
        response = self.post('/api/v1.1/servers/42/meta', 'user')
308
        self.assertItemNotFound(response)
309

    
310
    def test_get_meta_invalid_key(self):
311
        vm = self.vm
312
        response = self.get('/api/v1.1/servers/%d/meta/foo2' % vm.id,
313
                            vm.userid)
314
        self.assertItemNotFound(response)
315

    
316

    
317
@patch('synnefo.api.util.get_image')
318
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
319
class ServerActionAPITest(BaseAPITest):
320
    def test_actions(self, mrapi, mimage):
321
        actions = ['start', 'shutdown', 'reboot']
322
        vm = mfactory.VirtualMachineFactory()
323
        vm.operstate = "STOPPED"
324
        vm.save()
325
        for action in actions:
326
            val = {'type': 'HARD'} if action == 'reboot' else {}
327
            request = {action: val}
328
            response = self.post('/api/v1.1/servers/%d/action' % vm.id,
329
                                vm.userid, json.dumps(request), 'json')
330
            self.assertEqual(response.status_code, 202)
331
            if action == 'shutdown':
332
                self.assertEqual(VirtualMachine.objects.get(id=vm.id).action,
333
                                 "STOP")
334
            else:
335
                self.assertEqual(VirtualMachine.objects.get(id=vm.id).action,
336
                                 action.upper())
337

    
338
    def test_action_in_building_vm(self, mrapi, mimage):
339
        """Test building in progress"""
340
        vm = mfactory.VirtualMachineFactory()
341
        request = {'start': '{}'}
342
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
343
                             vm.userid, json.dumps(request), 'json')
344
        self.assertEqual(response.status_code, 409)
345
        self.assertFalse(mrapi.mock_calls)
346

    
347
    def test_destroy_build_vm(self, mrapi, mimage):
348
        """Test building in progress"""
349
        vm = mfactory.VirtualMachineFactory()
350
        response = self.delete('/api/v1.1/servers/%d' % vm.id,
351
                             vm.userid)
352
        self.assertSuccess(response)
353
        mrapi().RemoveInstance.assert_called_once()
354

    
355
    def test_firewall(self, mrapi, mimage):
356
        vm = mfactory.VirtualMachineFactory()
357
        vm.operstate = "STOPPED"
358
        vm.save()
359
        request = {'firewallProfile': {'profile': 'PROTECTED'}}
360
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
361
                             vm.userid, json.dumps(request), 'json')
362
        self.assertEqual(response.status_code, 202)
363
        mrapi().ModifyInstance.assert_called_once()
364

    
365
    def test_unsupported_firewall(self, mrapi, mimage):
366
        vm = mfactory.VirtualMachineFactory()
367
        vm.operstate = "STOPPED"
368
        vm.save()
369
        request = {'firewallProfile': {'profile': 'FOO'}}
370
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
371
                             vm.userid, json.dumps(request), 'json')
372
        self.assertBadRequest(response)
373
        self.assertFalse(mrapi.mock_calls)
374

    
375

    
376
class ServerVNCConsole(BaseAPITest):
377
    def test_not_active_server(self):
378
        """Test console req for not ACTIVE server returns badRequest"""
379
        vm = mfactory.VirtualMachineFactory()
380
        data = json.dumps({'console': {'type': 'vnc'}})
381
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
382
                             vm.userid, data, 'json')
383
        self.assertBadRequest(response)
384

    
385
    def test_active_server(self):
386
        """Test console req for ACTIVE server"""
387
        vm = mfactory.VirtualMachineFactory()
388
        vm.operstate = 'STARTED'
389
        vm.save()
390

    
391
        data = json.dumps({'console': {'type': 'vnc'}})
392
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
393
                             vm.userid, data, 'json')
394
        self.assertEqual(response.status_code, 200)
395
        reply = json.loads(response.content)
396
        self.assertEqual(reply.keys(), ['console'])
397
        console = reply['console']
398
        self.assertEqual(console['type'], 'vnc')
399
        self.assertEqual(set(console.keys()),
400
                         set(['type', 'host', 'port', 'password']))
401

    
402
    def test_wrong_console_type(self):
403
        """Test console req for ACTIVE server"""
404
        vm = mfactory.VirtualMachineFactory()
405
        vm.operstate = 'STARTED'
406
        vm.save()
407

    
408
        data = json.dumps({'console': {'type': 'foo'}})
409
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
410
                             vm.userid, data, 'json')
411
        self.assertBadRequest(response)