Commit fdc452c6 authored by Georgios Gousios's avatar Georgios Gousios
Browse files

Merge branch 'master' of https://code.grnet.gr/git/synnefo

parents 3316449a de71e571
......@@ -171,19 +171,18 @@ print out DDL statements. It should not fail.
$ ./bin/python manage.py loaddata db/fixtures/flavors.json
$ ./bin/python manage.py loaddata db/fixtures/images.json
The following fictures can be loaded optionally depending on
The following fixtures can be loaded optionally depending on
testing requirements:
$ ./bin/python manage.py loaddata db/fixtures/vms.json
$ ./bin/python manage.py loaddata db/fixtures/initial_data.json
$ ./bin/python manage.py loaddata db/fixtures/disks.json
7. Set the BACKEND_PREFIX_ID variable to some unique prefix, e.g. your commit
username
8. Start the system
$ ./bin/python db/db_controller.py #DB synch daemon
$ ./bin/python manage.py runserver #Django
$ ./bin/python db/db_controller.py # DB synch daemon
$ ./bin/python manage.py runserver # Django
9. (Hopefully) Done
......
......@@ -38,11 +38,26 @@ def get_console(request, vm, args):
"""Arrange for an OOB console of the specified type
This method arranges for an OOB console of the specified type.
Only "vnc" type consoles are supported for now.
Only consoles of type "vnc" are supported for now.
It uses a running instance of vncauthproxy to setup proper
VNC forwarding with a random password, then returns the necessary
VNC connection info to the caller.
JSON Request: {
"console": {
"type": "vnc"
}
}
JSON Reply: {
"vnc": {
"host": "fqdn_here",
"port": a_port_here,
"password": "a_password_here"
}
}
"""
# Normal Response Code: 200
# Error Response Codes: computeFault (400, 500),
......@@ -55,28 +70,39 @@ def get_console(request, vm, args):
# overLimit (413)
try:
console_type = args.get('type', '')
if console_type != 'VNC':
raise BadRequest(message="type can only be 'VNC'")
if console_type != 'vnc':
raise BadRequest(message="type can only be 'vnc'")
except KeyError:
raise BadRequest()
# Use RAPI to get VNC console information for this instance
if get_rsapi_state(vm) != 'ACTIVE':
raise BadRequest(message="Server not in ACTIVE state")
console_data = rapi.GetInstanceConsole(vm.backend_id)
if settings.TEST:
console_data = { 'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000 }
else:
console_data = rapi.GetInstanceConsole(vm.backend_id)
if console_data['kind'] != 'vnc':
raise ServiceUnavailable()
# Let vncauthproxy decide on the source port.
# FIXME
# sport = 0
sport = console_data['port'] - 1000
# The alternative: static allocation, e.g.
# sport = console_data['port'] - 1000
sport = 0
daddr = console_data['host']
dport = console_data['port']
passwd = random_password()
request_vnc_forwarding(sport, daddr, dport, passwd)
vnc = { 'host': getfqdn(), 'port': sport, 'password': passwd }
try:
if settings.TEST:
fwd = { 'source_port': 1234, 'status': 'OK' }
else:
fwd = request_vnc_forwarding(sport, daddr, dport, passwd)
if fwd['status'] != "OK":
raise ServiceUnavailable()
vnc = { 'host': getfqdn(), 'port': fwd['source_port'], 'password': passwd }
except Exception:
raise ServiceUnavailable("Could not allocate VNC console port")
# Format to be reviewed by [verigak], FIXME
if request.serialization == 'xml':
......@@ -84,7 +110,7 @@ def get_console(request, vm, args):
data = render_to_string('vnc.xml', {'vnc': vnc})
else:
mimetype = 'application/json'
data = json.dumps(vnc)
data = json.dumps({'vnc': vnc})
return HttpResponse(data, mimetype=mimetype, status=200)
......
......@@ -6,9 +6,9 @@ def camelCase(s):
return s[0].lower() + s[1:]
class Fault(BaseException):
class Fault(Exception):
def __init__(self, message='', details='', name=''):
BaseException.__init__(self, message, details, name)
Exception.__init__(self, message, details, name)
self.message = message
self.details = details
self.name = name or camelCase(self.__class__.__name__)
......
......@@ -220,7 +220,7 @@ def update_metadata(request, image_id):
except ImageMetadata.DoesNotExist:
pass # Ignore non-existent metadata
return render_metadata(request, metadata, status=201)
return render_metadata(request, updated, status=201)
@api_method('GET')
def get_metadata_item(request, image_id, key):
......
......@@ -178,17 +178,10 @@ def create_server(request):
# so that it gets a vm.id and vm.backend_id is valid.
vm.save()
if request.META.get('SERVER_NAME', None) == 'testserver':
backend_name = 'test-server'
dry_run = True
else:
backend_name = vm.backend_id
dry_run = False
try:
jobId = rapi.CreateInstance(
mode='create',
name=backend_name,
name=vm.backend_id,
disk_template='plain',
disks=[{"size": 2000}], #FIXME: Always ask for a 2GB disk for now
nics=[{}],
......@@ -196,7 +189,7 @@ def create_server(request):
ip_check=False,
name_check=False,
pnode=rapi.GetNodes()[0], #TODO: verify if this is necessary
dry_run=dry_run,
dry_run=settings.TEST,
beparams=dict(auto_balance=True, vcpus=flavor.cpu, memory=flavor.ram))
except GanetiApiError:
vm.delete()
......@@ -370,7 +363,7 @@ def update_metadata(request, server_id):
except VirtualMachineMetadata.DoesNotExist:
pass # Ignore non-existent metadata
return render_metadata(request, metadata, status=201)
return render_metadata(request, updated, status=201)
@api_method('GET')
def get_metadata_item(request, server_id, key):
......
......@@ -2,7 +2,11 @@
# Copyright (c) 2010 Greek Research and Technology Network
#
from __future__ import with_statement
from collections import defaultdict
from email.utils import parsedate
from random import choice, randint, sample
from time import mktime
import datetime
......@@ -12,8 +16,7 @@ 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.db.models import *
from synnefo.logic import utils
......@@ -344,77 +347,380 @@ class APITestCase(TestCase):
self.assertEqual(response.status_code, 201)
class APITestCase2(TestCase):
"""An attempt for a more thorough test scenario."""
def create_users(n=1):
for i in range(n):
SynnefoUser.objects.create(
name='User %d' % i,
credit=0)
def create_flavors(n=1):
for i in range(n):
Flavor.objects.create(
cpu=randint(1, 4),
ram=randint(1, 8) * 512,
disk=randint(1, 40))
def create_images(n=1):
users = SynnefoUser.objects.all()
for i in range(n):
Image.objects.create(
name='Image %d' % (i + 1),
state='ACTIVE',
owner=choice(users))
def create_image_metadata(n=1):
images = Image.objects.all()
for i in range(n):
ImageMetadata.objects.create(
meta_key='Key%d' % (i + 1),
meta_value='Value %d' % (i + 1),
image = choice(images))
def create_servers(n=1):
users = SynnefoUser.objects.all()
flavors = Flavor.objects.all()
images = Image.objects.all()
for i in range(n):
VirtualMachine.objects.create(
name='Server %d' % (i + 1),
owner=choice(users),
sourceimage=choice(images),
hostid=str(i),
ipfour='0.0.0.0',
ipsix='::1',
flavor=choice(flavors))
def create_server_metadata(n=1):
servers = VirtualMachine.objects.all()
for i in range(n):
VirtualMachineMetadata.objects.create(
meta_key='Key%d' % (i + 1),
meta_value='Value %d' % (i + 1),
vm = choice(servers))
class AssertInvariant(object):
def __init__(self, callable, *args, **kwargs):
self.callable = callable
self.args = args
self.kwargs = kwargs
def __enter__(self):
self.value = self.callable(*self.args, **self.kwargs)
return self.value
fixtures = [ 'api_test_data2' ]
def __exit__(self, type, value, tb):
assert self.value == self.callable(*self.args, **self.kwargs)
class BaseTestCase(TestCase):
USERS = 1
FLAVORS = 1
IMAGES = 1
SERVERS = 1
SERVER_METADATA = 0
IMAGE_METADATA = 0
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/v1.1/servers'
response = self.client.post(url, content_type='application/json', data=data)
verify_response(response, name)
name, data = new_server()
url = '/api/v1.1/servers.json'
response = self.client.post(url, content_type='application/json', data=data)
verify_response(response, name)
name, data = new_server()
url = '/api/v1.1/servers.json'
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/v1.1/servers'
response = self.client.post(url, content_type='application/json', data=data)
verify_error(response, 404, 'itemNotFound')
name, data = new_server(flavorRef=0)
url = '/api/v1.1/servers'
response = self.client.post(url, content_type='application/json', data=data)
verify_error(response, 404, 'itemNotFound')
url = '/api/v1.1/servers'
response = self.client.post(url, content_type='application/json', data='INVALID')
verify_error(response, 400, 'badRequest')
create_users(self.USERS)
create_flavors(self.FLAVORS)
create_images(self.IMAGES)
create_image_metadata(self.IMAGE_METADATA)
create_servers(self.SERVERS)
create_server_metadata(self.SERVER_METADATA)
def assertFault(self, response, status_code, name):
self.assertEqual(response.status_code, status_code)
fault = json.loads(response.content)
self.assertEqual(fault.keys(), [name])
def assertBadRequest(self, response):
self.assertFault(response, 400, 'badRequest')
def assertItemNotFound(self, response):
self.assertFault(response, 404, 'itemNotFound')
def list_metadata(self, path):
response = self.client.get(path)
self.assertTrue(response.status_code in (200, 203))
reply = json.loads(response.content)
self.assertEqual(reply.keys(), ['metadata'])
self.assertEqual(reply['metadata'].keys(), ['values'])
return reply['metadata']['values']
def list_server_metadata(self, server_id):
path = '/api/v1.1/servers/%d/meta' % server_id
return self.list_metadata(path)
def list_image_metadata(self, image_id):
path = '/api/v1.1/images/%d/meta' % image_id
return self.list_metadata(path)
def update_metadata(self, path, metadata):
data = json.dumps({'metadata': metadata})
response = self.client.post(path, data, content_type='application/json')
self.assertEqual(response.status_code, 201)
reply = json.loads(response.content)
self.assertEqual(reply.keys(), ['metadata'])
return reply['metadata']
def update_server_metadata(self, server_id, metadata):
path = '/api/v1.1/servers/%d/meta' % server_id
return self.update_metadata(path, metadata)
def update_image_metadata(self, image_id, metadata):
path = '/api/v1.1/images/%d/meta' % image_id
return self.update_metadata(path, metadata)
def create_server_meta(self, server_id, meta):
key = meta.keys()[0]
path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
data = json.dumps({'meta': meta})
response = self.client.put(path, data, content_type='application/json')
self.assertEqual(response.status_code, 201)
reply = json.loads(response.content)
self.assertEqual(reply.keys(), ['meta'])
response_meta = reply['meta']
self.assertEqual(response_meta, meta)
def get_all_server_metadata(self):
metadata = defaultdict(dict)
for m in VirtualMachineMetadata.objects.all():
metadata[m.vm.id][m.meta_key] = m.meta_value
return metadata
def get_all_image_metadata(self):
metadata = defaultdict(dict)
for m in ImageMetadata.objects.all():
metadata[m.image.id][m.meta_key] = m.meta_value
return metadata
class ListServerMetadata(BaseTestCase):
SERVERS = 5
SERVER_METADATA = 100
def test_list_metadata(self):
with AssertInvariant(self.get_all_server_metadata) as metadata:
for vm in VirtualMachine.objects.all():
response_metadata = self.list_server_metadata(vm.id)
self.assertEqual(response_metadata, metadata[vm.id])
def test_invalid_server(self):
with AssertInvariant(self.get_all_server_metadata):
response = self.client.get('/api/v1.1/servers/0/meta')
self.assertItemNotFound(response)
class UpdateServerMetadata(BaseTestCase):
SERVER_METADATA = 10
def test_update_metadata(self):
metadata = self.get_all_server_metadata()
server_id = choice(metadata.keys())
new_metadata = {}
for key in sample(metadata[server_id].keys(), 3):
new_metadata[key] = 'New %s value' % key
response_metadata = self.update_server_metadata(server_id, new_metadata)
self.assertEqual(response_metadata, new_metadata)
metadata[server_id].update(new_metadata)
self.assertEqual(metadata, self.get_all_server_metadata())
def test_does_not_create(self):
with AssertInvariant(self.get_all_server_metadata) as metadata:
server_id = choice(metadata.keys())
new_metadata = {'Foo': 'Bar'}
response_metadata = self.update_server_metadata(server_id, new_metadata)
self.assertEqual(response_metadata, {})
def test_invalid_data(self):
with AssertInvariant(self.get_all_server_metadata) as metadata:
server_id = choice(metadata.keys())
path = '/api/v1.1/servers/%d/meta' % server_id
response = self.client.post(path, 'metadata', content_type='application/json')
self.assertBadRequest(response)
def test_invalid_server(self):
with AssertInvariant(self.get_all_server_metadata):
path = '/api/v1.1/servers/0/meta'
data = json.dumps({'metadata': {'Key1': 'A Value'}})
response = self.client.post(path, data, content_type='application/json')
self.assertItemNotFound(response)
class GetServerMetadataItem(BaseTestCase):
SERVERS = 5
SERVER_METADATA = 100
def test_get_metadata_item(self):
with AssertInvariant(self.get_all_server_metadata) as metadata:
server_id = choice(metadata.keys())
key = choice(metadata[server_id].keys())
path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
response = self.client.get(path)
self.assertTrue(response.status_code in (200, 203))
reply = json.loads(response.content)
self.assertEqual(reply['meta'], {key: metadata[server_id][key]})
def test_invalid_key(self):
with AssertInvariant(self.get_all_server_metadata) as metadata:
server_id = choice(metadata.keys())
response = self.client.get('/api/v1.1/servers/%d/meta/foo' % server_id)
self.assertItemNotFound(response)
def test_invalid_server(self):
with AssertInvariant(self.get_all_server_metadata):
response = self.client.get('/api/v1.1/servers/0/meta/foo')
self.assertItemNotFound(response)
class CreateServerMetadataItem(BaseTestCase):
SERVER_METADATA = 10
def test_create_metadata(self):
metadata = self.get_all_server_metadata()
server_id = choice(metadata.keys())
meta = {'Foo': 'Bar'}
self.create_server_meta(server_id, meta)
metadata[server_id].update(meta)
self.assertEqual(metadata, self.get_all_server_metadata())
def test_update_metadata(self):
metadata = self.get_all_server_metadata()
server_id = choice(metadata.keys())
key = choice(metadata[server_id].keys())
meta = {key: 'New Value'}
self.create_server_meta(server_id, meta)
metadata[server_id].update(meta)
self.assertEqual(metadata, self.get_all_server_metadata())
def test_invalid_server(self):
with AssertInvariant(self.get_all_server_metadata):
path = '/api/v1.1/servers/0/meta/foo'
data = json.dumps({'meta': {'foo': 'bar'}})
response = self.client.put(path, data, content_type='application/json')
self.assertItemNotFound(response)
def test_invalid_key(self):
with AssertInvariant(self.get_all_server_metadata) as metadata:
server_id = choice(metadata.keys())
path = '/api/v1.1/servers/%d/meta/baz' % server_id
data = json.dumps({'meta': {'foo': 'bar'}})
response = self.client.put(path, data, content_type='application/json')
self.assertBadRequest(response)
def test_invalid_data(self):
with AssertInvariant(self.get_all_server_metadata) as metadata:
server_id = choice(metadata.keys())
path = '/api/v1.1/servers/%d/meta/foo' % server_id
response = self.client.put(path, 'meta', content_type='application/json')
self.assertBadRequest(response)
class DeleteServerMetadataItem(BaseTestCase):
SERVER_METADATA = 10
def test_delete_metadata(self):
metadata = self.get_all_server_metadata()
server_id = choice(metadata.keys())
key = choice(metadata[server_id].keys())
path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
response = self.client.delete(path)
self.assertEqual(response.status_code, 204)
metadata[server_id].pop(key)
self.assertEqual(metadata, self.get_all_server_metadata())
def test_invalid_server(self):
with AssertInvariant(self.get_all_server_metadata):
response = self.client.delete('/api/v1.1/servers/9/meta/Key1')
self.assertItemNotFound(response)
def test_invalid_key(self):
with AssertInvariant(self.get_all_server_metadata) as metadata:
server_id = choice(metadata.keys())
path = '/api/v1.1/servers/%d/meta/foo' % server_id
response = self.client.delete(path)
self.assertItemNotFound(response)
class ListImageMetadata(BaseTestCase):
IMAGES = 5
IMAGE_METADATA = 100
def test_list_metadata(self):
with AssertInvariant(self.get_all_image_metadata) as metadata:
for image in Image.objects.all():
response_metadata = self.list_image_metadata(image.id)
self.assertEqual(response_metadata, metadata[image.id])
def test_invalid_image(self):
with AssertInvariant(self.get_all_image_metadata):
response = self.client.get('/api/v1.1/images/0/meta')
self.assertItemNotFound(response)
class UpdateImageMetadata(BaseTestCase):
IMAGE_METADATA = 10
def test_update_metadata(self):
metadata = self.get_all_image_metadata()
image_id = choice(metadata.keys())
new_metadata = {}
for key in sample(metadata[image_id].keys(), 3):
new_metadata[key] = 'New %s value' % key
response_metadata = self.update_image_metadata(image_id, new_metadata)
self.assertEqual(response_metadata, new_metadata)
metadata[image_id].update(new_metadata)
self.assertEqual(metadata, self.get_all_image_metadata())
def test_does_not_create(self):
with AssertInvariant(self.get_all_image_metadata) as metadata:
image_id = choice(metadata.keys())
new_metadata = {'Foo': 'Bar'}
response_metadata = self.update_image_metadata(image_id, new_metadata)
self.assertEqual(response_metadata, {})
def test_invalid_data(self):
with AssertInvariant(self.get_all_image_metadata) as metadata:
image_id = choice(metadata.keys())
path = '/api/v1.1/images/%d/meta' % image_id
response = self.client.post(path, 'metadata', content_type='application/json')
self.assertBadRequest(response)
def test_invalid_server(self):
with AssertInvariant(self.get_all_image_metadata):
path = '/api/v1.1/images/0/meta'
data = json.dumps({'metadata': {'Key1': 'A Value'}})
response = self.client.post(path, data, content_type='application/json')
self.assertItemNotFound(response)