Commit 238b78d6 authored by Vangelis Koukis's avatar Vangelis Koukis
Browse files

Merge branch 'api-tests'

parents e45740ca c3fa31bd
......@@ -2,6 +2,8 @@
# Copyright (c) 2010 Greek Research and Technology Network
#
from socket import getfqdn
from django.conf import settings
from django.http import HttpResponse
from django.template.loader import render_to_string
......@@ -74,10 +76,10 @@ def get_console(request, vm, args):
passwd = random_password()
request_vnc_forwarding(sport, daddr, dport, passwd)
vnc = { 'host': '62.217.120.67', 'port': sport, 'password': passwd }
vnc = { 'host': getfqdn(), 'port': sport, 'password': passwd }
# Format to be reviewed by [verigak], FIXME
if request.type == 'xml':
if request.serialization == 'xml':
mimetype = 'application/xml'
data = render_to_string('vnc.xml', {'vnc': vnc})
else:
......
......@@ -249,9 +249,7 @@
"name": "Debian Squeeze",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE",
"description": "Full Debian Squeeze Installation",
"size": 5678
"state": "ACTIVE"
}
},
{
......@@ -330,33 +328,8 @@
"created": "2011-02-10 00:00:00",
"updated": "2011-02-10 00:00:00",
"state": "ACTIVE",
"description": "Full Slackware 13.1 Installation",
"size": 1234,
"owner" : 1,
"sourcevm": 1001
}
},
{
"model": "db.VirtualMachineGroup",
"pk": 1,
"fields": {
"name": "one group of vms",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"owner" : 1,
"machines" : [1001,1002]
}
},
{
"model": "db.VirtualMachineGroup",
"pk": 2,
"fields": {
"name": "a second group of vms",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"owner" : 1,
"machines" : [1003,1004]
}
}
]
......@@ -6,9 +6,7 @@
"name": "Debian Unstable",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE",
"description": "Debian Sid, full installation",
"size": 4096
"state": "ACTIVE"
}
},
{
......@@ -18,9 +16,7 @@
"name": "Red Hat Enterprise Linux",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE",
"description": "Red Hat Enterprise Linux, full installation",
"size": 2048
"state": "ACTIVE"
}
},
{
......@@ -30,9 +26,7 @@
"name": "Ubuntu 10.10",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE",
"description": "Ubuntu 10.10, full installation",
"size": 8192
"state": "ACTIVE"
}
},
......
......@@ -7,6 +7,7 @@ from django.http import HttpResponse
from django.template.loader import render_to_string
from django.utils import simplejson as json
from synnefo.api.faults import ItemNotFound
from synnefo.api.util import get_user, get_request_dict, api_method
from synnefo.db.models import Flavor
......
......@@ -91,7 +91,7 @@ def list_images(request, detail=False):
since = isoparse(request.GET.get('changes-since'))
if since:
avail_images = Image.objects.filter(updated__gt=since)
avail_images = Image.objects.filter(updated__gte=since)
if not avail_images:
return HttpResponse(status=304)
else:
......@@ -132,8 +132,7 @@ def create_image(request):
owner = get_user()
vm = get_vm(server_id)
image = Image.objects.create(name=name, size=0, owner=owner, sourcevm=vm)
image.save()
image = Image.objects.create(name=name, owner=owner, sourcevm=vm)
imagedict = image_to_dict(image)
if request.serialization == 'xml':
......
......@@ -14,7 +14,7 @@ from synnefo.api.faults import BadRequest, ItemNotFound
from synnefo.api.util import *
from synnefo.db.models import Image, Flavor, VirtualMachine, VirtualMachineMetadata
from synnefo.logic.utils import get_rsapi_state
from synnefo.util.rapi import GanetiRapiClient
from synnefo.util.rapi import GanetiRapiClient, GanetiApiError
from synnefo.logic import backend
import logging
......@@ -120,7 +120,7 @@ def list_servers(request, detail=False):
since = isoparse(request.GET.get('changes-since'))
if since:
user_vms = VirtualMachine.objects.filter(updated__gt=since)
user_vms = VirtualMachine.objects.filter(updated__gte=since)
if not user_vms:
return HttpResponse(status=304)
else:
......@@ -151,50 +151,65 @@ def create_server(request):
try:
server = req['server']
name = server['name']
metadata = server.get('metadata', {})
assert isinstance(metadata, dict)
sourceimage = Image.objects.get(id=server['imageRef'])
flavor = Flavor.objects.get(id=server['flavorRef'])
except KeyError:
except (KeyError, AssertionError):
raise BadRequest('Malformed request.')
except Image.DoesNotExist:
raise ItemNotFound
except Flavor.DoesNotExist:
raise ItemNotFound
vm = VirtualMachine.objects.create(
vm = VirtualMachine(
name=name,
owner=get_user(),
sourceimage=sourceimage,
ipfour='0.0.0.0',
ipsix='::1',
flavor=flavor)
# Pick a random password for the VM.
# FIXME: This must be passed to the Ganeti OS provider via CreateInstance()
passwd = random_password()
# We *must* save the VM instance now,
# so that it gets a vm.id and vm.backend_id is valid.
vm.save()
if request.META.get('SERVER_NAME', None) == 'testserver':
name = 'test-server'
backend_name = 'test-server'
dry_run = True
else:
name = vm.backend_id
backend_name = vm.backend_id
dry_run = False
jobId = rapi.CreateInstance(
mode='create',
name=name,
disk_template='plain',
disks=[{"size": 2000}], #FIXME: Always ask for a 2GB disk for now
nics=[{}],
os='debootstrap+default', #TODO: select OS from imageRef
ip_check=False,
name_check=False,
pnode=rapi.GetNodes()[0], #TODO: verify if this is necessary
dry_run=dry_run,
beparams=dict(auto_balance=True, vcpus=flavor.cpu, memory=flavor.ram))
vm.save()
try:
jobId = rapi.CreateInstance(
mode='create',
name=backend_name,
disk_template='plain',
disks=[{"size": 2000}], #FIXME: Always ask for a 2GB disk for now
nics=[{}],
os='debootstrap+default', #TODO: select OS from imageRef
ip_check=False,
name_check=False,
pnode=rapi.GetNodes()[0], #TODO: verify if this is necessary
dry_run=dry_run,
beparams=dict(auto_balance=True, vcpus=flavor.cpu, memory=flavor.ram))
except GanetiApiError:
vm.delete()
raise ServiceUnavailable('Could not create server.')
for key, val in metadata.items():
VirtualMachineMetadata.objects.create(meta_key=key, meta_value=val, vm=vm)
logging.info('created vm with %s cpus, %s ram and %s storage' % (flavor.cpu, flavor.ram, flavor.disk))
server = vm_to_dict(vm, detail=True)
server['status'] = 'BUILD'
server['adminPass'] = random_password()
server['adminPass'] = passwd
return render_server(request, server, status=202)
@api_method('GET')
......
......@@ -15,7 +15,7 @@
<title type="text">Version {{ version.id }}</title>
<updated>{{ version.updated }}</updated>
{% for link in version.links %}
<link rel="{{ link.rel }}" href="{{ link.href }}"/>
<link rel="{{ link.rel }}" {% if link.type %}type="{{ link.type }}" {% endif %}href="{{ link.href }}"/>
{% endfor %}
<content type="text">Version {{ version.id }} {{ version.status }} ({{ version.updated }})</content>
</entry>
......
......@@ -7,7 +7,7 @@
{% endfor %}
</media-types>
{% for link in version.links %}
<atom:link rel="{{ link.rel }}" href="{{ link.href }}"/>
<atom:link rel="{{ link.rel }}" {% if link.type %}type="{{ link.type }}" {% endif %}href="{{ link.href }}"/>
{% endfor %}
</version>
{% endspaceless %}
#
# Unit Tests for api
#
# Provides automated tests for api module
#
# Copyright 2011 Greek Research and Technology Network
# Copyright (c) 2010 Greek Research and Technology Network
#
from email.utils import parsedate
from time import mktime
import datetime
from django.utils import simplejson as json
from django.test import TestCase
from django.test.client import Client
#from synnefo.api.tests_auth import AuthTestCase
from synnefo.db.models import VirtualMachine, VirtualMachineGroup
from synnefo.db.models import Flavor, Image
from synnefo.api.tests_redux import APIReduxTestCase
from synnefo.api.tests_auth import AuthTestCase
from synnefo.logic import utils
class APITestCase(TestCase):
fixtures = ['api_test_data', ]
fixtures = ['api_test_data']
test_server_id = 1001
test_image_id = 1
test_flavor_id = 1
......@@ -33,24 +33,19 @@ class APITestCase(TestCase):
self.client = Client()
def test_api_version(self):
""" check rackspace cloud servers API version
"""
response = self.client.get('/api/v1.0/')
# Check that the response is 200 OK.
"""Check API version."""
response = self.client.get('/api/v1.1/')
self.assertEqual(response.status_code, 200)
api_version = json.loads(response.content)['version']
self.assertEqual(api_version['id'], 'v1.1')
self.assertEqual(api_version['status'], 'CURRENT')
self.assertEqual(api_version['wadl'],
'http://docs.rackspacecloud.com/servers/api/v1.0/application.wadl')
self.assertEqual(api_version['docURL'],
'http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf')
self.assertEqual(api_version['id'], 'v1.0')
def test_server_list(self):
""" test if the expected list of servers is returned by the API
"""
response = self.client.get('/api/v1.0/servers')
vms_from_api = json.loads(response.content)['servers']
"""Test if the expected list of servers is returned."""
response = self.client.get('/api/v1.1/servers')
vms_from_api = json.loads(response.content)['servers']['values']
vms_from_db = VirtualMachine.objects.filter(deleted=False)
self.assertEqual(len(vms_from_api), len(vms_from_db))
self.assertTrue(response.status_code in [200, 203])
......@@ -60,10 +55,9 @@ class APITestCase(TestCase):
self.assertEqual(vm_from_api['name'], vm_from_db.name)
def test_server_details(self):
""" test if the expected server is returned by the API
"""
response = self.client.get('/api/v1.0/servers/' +
str(self.test_server_id))
"""Test if the expected server is returned."""
response = self.client.get('/api/v1.1/servers/%d' % self.test_server_id)
vm_from_api = json.loads(response.content)['server']
vm_from_db = VirtualMachine.objects.get(id=self.test_server_id)
self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
......@@ -72,18 +66,18 @@ class APITestCase(TestCase):
self.assertEqual(vm_from_api['imageRef'], vm_from_db.flavor.id)
self.assertEqual(vm_from_api['name'], vm_from_db.name)
self.assertEqual(vm_from_api['status'], utils.get_rsapi_state(vm_from_db))
self.assertTrue(response.status_code in [200,203])
self.assertTrue(response.status_code in [200, 203])
def test_servers_details(self):
""" test if the servers details are returned by the API
"""
response = self.client.get('/api/v1.0/servers/detail')
"""Test if the servers details are returned."""
response = self.client.get('/api/v1.1/servers/detail')
vms_from_db = VirtualMachine.objects.filter(deleted=False)
id_list = [vm.id for vm in vms_from_db]
number = 0
for vm_id in id_list:
vm_from_api = json.loads(response.content)['servers'][number]
vm_from_api = json.loads(response.content)['servers']['values'][number]
vm_from_db = VirtualMachine.objects.get(id=vm_id)
self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
self.assertEqual(vm_from_api['hostId'], vm_from_db.hostid)
......@@ -92,7 +86,7 @@ class APITestCase(TestCase):
self.assertEqual(vm_from_api['name'], vm_from_db.name)
self.assertEqual(vm_from_api['status'], utils.get_rsapi_state(vm_from_db))
number += 1
vms_from_api = json.loads(response.content)['servers']
vms_from_api = json.loads(response.content)['servers']['values']
for vm_from_api in vms_from_api:
vm_from_db = VirtualMachine.objects.get(id=vm_from_api['id'])
self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
......@@ -105,23 +99,22 @@ class APITestCase(TestCase):
def test_wrong_server(self):
""" test 404 response if server does not exist
"""
response = self.client.get('/api/v1.0/servers/' +
str(self.test_wrong_server_id))
"""Test 404 response if server does not exist."""
response = self.client.get('/api/v1.1/servers/%d' % self.test_wrong_server_id)
self.assertEqual(response.status_code, 404)
def test_create_server_empty(self):
""" test if the create server call returns a 400 badRequest if no
attributes are specified
"""
response = self.client.post('/api/v1.0/servers', {})
"""Test if the create server call returns a 400 badRequest if
no attributes are specified."""
response = self.client.post('/api/v1.1/servers', {})
self.assertEqual(response.status_code, 400)
def test_create_server(self):
""" test if the create server call returns the expected response
if a valid request has been speficied
"""
"""Test if the create server call returns the expected response
if a valid request has been speficied."""
request = {
"server": {
"name": "new-server-test",
......@@ -133,25 +126,21 @@ class APITestCase(TestCase):
"personality": []
}
}
response = self.client.post('/api/v1.0/servers',
json.dumps(request),
response = self.client.post('/api/v1.1/servers', json.dumps(request),
content_type='application/json')
self.assertEqual(response.status_code, 202)
#TODO: check response.content
#TODO: check create server with wrong options (eg non existing flavor)
def test_server_polling(self):
""" test if the server polling works as expected
"""
response = self.client.get('/api/v1.0/servers/detail')
vms_from_api_initial = json.loads(response.content)['servers']
then = datetime.datetime.now().isoformat().split('.')[0]
#isoformat also gives miliseconds that are not needed
response = self.client.get('/api/v1.0/servers/detail?changes-since=%s'
% then)
"""Test if the server polling works as expected."""
response = self.client.get('/api/v1.1/servers/detail')
vms_from_api_initial = json.loads(response.content)['servers']['values']
ts = mktime(parsedate(response['Date']))
since = datetime.datetime.fromtimestamp(ts).isoformat() + 'Z'
response = self.client.get('/api/v1.1/servers/detail?changes-since=%s' % since)
self.assertEqual(len(response.content), 0)
#no changes were made
#now create a machine. Then check if it is on the list
request = {
......@@ -165,87 +154,66 @@ class APITestCase(TestCase):
"personality": []
}
}
response = self.client.post('/api/v1.0/servers',
json.dumps(request),
content_type='application/json')
path = '/api/v1.1/servers'
response = self.client.post(path, json.dumps(request), content_type='application/json')
self.assertEqual(response.status_code, 202)
response = self.client.get('/api/v1.0/servers/detail?changes-since=%s'
% then)
vms_from_api_after = json.loads(response.content)['servers']
response = self.client.get('/api/v1.1/servers/detail?changes-since=%s' % since)
self.assertEqual(response.status_code, 200)
vms_from_api_after = json.loads(response.content)['servers']['values']
#make sure the newly created server is included on the updated list
self.assertEqual(len(vms_from_api_after), 1)
def test_reboot_server(self):
""" test if the specified server is rebooted
"""
request = {
"reboot": '{"type" : "HARD"}'
}
response = self.client.post('/api/v1.0/servers/' +
str(self.test_server_id) + '/action',
json.dumps(request),
content_type='application/json')
"""Test if the specified server is rebooted."""
request = {'reboot': {'type': 'HARD'}}
path = '/api/v1.1/servers/%d/action' % self.test_server_id
response = self.client.post(path, json.dumps(request), content_type='application/json')
self.assertEqual(response.status_code, 202)
#server id that does not exist
response = self.client.post('/api/v1.0/servers/' +
str(self.test_wrong_server_id) + '/action',
json.dumps(request),
content_type='application/json')
path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
response = self.client.post(path, json.dumps(request), content_type='application/json')
self.assertEqual(response.status_code, 404)
def test_shutdown_server(self):
""" test if the specified server is shutdown
"""
request = {
"shutdown": {"timeout": "5"}
}
response = self.client.post('/api/v1.0/servers/' +
str(self.test_server_id) + '/action',
json.dumps(request),
content_type='application/json')
"""Test if the specified server is shutdown."""
request = {'shutdown': {}}
path = '/api/v1.1/servers/%d/action' % self.test_server_id
response = self.client.post(path, json.dumps(request), content_type='application/json')
self.assertEqual(response.status_code, 202)
#server id that does not exist
response = self.client.post('/api/v1.0/servers/' +
str(self.test_wrong_server_id) + '/action',
json.dumps(request),
content_type='application/json')
path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
response = self.client.post(path, json.dumps(request), content_type='application/json')
self.assertEqual(response.status_code, 404)
def test_start_server(self):
""" test if the specified server is started
"""
request = {
"start": {"type": "NORMAL"}
}
response = self.client.post('/api/v1.0/servers/' +
str(self.test_server_id) + '/action',
json.dumps(request),
content_type='application/json')
"""Test if the specified server is started."""
request = {'start': {}}
path = '/api/v1.1/servers/%d/action' % self.test_server_id
response = self.client.post(path, json.dumps(request), content_type='application/json')
self.assertEqual(response.status_code, 202)
#server id that does not exist
response = self.client.post('/api/v1.0/servers/' +
str(self.test_wrong_server_id) + '/action',
json.dumps(request),
content_type='application/json')
path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
response = self.client.post(path, json.dumps(request), content_type='application/json')
self.assertEqual(response.status_code, 404)
def test_delete_server(self):
""" test if the specified server is deleted
"""
response = self.client.delete('/api/v1.0/servers/' +
str(self.test_server_id))
self.assertEqual(response.status_code, 202)
"""Test if the specified server is deleted."""
response = self.client.delete('/api/v1.1/servers/%d' % self.test_server_id)
self.assertEqual(response.status_code, 204)
#server id that does not exist
response = self.client.delete('/api/v1.0/servers/' +
str(self.test_wrong_server_id))
response = self.client.delete('/api/v1.1/servers/%d' % self.test_wrong_server_id)
self.assertEqual(response.status_code, 404)
def test_flavor_list(self):
""" test if the expected list of flavors is returned by the API
"""
response = self.client.get('/api/v1.0/flavors')
flavors_from_api = json.loads(response.content)['flavors']
"""Test if the expected list of flavors is returned by."""
response = self.client.get('/api/v1.1/flavors')
flavors_from_api = json.loads(response.content)['flavors']['values']
flavors_from_db = Flavor.objects.all()
self.assertEqual(len(flavors_from_api), len(flavors_from_db))
self.assertTrue(response.status_code in [200, 203])
......@@ -255,11 +223,11 @@ class APITestCase(TestCase):
self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
def test_flavors_details(self):
""" test if the flavors details are returned by the API
"""
response = self.client.get('/api/v1.0/flavors/detail')
"""Test if the flavors details are returned."""
response = self.client.get('/api/v1.1/flavors/detail')
flavors_from_db = Flavor.objects.all()
flavors_from_api = json.loads(response.content)['flavors']
flavors_from_api = json.loads(response.content)['flavors']['values']
# Assert that all flavors in the db appear inthe API call result
for i in range(0, len(flavors_from_db)):
......@@ -284,10 +252,9 @@ class APITestCase(TestCase):
self.assertTrue(response.status_code in [200, 203])
def test_flavor_details(self):
""" test if the expected flavor is returned by the API
"""
response = self.client.get('/api/v1.0/flavors/' +
str(self.test_flavor_id))
"""Test if the expected flavor is returned."""
response = self.client.get('/api/v1.1/flavors/%d' % self.test_flavor_id)
flavor_from_api = json.loads(response.content)['flavor']
flavor_from_db = Flavor.objects.get(id=self.test_flavor_id)
self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
......@@ -298,17 +265,16 @@ class APITestCase(TestCase):
self.assertTrue(response.status_code in [200, 203])
def test_wrong_flavor(self):
""" test 404 result when requesting a flavor that does not exist
"""
response = self.client.get('/api/v1.0/flavors/' +
str(self.test_wrong_flavor_id))
"""Test 404 result when requesting a flavor that does not exist."""
response = self.client.get('/api/v1.1/flavors/%d' % self.test_wrong_flavor_id)
self.assertTrue(response.status_code in [404, 503])
def test_image_list(self):
""" test if the expected list of images is returned by the API
"""
response = self.client.get('/api/v1.0/images')
images_from_api = json.loads(response.content)['images']
"""Test if the expected list of images is returned by the API."""
response = self.client.get('/api/v1.1/images')
images_from_api = json.loads(response.content)['images']['values']
images_from_db = Image.objects.all()
self.assertEqual(len(images_from_api), len(images_from_db))
self.assertTrue(response.status_code in [200, 203])
......@@ -318,139 +284,133 @@ class APITestCase(TestCase):
self.assertEqual(image_from_api['name'], image_from_db.name)