Commit 9359b523 authored by Giorgos Verigakis's avatar Giorgos Verigakis
Browse files

Add metadata support in Images

Removed description and size from Image model.
parent a2b94bb1
......@@ -3,7 +3,7 @@
#
from synnefo.api.common import method_not_allowed
from synnefo.api.util import isoformat, isoparse, get_user, get_image, get_request_dict, api_method
from synnefo.api.util import *
from synnefo.db.models import Image, ImageMetadata, VirtualMachine
from django.conf.urls.defaults import patterns
......@@ -16,6 +16,8 @@ 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'),
)
def demux(request):
......@@ -34,6 +36,24 @@ def image_demux(request, image_id):
else:
return method_not_allowed(request)
def metadata_demux(request, image_id):
if request.method == 'GET':
return list_metadata(request, image_id)
elif request.method == 'POST':
return update_metadata(request, image_id)
else:
return method_not_allowed(request)
def metadata_item_demux(request, image_id, key):
if request.method == 'GET':
return get_metadata_item(request, image_id, key)
elif request.method == 'PUT':
return create_metadata_item(request, image_id, key)
elif request.method == 'DELETE':
return delete_metadata_item(request, image_id, key)
else:
return method_not_allowed(request)
def image_to_dict(image, detail=True):
d = {'id': image.id, 'name': image.name}
......@@ -55,6 +75,10 @@ def image_to_dict(image, detail=True):
return d
def metadata_to_dict(image):
image_meta = image.imagemetadata_set.all()
return dict((meta.meta_key, meta.meta_value) for meta in image_meta)
@api_method('GET')
def list_images(request, detail=False):
......@@ -99,20 +123,18 @@ def create_image(request):
# overLimit (413)
req = get_request_dict(request)
owner = get_user()
try:
d = req['image']
server_id = int(d['serverRef'])
vm = VirtualMachine.objects.get(id=server_id)
image = Image.objects.create(name=d['name'], size=0, owner=owner, sourcevm=vm)
image.save()
except KeyError:
raise BadRequest
except ValueError:
raise BadRequest
except VirtualMachine.DoesNotExist:
raise ItemNotFound
server_id = d['serverRef']
name = d['name']
except (KeyError, ValueError):
raise BadRequest('Malformed request.')
owner = get_user()
vm = get_vm(server_id)
image = Image.objects.create(name=name, size=0, owner=owner, sourcevm=vm)
image.save()
imagedict = image_to_dict(image)
if request.serialization == 'xml':
......@@ -156,3 +178,104 @@ def delete_image(request, image_id):
raise Unauthorized()
image.delete()
return HttpResponse(status=204)
@api_method('GET')
def list_metadata(request, image_id):
# Normal Response Codes: 200, 203
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# overLimit (413)
image = get_image(image_id)
metadata = metadata_to_dict(image)
return render_metadata(request, metadata, use_values=True, status=200)
@api_method('POST')
def update_metadata(request, image_id):
# Normal Response Code: 201
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# buildInProgress (409),
# badMediaType(415),
# overLimit (413)
image = get_image(image_id)
req = get_request_dict(request)
try:
metadata = req['metadata']
assert isinstance(metadata, dict)
except (KeyError, AssertionError):
raise BadRequest('Malformed request.')
updated = {}
for key, val in metadata.items():
try:
meta = ImageMetadata.objects.get(meta_key=key, image=image)
meta.meta_value = val
meta.save()
updated[key] = val
except ImageMetadata.DoesNotExist:
pass # Ignore non-existent metadata
return render_metadata(request, metadata, status=201)
@api_method('GET')
def get_metadata_item(request, image_id, key):
# Normal Response Codes: 200, 203
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# itemNotFound (404),
# badRequest (400),
# overLimit (413)
meta = get_image_meta(image_id, key)
return render_meta(request, meta, status=200)
@api_method('PUT')
def create_metadata_item(request, image_id, key):
# Normal Response Code: 201
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# itemNotFound (404),
# badRequest (400),
# buildInProgress (409),
# badMediaType(415),
# overLimit (413)
image = get_image(image_id)
req = get_request_dict(request)
try:
metadict = req['meta']
assert isinstance(metadict, dict)
assert len(metadict) == 1
assert key in metadict
except (KeyError, AssertionError):
raise BadRequest('Malformed request.')
meta, created = ImageMetadata.objects.get_or_create(meta_key=key, image=image)
meta.meta_value = metadict[key]
meta.save()
return render_meta(request, meta, status=201)
@api_method('DELETE')
def delete_metadata_item(request, image_id, key):
# Normal Response Code: 204
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# itemNotFound (404),
# badRequest (400),
# buildInProgress (409),
# badMediaType(415),
# overLimit (413),
meta = get_image_meta(image_id, key)
meta.delete()
return HttpResponse(status=204)
......@@ -323,14 +323,7 @@ def list_metadata(request, server_id):
vm = get_vm(server_id)
metadata = metadata_to_dict(vm)
if request.serialization == 'xml':
data = render_to_string('metadata.xml', {'metadata': metadata})
else:
data = json.dumps({'metadata': {'values': metadata}})
return HttpResponse(data, status=200)
return render_metadata(request, metadata, use_values=True, status=200)
@api_method('POST')
def update_metadata(request, server_id):
......@@ -362,11 +355,7 @@ def update_metadata(request, server_id):
except VirtualMachineMetadata.DoesNotExist:
pass # Ignore non-existent metadata
if request.serialization == 'xml':
data = render_to_string('servers/metadata.xml', {'metadata': updated})
else:
data = json.dumps({'metadata': updated})
return HttpResponse(data, status=201)
return render_metadata(request, metadata, status=201)
@api_method('GET')
def get_metadata_item(request, server_id, key):
......@@ -379,11 +368,7 @@ def get_metadata_item(request, server_id, key):
# overLimit (413)
meta = get_vm_meta(server_id, key)
if request.serialization == 'xml':
data = render_to_string('meta.xml', {'meta': meta})
else:
data = json.dumps({'meta': {key: meta.meta_value}})
return HttpResponse(data, status=200)
return render_meta(request, meta, status=200)
@api_method('PUT')
def create_metadata_item(request, server_id, key):
......@@ -410,12 +395,7 @@ def create_metadata_item(request, server_id, key):
meta, created = VirtualMachineMetadata.objects.get_or_create(meta_key=key, vm=vm)
meta.meta_value = metadict[key]
meta.save()
if request.serialization == 'xml':
data = render_to_string('servers/meta.xml', {'meta': meta})
else:
data = json.dumps({'meta': {key: meta.meta_value}})
return HttpResponse(data, status=201)
return render_meta(request, meta, status=201)
@api_method('DELETE')
def delete_metadata_item(request, server_id, key):
......
......@@ -13,8 +13,8 @@ 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 *
from synnefo.db.models import *
from synnefo.api.faults import Fault, BadRequest, ItemNotFound, ServiceUnavailable
from synnefo.db.models import SynnefoUser, Image, ImageMetadata, VirtualMachine, VirtualMachineMetadata
import datetime
import dateutil.parser
......@@ -98,6 +98,15 @@ def get_image(image_id):
except Image.DoesNotExist:
raise ItemNotFound('Image not found.')
def get_image_meta(image_id, key):
"""Return a ImageMetadata instance or raise ItemNotFound."""
try:
image_id = int(image_id)
return ImageMetadata.objects.get(meta_key=key, image=image_id)
except ImageMetadata.DoesNotExist:
raise ItemNotFound('Metadata key not found.')
def get_request_dict(request):
"""Returns data sent by the client as a python dict."""
......@@ -111,6 +120,22 @@ def get_request_dict(request):
else:
raise BadRequest('Unsupported Content-Type.')
def render_metadata(request, metadata, use_values=False, status=200):
if request.serialization == 'xml':
data = render_to_string('metadata.xml', {'metadata': metadata})
else:
d = {'metadata': {'values': metadata}} if use_values else {'metadata': metadata}
data = json.dumps(d)
return HttpResponse(data, status=status)
def render_meta(request, meta, status=200):
if request.serialization == 'xml':
data = render_to_string('meta.xml', {'meta': meta})
else:
data = json.dumps({'meta': {meta.meta_key: meta.meta_value}})
return HttpResponse(data, status=status)
def render_fault(request, fault):
if settings.DEBUG or request.META.get('SERVER_NAME') == 'testserver':
fault.details = format_exc(fault)
......@@ -132,6 +157,7 @@ def render_fault(request, fault):
return resp
def request_serialization(request, atom_allowed=False):
"""Return the serialization format requested.
......
......@@ -7,8 +7,6 @@
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE",
"description": "Debian Sid, full installation",
"size": 4096
}
},
{
......@@ -19,8 +17,6 @@
"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
}
},
{
......@@ -31,8 +27,6 @@
"created": "2011-02-06 00:00:00",
"updated": "2011-02-06 00:00:00",
"state": "ACTIVE",
"description": "Ubuntu 10.10, full installation",
"size": 8192
}
},
{
......
......@@ -54,8 +54,6 @@ class Image(models.Model):
name = models.CharField('Image name', max_length=255)
state = models.CharField('Current Image State', choices=IMAGE_STATES, max_length=30)
description = models.TextField('General description')
size = models.PositiveIntegerField('Image size in MBs')
owner = models.ForeignKey(SynnefoUser, blank=True, null=True)
created = models.DateTimeField('Time of creation', auto_now_add=True)
updated = models.DateTimeField('Time of last update', auto_now=True)
......
......@@ -406,6 +406,54 @@ class DeleteServerMeta(Command):
path = '/api/%s/servers/%d/meta/%s' % (self.api, int(server_id), key)
reply = self.http_delete(path)
@command_name('lsimgmeta')
class ListImageMeta(Command):
description = 'list image meta'
syntax = '<image id> [key]'
def execute(self, image_id, key=None):
path = '/api/%s/images/%d/meta' % (self.api, int(image_id))
if key:
path += '/' + key
reply = self.http_get(path)
if key:
print_dict(reply['meta'])
else:
print_dict(reply['metadata']['values'])
@command_name('setimgmeta')
class UpdateImageMeta(Command):
description = 'update image meta'
syntax = '<image id> <key> <val>'
def execute(self, image_id, key, val):
path = '/api/%s/images/%d/meta' % (self.api, int(image_id))
metadata = {key: val}
body = json.dumps({'metadata': metadata})
reply = self.http_post(path, body, expected_status=201)
print_dict(reply['metadata'])
@command_name('addimgmeta')
class CreateImageMeta(Command):
description = 'add image meta'
syntax = '<image id> <key> <val>'
def execute(self, image_id, key, val):
path = '/api/%s/images/%d/meta/%s' % (self.api, int(image_id), key)
meta = {key: val}
body = json.dumps({'meta': meta})
reply = self.http_put(path, body, expected_status=201)
print_dict(reply['meta'])
@command_name('delimgmeta')
class DeleteImageMeta(Command):
description = 'delete image meta'
syntax = '<image id> <key>'
def execute(self, image_id, key):
path = '/api/%s/images/%d/meta/%s' % (self.api, int(image_id), key)
reply = self.http_delete(path)
def main():
try:
......
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