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,16 +151,18 @@ 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,
......@@ -168,16 +170,25 @@ def create_server(request):
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
try:
jobId = rapi.CreateInstance(
mode='create',
name=name,
name=backend_name,
disk_template='plain',
disks=[{"size": 2000}], #FIXME: Always ask for a 2GB disk for now
nics=[{}],
......@@ -187,14 +198,18 @@ def create_server(request):
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.')
vm.save()
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 %}
This diff is collapsed.
#
# Copyright (c) 2010 Greek Research and Technology Network
#
from django.test import TestCase
from django.test.client import Client
from django.utils import simplejson as json
API = 'v1.1redux'
class APIReduxTestCase(TestCase):
fixtures = [ 'api_redux_test_data' ]
def setUp(self):
self.client = Client()
self.server_id = 0
def create_server_name(self):
self.server_id += 1
return 'server%d' % self.server_id
def test_create_server_json(self):
TEMPLATE = '''
{
"server" : {
"name" : "%(name)s",
"flavorRef" : "%(flavorRef)s",
"imageRef" : "%(imageRef)s"
}
}
'''
def new_server(imageRef=1, flavorRef=1):
name = self.create_server_name()
return name, TEMPLATE % dict(name=name, imageRef=imageRef, flavorRef=flavorRef)
def verify_response(response, name):
assert response.status_code == 202
reply = json.loads(response.content)
server = reply['server']
assert server['name'] == name
assert server['imageRef'] == 1
assert server['flavorRef'] == 1
assert server['status'] == 'BUILD'
assert server['adminPass']
assert server['addresses']
def verify_error(response, code, name):
assert response.status_code == code
reply = json.loads(response.content)
assert name in reply
assert reply[name]['code'] == code
name, data = new_server()
url = '/api/%s/servers' % API
response = self.client.post(url, content_type='application/json', data=data)
verify_response(response, name)
name, data = new_server()
url = '/api/%s/servers.json' % API
response = self.client.post(url, content_type='application/json', data=data)
verify_response(response, name)
name, data = new_server()
url = '/api/%s/servers.json' % API
response = self.client.post(url, content_type='application/json', data=data,
HTTP_ACCEPT='application/xml')
verify_response(response, name)
name, data = new_server(imageRef=0)
url = '/api/%s/servers' % API
response = self.client.post(url, content_type='application/json', data=data)
verify_error(response, 404, 'itemNotFound')
name, data = new_server(flavorRef=0)
url = '/api/%s/servers' % API
response = self.client.post(url, content_type='application/json', data=data)
verify_error(response, 404, 'itemNotFound')
url = '/api/%s/servers' % API
response = self.client.post(url, content_type='application/json', data='INVALID')
verify_error(response, 400, 'badRequest')
......@@ -6,7 +6,9 @@ from datetime import timedelta, tzinfo
from functools import wraps
from random import choice
from string import ascii_letters, digits
from time import time
from traceback import format_exc
from wsgiref.handlers import format_date_time
from django.conf import settings
from django.http import HttpResponse
......@@ -45,17 +47,18 @@ def isoparse(s):
try:
since = dateutil.parser.parse(s)
utc_since = since.astimezone(UTC()).replace(tzinfo=None)
except ValueError:
raise BadRequest('Invalid changes-since parameter.')
now = datetime.datetime.now(UTC())
if since > now:
now = datetime.datetime.now()
if utc_since > now:
raise BadRequest('changes-since value set in the future.')
if now - since > timedelta(seconds=settings.POLL_LIMIT):
if now - utc_since > timedelta(seconds=settings.POLL_LIMIT):
raise BadRequest('Too old changes-since value.')
return since
return utc_since
def random_password(length=8):
pool = ascii_letters + digits
......@@ -121,6 +124,17 @@ def get_request_dict(request):
raise BadRequest('Unsupported Content-Type.')
def update_response_headers(request, response):
if request.serialization == 'xml':
response['Content-Type'] = 'application/xml'
elif request.serialization == 'atom':
response['Content-Type'] = 'application/atom+xml'
else:
response['Content-Type'] = 'application/json'
if request.META.get('SERVER_NAME') == 'testserver':
response['Date'] = format_date_time(time())
def render_metadata(request, metadata, use_values=False, status=200):
if request.serialization == 'xml':
data = render_to_string('metadata.xml', {'metadata': metadata})
......@@ -147,14 +161,7 @@ def render_fault(request, fault):
data = json.dumps(d)
resp = HttpResponse(data, status=fault.code)
if request.serialization == 'xml':
resp['Content-Type'] = 'application/xml'
elif request.serialization == 'atom':
resp['Content-Type'] = 'application/atom+xml'
else:
resp['Content-Type'] = 'application/json'
update_response_headers(request, resp)
return resp
......@@ -196,13 +203,7 @@ def api_method(http_method=None, atom_allowed=False):
raise BadRequest('Method not allowed.')
resp = func(request, *args, **kwargs)
if request.serialization == 'xml':
resp['Content-Type'] = 'application/xml'
elif request.serialization == 'atom':
resp['Content-Type'] = 'application/atom+xml'
else:
resp['Content-Type'] = 'application/json'
update_response_headers(request, resp)
return resp
except Fault, fault:
......
......@@ -31,6 +31,18 @@ MEDIA_TYPES = [
{'base': 'application/json', 'type': 'application/vnd.openstack.compute-v1.1+json'}
]
DESCRIBED_BY = [
{
'rel' : 'describedby',
'type' : 'application/pdf',
'href' : 'http://docs.rackspacecloud.com/servers/api/v1.1/cs-devguide-20110125.pdf'
},
{
'rel' : 'describedby',
'type' : 'application/vnd.sun.wadl+xml',
'href' : 'http://docs.rackspacecloud.com/servers/api/v1.1/application.wadl'
}
]
@api_method('GET', atom_allowed=True)
def versions_list(request):
......@@ -58,6 +70,7 @@ def version_details(request, api_version):
# We hardcode to v1.1 since it is the only one we support
version = VERSION_1_1.copy()
version['links'] = version['links'] + DESCRIBED_BY
if request.serialization == 'xml':
version['media_types'] = MEDIA_TYPES
......
......@@ -100,12 +100,28 @@
"name": "Debian Squeeze",
"updated": "2011-02-06 00:00:00",
"created": "2011-02-06 00:00:00",
"size" : 2000,
"state": "ACTIVE",
"description": "Full Debian Squeeze Installation",
"owner" : 30000
}
},
{
"model": "db.ImageMetadata",
"pk": 1,
"fields": {
"meta_key": "description",
"meta_value": "Debian Squeeze, full installation",
"image": 30000
}
},
{
"model": "db.ImageMetadata",
"pk": 2,
"fields": {
"meta_key": "size",
"meta_value": "2048",
"image": 30000
}
},
{
"model": "db.VirtualMachine",
"pk": 30000,
......
......@@ -6,7 +6,7 @@
"name": "Debian Unstable",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE",
"state": "ACTIVE"
}
},
{
......@@ -16,7 +16,7 @@
"name": "Red Hat Enterprise Linux",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE",
"state": "ACTIVE"
}
},
{
......@@ -26,7 +26,7 @@
"name": "Ubuntu 10.10",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE",
"state": "ACTIVE"
}
},
{
......
......@@ -6,8 +6,7 @@
"name": "Debian Squeeze",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE",
"size": 5678
"state": "ACTIVE"
}
},
{
......@@ -96,7 +95,6 @@
"created": "2011-02-10 00:00:00",
"updated": "2011-02-10 00:00:00",
"state": "ACTIVE",
"size": 1234,
"owner": 1,
"sourcevm": 1001
}
......
......@@ -274,6 +274,8 @@ class VirtualMachine(models.Model):
def _get_backend_id(self):
"""Returns the backend id for this VM by prepending backend-prefix."""
if not self.id:
raise VirtualMachine.InvalidBackendIdError("self.id is None")
return '%s%s' % (settings.BACKEND_PREFIX_ID, str(self.id))
backend_id = property(_get_backend_id)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment