Added volume_attachments

This commit is contained in:
Justin Santa Barbara 2011-03-24 01:03:41 -07:00
parent 7b9888df2d
commit 1894937e1e
4 changed files with 192 additions and 30 deletions

View File

@ -14,3 +14,5 @@ CA/newcerts/*.pem
CA/private/cakey.pem
nova/vcsversion.py
*.DS_Store
.project
.pydevproject

View File

@ -38,6 +38,7 @@ from nova.api.openstack import servers
from nova.api.openstack import shared_ip_groups
from nova.api.openstack import users
from nova.api.openstack import volumes
from nova.api.openstack import volume_attachments
from nova.api.openstack import zones
@ -109,6 +110,11 @@ class APIRouter(wsgi.Router):
parent_resource=dict(member_name='server',
collection_name='servers'))
mapper.resource("volume_attachment", "volume_attachment",
controller=volume_attachments.Controller(),
parent_resource=dict(member_name='server',
collection_name='servers'))
mapper.resource("console", "consoles",
controller=consoles.Controller(),
parent_resource=dict(member_name='server',

View File

@ -0,0 +1,154 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Justin Santa Barbara
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from webob import exc
from nova import compute
from nova import exception
from nova import flags
from nova import log as logging
from nova import volume
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
LOG = logging.getLogger("nova.api.volumes")
FLAGS = flags.FLAGS
def _translate_detail_view(context, volume):
""" Maps keys for details view"""
v = _translate_summary_view(context, volume)
# No additional data / lookups at the moment
return v
def _translate_summary_view(context, volume):
""" Maps keys for summary view"""
v = {}
volume_id = volume['id']
# NOTE(justinsb): We use the volume id as the id of the attachment object
v['id'] = volume_id
v['volumeId'] = volume_id
v['serverId'] = volume['instance_id']
v['device'] = volume['mountpoint']
return v
class Controller(wsgi.Controller):
""" The volume attachment API controller for the Openstack API
A child resource of the server. Note that we use the volume id
as the ID of the attachment (though this is not guaranteed externally)"""
_serialization_metadata = {
'application/xml': {
'attributes': {
'volumeAttachment': [ 'id',
'serverId',
'volumeId',
'device' ]}}}
def __init__(self):
self.compute_api = compute.API()
self.volume_api = volume.API()
super(Controller, self).__init__()
def index(self, req, server_id):
""" Returns the list of volume attachments for a given instance """
return self._items(req, server_id,
entity_maker=_translate_summary_view)
def show(self, req, id):
"""Return data about the given volume"""
context = req.environ['nova.context']
try:
vol = self.volume_api.get(context, id)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
return {'volume': _translate_detail_view(context, vol)}
def create(self, req, server_id):
""" Attach a volume to an instance """
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
if not env:
return faults.Fault(exc.HTTPUnprocessableEntity())
instance_id = server_id
volume_id = env['volumeAttachment']['volumeId']
device = env['volumeAttachment']['device']
msg = _("Attach volume %(volume_id)s to instance %(server_id)s"
" at %(device)s") % locals()
LOG.audit(msg, context=context)
self.compute_api.attach_volume(context,
instance_id=instance_id,
volume_id=volume_id,
device=device)
vol = self.volume_api.get(context, volume_id)
retval = _translate_detail_view(context, vol)
return {'volumeAttachment': retval}
def update(self, _req, _server_id, _id):
""" Update a volume attachment. We don't currently support this."""
return faults.Fault(exc.HTTPBadRequest())
def delete(self, req, server_id, id):
""" Detach a volume from an instance """
context = req.environ['nova.context']
volume_id = id
LOG.audit(_("Detach volume %s"), volume_id, context=context)
vol = self.volume_api.get(context, volume_id)
if vol['instance_id'] != server_id:
return faults.Fault(exc.HTTPNotFound())
self.compute_api.detach_volume(context,
volume_id=volume_id)
return exc.HTTPAccepted()
def _items(self, req, server_id, entity_maker):
"""Returns a list of attachments, transformed through entity_maker"""
context = req.environ['nova.context']
try:
instance = self.compute_api.get(context, server_id)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
volumes = instance['volumes']
limited_list = common.limited(volumes, req)
res = [entity_maker(context, vol) for vol in limited_list]
return {'volumeAttachments': res}

View File

@ -29,52 +29,52 @@ LOG = logging.getLogger("nova.api.volumes")
FLAGS = flags.FLAGS
def _translate_detail_view(context, inst):
def _translate_detail_view(context, vol):
""" Maps keys for details view"""
inst_dict = _translate_summary_view(context, inst)
d = _translate_summary_view(context, vol)
# No additional data / lookups at the moment
return inst_dict
return d
def _translate_summary_view(context, volume):
def _translate_summary_view(_context, vol):
""" Maps keys for summary view"""
v = {}
d = {}
instance_id = None
# instance_data = None
attached_to = volume.get('instance')
attached_to = vol.get('instance')
if attached_to:
instance_id = attached_to['id']
# instance_data = '%s[%s]' % (instance_ec2_id,
# attached_to['host'])
v['id'] = volume['id']
v['status'] = volume['status']
v['size'] = volume['size']
v['availabilityZone'] = volume['availability_zone']
v['createdAt'] = volume['created_at']
d['id'] = vol['id']
d['status'] = vol['status']
d['size'] = vol['size']
d['availabilityZone'] = vol['availability_zone']
d['createdAt'] = vol['created_at']
# if context.is_admin:
# v['status'] = '%s (%s, %s, %s, %s)' % (
# volume['status'],
# volume['user_id'],
# volume['host'],
# vol['status'],
# vol['user_id'],
# vol['host'],
# instance_data,
# volume['mountpoint'])
if volume['attach_status'] == 'attached':
v['attachments'] = [{'attachTime': volume['attach_time'],
# vol['mountpoint'])
if vol['attach_status'] == 'attached':
d['attachments'] = [{'attachTime': vol['attach_time'],
'deleteOnTermination': False,
'mountpoint': volume['mountpoint'],
'mountpoint': vol['mountpoint'],
'instanceId': instance_id,
'status': 'attached',
'volumeId': volume['id']}]
'volumeId': vol['id']}]
else:
v['attachments'] = [{}]
d['attachments'] = [{}]
v['displayName'] = volume['display_name']
v['displayDescription'] = volume['display_description']
return v
d['displayName'] = vol['display_name']
d['displayDescription'] = vol['display_description']
return d
class Controller(wsgi.Controller):
@ -102,11 +102,11 @@ class Controller(wsgi.Controller):
context = req.environ['nova.context']
try:
volume = self.volume_api.get(context, id)
vol = self.volume_api.get(context, id)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
return {'volume': _translate_detail_view(context, volume)}
return {'volume': _translate_detail_view(context, vol)}
def delete(self, req, id):
""" Delete a volume """
@ -134,7 +134,7 @@ class Controller(wsgi.Controller):
volumes = self.volume_api.get_all(context)
limited_list = common.limited(volumes, req)
res = [entity_maker(context, inst) for inst in limited_list]
res = [entity_maker(context, vol) for vol in limited_list]
return {'volumes': res}
def create(self, req):
@ -148,13 +148,13 @@ class Controller(wsgi.Controller):
vol = env['volume']
size = vol['size']
LOG.audit(_("Create volume of %s GB"), size, context=context)
volume = self.volume_api.create(context, size,
new_volume = self.volume_api.create(context, size,
vol.get('display_name'),
vol.get('display_description'))
# Work around problem that instance is lazy-loaded...
volume['instance'] = None
retval = _translate_detail_view(context, volume)
retval = _translate_detail_view(context, new_volume)
return {'volume': retval}