summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorJustin Santa Barbara <justin@fathomdb.com>2011-03-24 01:03:41 -0700
committerJustin Santa Barbara <justin@fathomdb.com>2011-03-24 01:03:41 -0700
commit1894937e1ef6769a5f76c0a382931480e2547ce8 (patch)
treee6513ce0ab1921f46187ebbbd71cc3635487b9cc /nova/api
parent7b9888df2d2ab4cb1c706ffb16c3c650cb8ad61a (diff)
Added volume_attachments
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/__init__.py6
-rw-r--r--nova/api/openstack/volume_attachments.py154
-rw-r--r--nova/api/openstack/volumes.py60
3 files changed, 190 insertions, 30 deletions
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 474c1d0e6..af3f8c5ce 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -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',
diff --git a/nova/api/openstack/volume_attachments.py b/nova/api/openstack/volume_attachments.py
new file mode 100644
index 000000000..fbcec7c29
--- /dev/null
+++ b/nova/api/openstack/volume_attachments.py
@@ -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}
diff --git a/nova/api/openstack/volumes.py b/nova/api/openstack/volumes.py
index 99300421e..ea2dc4aab 100644
--- a/nova/api/openstack/volumes.py
+++ b/nova/api/openstack/volumes.py
@@ -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,
- vol.get('display_name'),
- vol.get('display_description'))
+ 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}