Commit 804347ee authored by Giorgos Verigakis's avatar Giorgos Verigakis
Browse files

Work in progress commit towards Astakos integration

* Added middleware to authenticate with Astakos
* Removed more legacy code
* Deleted Image and ImageMetadata models

Tests and admin still pending.
parent 371dc030
......@@ -242,17 +242,6 @@
"disk": 80
}
},
{
"model": "db.Image",
"pk": 1,
"fields": {
"name": "Debian Squeeze",
"userid": "test",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE"
}
},
{
"model": "db.VirtualMachine",
"pk": 1001,
......@@ -261,7 +250,6 @@
"name": "snf-1001",
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"charged": "2011-02-06 00:00:00",
"imageid": "1",
"hostid": "HAL-9000",
"flavor": 1,
......@@ -276,7 +264,6 @@
"name": "snf-1002",
"created": "2011-02-10 00:00:00",
"updated": "2011-02-10 00:00:00",
"charged": "2011-02-10 00:00:00",
"imageid": "1",
"hostid": "HAL-9000",
"flavor": 1,
......@@ -291,7 +278,6 @@
"name": "snf-1003",
"created": "2011-02-10 00:00:00",
"updated": "2011-02-10 00:00:00",
"charged": "2011-02-10 00:00:00",
"imageid": "1",
"hostid": "HAL-9000",
"flavor": 1,
......@@ -306,23 +292,10 @@
"name": "snf-1004",
"created": "2011-02-10 00:00:00",
"updated": "2011-02-10 00:00:00",
"charged": "2011-02-10 00:00:00",
"imageid": "1",
"hostid": "HAL-9000",
"flavor": 1,
"operstate": "STARTED"
}
},
{
"model": "db.Image",
"pk": 2,
"fields": {
"name": "Slackware 13.1",
"created": "2011-02-10 00:00:00",
"updated": "2011-02-10 00:00:00",
"state": "ACTIVE",
"userid" : "test",
"sourcevm": 1001
}
}
]
# Copyright 2011 GRNET S.A. All rights reserved.
# Copyright 2011-2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
......@@ -31,16 +31,18 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import dateutil.parser
from django.conf.urls.defaults import patterns
from django.db.models import Q
from django.http import HttpResponse
from django.template.loader import render_to_string
from django.utils import simplejson as json
from synnefo.api import util
from synnefo.api.common import method_not_allowed
from synnefo.api.faults import BadRequest
from synnefo.db.models import Image, ImageMetadata
from synnefo.api.faults import BadRequest, ItemNotFound, ServiceUnavailable
from synnefo.api.util import api_method, isoformat, isoparse
from synnefo.plankton.backend import ImageBackend
from synnefo.util.log import getLogger
......@@ -49,9 +51,9 @@ log = getLogger('synnefo.api')
urlpatterns = patterns('synnefo.api.images',
(r'^(?:/|.json|.xml)?$', 'demux'),
(r'^/detail(?:.json|.xml)?$', 'list_images', {'detail': True}),
(r'^/(\d+)(?:.json|.xml)?$', 'image_demux'),
(r'^/(\d+)/meta(?:.json|.xml)?$', 'metadata_demux'),
(r'^/(\d+)/meta/(.+?)(?:.json|.xml)?$', 'metadata_item_demux'),
(r'^/([\w-]+)(?:.json|.xml)?$', 'image_demux'),
(r'^/([\w-]+)/meta(?:.json|.xml)?$', 'metadata_demux'),
(r'^/([\w-]+)/meta/(.+?)(?:.json|.xml)?$', 'metadata_item_demux')
)
def demux(request):
......@@ -90,26 +92,18 @@ def metadata_item_demux(request, image_id, key):
def image_to_dict(image, detail=True):
d = {'id': image.id, 'name': image.name}
d = dict(id=image['id'], name=image['name'])
if detail:
d['updated'] = util.isoformat(image.updated)
d['created'] = util.isoformat(image.created)
d['status'] = image.state
d['progress'] = 100 if image.state == 'ACTIVE' else 0
if image.sourcevm:
d['serverRef'] = image.sourcevm.id
metadata = {}
for meta in ImageMetadata.objects.filter(image=image):
metadata[meta.meta_key] = meta.meta_value
if metadata:
d['metadata'] = {'values': metadata}
d['updated'] = isoformat(dateutil.parser.parse(image['updated_at']))
d['created'] = isoformat(dateutil.parser.parse(image['created_at']))
d['status'] = 'DELETED' if image['deleted_at'] else 'ACTIVE'
d['progress'] = 100 if image['status'] == 'available' else 0
if image['properties']:
d['metadata'] = {'values': image['properties']}
return d
@util.api_method('GET')
@api_method('GET')
def list_images(request, detail=False):
# Normal Response Codes: 200, 203
# Error Response Codes: computeFault (400, 500),
......@@ -119,28 +113,32 @@ def list_images(request, detail=False):
# overLimit (413)
log.debug('list_images detail=%s', detail)
user_images = Image.objects.filter(Q(owner=request.user) | Q(public=True))
since = util.isoparse(request.GET.get('changes-since'))
backend = ImageBackend(request.user)
since = isoparse(request.GET.get('changes-since'))
if since:
user_images = user_images.filter(updated__gte=since)
if not user_images:
images = []
for image in backend.iter():
updated = dateutil.parser.parse(image['updated_at'])
if updated >= since:
images.append(image)
if not images:
return HttpResponse(status=304)
else:
user_images = user_images.exclude(state='DELETED')
images = backend.list()
images = [image_to_dict(image, detail) for image in user_images]
reply = [image_to_dict(image, detail) for image in images]
if request.serialization == 'xml':
data = render_to_string('list_images.xml', {
'images': images,
'detail': detail})
data = render_to_string('list_images.xml',
dict(images=reply, detail=detail))
else:
data = json.dumps({'images': {'values': images}})
data = json.dumps(dict(images={'values': reply}))
return HttpResponse(data, status=200)
@util.api_method('POST')
@api_method('POST')
def create_image(request):
# Normal Response Code: 202
# Error Response Codes: computeFault (400, 500),
......@@ -154,31 +152,11 @@ def create_image(request):
# resizeNotAllowed (403),
# backupOrResizeInProgress (409),
# overLimit (413)
req = util.get_request_dict(request)
log.debug('create_image %s', req)
try:
d = req['image']
server_id = d['serverRef']
name = d['name']
except (KeyError, ValueError):
raise BadRequest('Malformed request.')
owner = request.user
vm = util.get_vm(server_id, owner)
image = Image.objects.create(name=name, owner=owner, sourcevm=vm)
log.info('User %d created image %d', owner.id, image.id)
raise ServiceUnavailable('Not supported.')
imagedict = image_to_dict(image)
if request.serialization == 'xml':
data = render_to_string('image.xml', {'image': imagedict})
else:
data = json.dumps({'image': imagedict})
return HttpResponse(data, status=202)
@util.api_method('GET')
@api_method('GET')
def get_image_details(request, image_id):
# Normal Response Codes: 200, 203
# Error Response Codes: computeFault (400, 500),
......@@ -190,16 +168,17 @@ def get_image_details(request, image_id):
log.debug('get_image_details %s', image_id)
image = util.get_image(image_id, request.user)
imagedict = image_to_dict(image)
reply = image_to_dict(image)
if request.serialization == 'xml':
data = render_to_string('image.xml', {'image': imagedict})
data = render_to_string('image.xml', dict(image=reply))
else:
data = json.dumps({'image': imagedict})
data = json.dumps(dict(image=reply))
return HttpResponse(data, status=200)
@util.api_method('DELETE')
@api_method('DELETE')
def delete_image(request, image_id):
# Normal Response Code: 204
# Error Response Codes: computeFault (400, 500),
......@@ -209,13 +188,14 @@ def delete_image(request, image_id):
# overLimit (413)
log.debug('delete_image %s', image_id)
image = util.get_image(image_id, request.user)
image.state = 'DELETED'
image.save()
log.info('User %d deleted image %d', request.user.id, image.id)
backend = ImageBackend(request.user)
backend.delete(image_id)
backend.close()
log.info('User %s deleted image %s', request.user, image_id)
return HttpResponse(status=204)
@util.api_method('GET')
@api_method('GET')
def list_metadata(request, image_id):
# Normal Response Codes: 200, 203
# Error Response Codes: computeFault (400, 500),
......@@ -226,10 +206,11 @@ def list_metadata(request, image_id):
log.debug('list_image_metadata %s', image_id)
image = util.get_image(image_id, request.user)
metadata = dict((m.meta_key, m.meta_value) for m in image.metadata.all())
metadata = image['properties']
return util.render_metadata(request, metadata, use_values=True, status=200)
@util.api_method('POST')
@api_method('POST')
def update_metadata(request, image_id):
# Normal Response Code: 201
# Error Response Codes: computeFault (400, 500),
......@@ -249,16 +230,17 @@ def update_metadata(request, image_id):
except (KeyError, AssertionError):
raise BadRequest('Malformed request.')
for key, val in metadata.items():
meta, created = image.metadata.get_or_create(meta_key=key)
meta.meta_value = val
meta.save()
properties = image['properties']
properties.update(metadata)
backend = ImageBackend(request.user)
backend.update(image_id, dict(properties=properties))
backend.close()
image.save()
image_meta = dict((m.meta_key, m.meta_value) for m in image.metadata.all())
return util.render_metadata(request, image_meta, status=201)
return util.render_metadata(request, properties, status=201)
@util.api_method('GET')
@api_method('GET')
def get_metadata_item(request, image_id, key):
# Normal Response Codes: 200, 203
# Error Response Codes: computeFault (400, 500),
......@@ -270,10 +252,13 @@ def get_metadata_item(request, image_id, key):
log.debug('get_image_metadata_item %s %s', image_id, key)
image = util.get_image(image_id, request.user)
meta = util.get_image_meta(image, key)
return util.render_meta(request, meta, status=200)
val = image['properties'].get(key)
if val is None:
raise ItemNotFound('Metadata key not found.')
return util.render_meta(request, {key: val}, status=200)
@util.api_method('PUT')
@api_method('PUT')
def create_metadata_item(request, image_id, key):
# Normal Response Code: 201
# Error Response Codes: computeFault (400, 500),
......@@ -287,7 +272,6 @@ def create_metadata_item(request, image_id, key):
req = util.get_request_dict(request)
log.debug('create_image_metadata_item %s %s %s', image_id, key, req)
image = util.get_image(image_id, request.user)
try:
metadict = req['meta']
assert isinstance(metadict, dict)
......@@ -295,17 +279,20 @@ def create_metadata_item(request, image_id, key):
assert key in metadict
except (KeyError, AssertionError):
raise BadRequest('Malformed request.')
val = metadict[key]
image = util.get_image(image_id, request.user)
properties = image['properties']
properties[key] = val
meta, created = ImageMetadata.objects.get_or_create(
meta_key=key,
image=image)
backend = ImageBackend(request.user)
backend.update(image_id, dict(properties=properties))
backend.close()
meta.meta_value = metadict[key]
meta.save()
image.save()
return util.render_meta(request, meta, status=201)
return util.render_meta(request, {key: val}, status=201)
@util.api_method('DELETE')
@api_method('DELETE')
def delete_metadata_item(request, image_id, key):
# Normal Response Code: 204
# Error Response Codes: computeFault (400, 500),
......@@ -319,7 +306,11 @@ def delete_metadata_item(request, image_id, key):
log.debug('delete_image_metadata_item %s %s', image_id, key)
image = util.get_image(image_id, request.user)
meta = util.get_image_meta(image, key)
meta.delete()
image.save()
properties = image['properties']
properties.pop(key, None)
backend = ImageBackend(request.user)
backend.update(image_id, dict(properties=properties))
backend.close()
return HttpResponse(status=204)
......@@ -31,13 +31,50 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import json
from httplib import HTTPConnection, HTTPSConnection
from urlparse import urlparse
from django.conf import settings
from django.utils.cache import patch_vary_headers
class ApiAuthMiddleware(object):
def process_request(self, request):
request.user = None
token = request.GET.get('X-Auth-Token')
if not token:
token = request.META.get('HTTP_X_AUTH_TOKEN')
if not token:
token = request.COOKIES.get('X-Auth-Token')
if not token:
return
p = urlparse(settings.ASTAKOS_URL)
if p.scheme == 'https':
conn = HTTPSConnection(p.netloc)
else:
conn = HTTPConnection(p.netloc)
headers = {'X-Auth-Token': token}
conn.request('GET', p.path, headers=headers)
resp = conn.getresponse()
if resp.status != 200:
return
try:
reply = json.loads(resp.read())
assert 'uniq' in reply
assert 'username' in reply
except (ValueError, AssertionError):
return
request.user = reply['uniq']
request.username = reply['username']
def process_response(self, request, response):
# Tell proxies and other interested parties that the request varies
# based on X-Auth-Token, to avoid caching of results
......
# Copyright 2011 GRNET S.A. All rights reserved.
# Copyright 2011-2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
......@@ -65,6 +65,7 @@ def demux(request):
else:
return method_not_allowed(request)
def network_demux(request, network_id):
if request.method == 'GET':
return get_network_details(request, network_id)
......@@ -76,17 +77,18 @@ def network_demux(request, network_id):
return method_not_allowed(request)
def network_to_dict(network, user, detail=True):
def network_to_dict(network, user_id, detail=True):
network_id = str(network.id) if not network.public else 'public'
d = {'id': network_id, 'name': network.name}
if detail:
d['updated'] = util.isoformat(network.updated)
d['created'] = util.isoformat(network.created)
d['status'] = network.state
servers = [vm.id for vm in network.machines.filter(owner=user)]
servers = [vm.id for vm in network.machines.filter(userid=user_id)]
d['servers'] = {'values': servers}
return d
def render_network(request, networkdict, status=200):
if request.serialization == 'xml':
data = render_to_string('network.xml', {'network': networkdict})
......@@ -105,9 +107,9 @@ def list_networks(request, detail=False):
# overLimit (413)
log.debug('list_networks detail=%s', detail)
owner = request.user
since = util.isoparse(request.GET.get('changes-since'))
user_networks = Network.objects.filter(Q(owner=owner) | Q(public=True))
user_networks = Network.objects.filter(
Q(userid=request.user) | Q(public=True))
if since:
user_networks = user_networks.filter(updated__gte=since)
......@@ -116,7 +118,7 @@ def list_networks(request, detail=False):
else:
user_networks = user_networks.filter(state='ACTIVE')
networks = [network_to_dict(network, owner, detail)
networks = [network_to_dict(network, request.user, detail)
for network in user_networks]
if request.serialization == 'xml':
......@@ -128,6 +130,7 @@ def list_networks(request, detail=False):
return HttpResponse(data, status=200)
@util.api_method('POST')
def create_network(request):
# Normal Response Code: 202
......@@ -154,6 +157,7 @@ def create_network(request):
networkdict = network_to_dict(network, request.user)
return render_network(request, networkdict, status=202)
@util.api_method('GET')
def get_network_details(request, network_id):
# Normal Response Codes: 200, 203
......@@ -169,6 +173,7 @@ def get_network_details(request, network_id):
netdict = network_to_dict(net, request.user)
return render_network(request, netdict)
@util.api_method('PUT')
def update_network_name(request, network_id):
# Normal Response Code: 204
......@@ -195,6 +200,7 @@ def update_network_name(request, network_id):
net.save()
return HttpResponse(status=204)
@util.api_method('DELETE')
def delete_network(request, network_id):
# Normal Response Code: 204
......@@ -212,6 +218,7 @@ def delete_network(request, network_id):
backend.delete_network(net)
return HttpResponse(status=204)
@util.api_method('POST')
def network_action(request, network_id):
req = util.get_request_dict(request)
......
# Copyright 2011 GRNET S.A. All rights reserved.
# Copyright 2011-2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
......@@ -161,7 +161,7 @@ def list_servers(request, detail=False):
# overLimit (413)
log.debug('list_servers detail=%s', detail)
user_vms = VirtualMachine.objects.filter(owner=request.user)
user_vms = VirtualMachine.objects.filter(userid=request.user)
since = util.isoparse(request.GET.get('changes-since'))
if since:
......@@ -197,7 +197,6 @@ def create_server(request):
req = util.get_request_dict(request)
log.debug('create_server %s', req)
owner = request.user
try:
server = req['server']
......@@ -231,31 +230,25 @@ def create_server(request):
raise faults.BadRequest("Malformed personality in request")
image = {}
try:
img = util.get_image(image_id, owner)
image['backend_id'] = img.backend_id
image['format'] = img.format
image['metadata'] = dict((m.meta_key.upper(), m.meta_value)
for m in img.metadata.all())
except faults.ItemNotFound:
img = util.get_backend_image(image_id, owner)
properties = img.get('properties', {})
image['backend_id'] = img['location']
image['format'] = img['disk_format']
image['metadata'] = dict((key.upper(), val)
for key, val in properties.items())
img = util.get_image(image_id, request.user)
properties = img.get('properties', {})
image['backend_id'] = img['location']
image['format'] = img['disk_format']
image['metadata'] = dict((key.upper(), val) \
for key, val in properties.items())
flavor = util.get_flavor(flavor_id)
password = util.random_password()
count = VirtualMachine.objects.filter(owner=owner, deleted=False).count()
count = VirtualMachine.objects.filter(userid=request.user,
deleted=False).count()
if count >= settings.MAX_VMS_PER_USER:
raise faults.OverLimit("Server count limit exceeded for your account.")
# We must save the VM instance now, so that it gets a valid vm.backend_id.
vm = VirtualMachine.objects.create(
name=name,
owner=owner,
userid=request.user,
imageid=image_id,
flavor=flavor)
......@@ -271,8 +264,8 @@ def create_server(request):
meta_value=val,
vm=vm)
log.info('User %d created vm with %s cpus, %s ram and %s storage',
owner.id, flavor.cpu, flavor.ram, flavor.disk)
log.info('User %s created vm with %s cpus, %s ram and %s storage',
request.user, flavor.cpu, flavor.ram, flavor.disk)
server = vm_to_dict(vm, detail=True)
server['status'] = 'BUILD'
......@@ -392,9 +385,8 @@ def list_addresses_by_network(request, server_id, network_id):
# overLimit (413)
log.debug('list_addresses_by_network %s %s', server_id, network_id)
owner = request.user
machine = util.get_vm(server_id, owner)
network = util.get_network(network_id, owner)
machine = util.get_vm(server_id, request.user)
network = util.get_network(network_id, request.user)
nic = util.get_nic(machine, network)
address = nic_to_dict(nic)
......
<?xml version="1.0" encoding="UTF-8"?>
<meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="{{ meta.meta_key }}">{{ meta.meta_value }}</meta>
<meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="{{ key }}">{{ val }}</meta>
# Copyright 2011 GRNET S.A. All rights reserved.
# Copyright 2011-2012 GRNET S.A. All rights reserved.