summaryrefslogtreecommitdiffstats
path: root/nova/volume
diff options
context:
space:
mode:
authorJohn Griffith <john.griffith@solidfire.com>2012-07-02 11:12:52 -0700
committerVishvananda Ishaya <vishvananda@gmail.com>2012-07-03 12:00:40 -0700
commitd0e99facdca719a16b3b4c247bb1364c26ba3ef9 (patch)
treebfed3e29a21e9923ea5b0fc556103428b4960880 /nova/volume
parent0038e3818393415fc9628aef2747136a626682ee (diff)
Add Cinder Volume API to Nova
* Implements part of blueprint extract-nova-volumes * Adds Cinder API to Nova (nova/volume/cinder.py) * Add fake volume API to use for unit tests * Add specific Cinder ec2/test_cloud version Signed-off-by: Anthony Young <sleepsonthefloor@gmail.com> Signed-off-by: Vishvananda Ishaya <vishvananda@gmail.com> Change-Id: Id56972e2388a94d184c64bfae9c70a1f2805738b
Diffstat (limited to 'nova/volume')
-rw-r--r--nova/volume/__init__.py7
-rw-r--r--nova/volume/cinder.py229
2 files changed, 234 insertions, 2 deletions
diff --git a/nova/volume/__init__.py b/nova/volume/__init__.py
index 1db8efbfd..3fb070052 100644
--- a/nova/volume/__init__.py
+++ b/nova/volume/__init__.py
@@ -21,5 +21,8 @@
import nova.flags
import nova.openstack.common.importutils
-API = nova.openstack.common.importutils.import_class(
- nova.flags.FLAGS.volume_api_class)
+
+def API():
+ importutils = nova.openstack.common.importutils
+ cls = importutils.import_class(nova.flags.FLAGS.volume_api_class)
+ return cls()
diff --git a/nova/volume/cinder.py b/nova/volume/cinder.py
new file mode 100644
index 000000000..0950f4113
--- /dev/null
+++ b/nova/volume/cinder.py
@@ -0,0 +1,229 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# 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.
+
+"""
+Handles all requests relating to volumes + cinder.
+"""
+
+
+from cinderclient import service_catalog
+from cinderclient.v1 import client as cinder_client
+
+from nova.db import base
+from nova import exception
+from nova import flags
+from nova.openstack.common import log as logging
+
+FLAGS = flags.FLAGS
+
+LOG = logging.getLogger(__name__)
+
+
+def cinderclient(context):
+
+ # FIXME: the cinderclient ServiceCatalog object is mis-named.
+ # It actually contains the entire access blob.
+ compat_catalog = {
+ 'access': {'serviceCatalog': context.service_catalog}
+ }
+ sc = service_catalog.ServiceCatalog(compat_catalog)
+ url = sc.url_for(service_type='volume', service_name='cinder')
+
+ LOG.debug('cinderclient connection created using token "%s" and url "%s"' %
+ (context.auth_token, url))
+
+ c = cinder_client.Client(context.user_id,
+ context.auth_token,
+ project_id=context.project_id,
+ auth_url=url)
+ c.client.auth_token = context.auth_token
+ c.client.management_url = url
+ return c
+
+
+def _untranslate_volume_summary_view(context, vol):
+ """Maps keys for volumes summary view."""
+ d = {}
+
+ d['id'] = vol.id
+ d['status'] = vol.status
+ d['size'] = vol.size
+ d['availability_zone'] = vol.availability_zone
+ d['created_at'] = vol.created_at
+
+ # TODO(jdg): The calling code expects attach_time and
+ # mountpoint to be set. When the calling
+ # code is more defensive this can be
+ # removed.
+ d['attach_time'] = ""
+ d['mountpoint'] = ""
+
+ if vol.attachments:
+ att = vol.attachments[0]
+ d['attach_status'] = 'attached'
+ d['instance_uuid'] = att['server_id']
+ d['mountpoint'] = att['device']
+ else:
+ d['attach_status'] = 'detached'
+
+ d['display_name'] = vol.display_name
+ d['display_description'] = vol.display_description
+
+ # TODO(jdg): Information may be lost in this translation
+ d['volume_type_id'] = vol.volume_type
+ d['snapshot_id'] = vol.snapshot_id
+
+ d['vol_metadata'] = []
+ for k, v in vol.metadata:
+ item = {}
+ item['key'] = k
+ item['value'] = v
+ d['vol_metadata'].append(item)
+
+ return d
+
+
+def _untranslate_snapshot_summary_view(context, snapshot):
+ """Maps keys for snapshots summary view."""
+ d = {}
+
+ d['id'] = snapshot.id
+ d['status'] = snapshot.status
+ d['progress'] = snapshot.progress
+ d['size'] = snapshot.size
+ d['created_at'] = snapshot.created_at
+ d['display_name'] = snapshot.display_name
+ d['display_description'] = snapshot.display_description
+ d['volume_id'] = snapshot.volume_id
+ d['project_id'] = snapshot.project_id
+ d['volume_size'] = snapshot.size
+
+ return d
+
+
+class API(base.Base):
+ """API for interacting with the volume manager."""
+
+ def get(self, context, volume_id):
+ item = cinderclient(context).volumes.get(volume_id)
+ return _untranslate_volume_summary_view(context, item)
+
+ def get_all(self, context, search_opts={}):
+ items = cinderclient(context).volumes.list(detailed=True)
+ rval = []
+
+ for item in items:
+ rval.append(_untranslate_volume_summary_view(context, item))
+
+ return rval
+
+ def check_attach(self, context, volume):
+ # TODO(vish): abstract status checking?
+ if volume['status'] != "available":
+ msg = _("status must be available")
+ raise exception.InvalidVolume(reason=msg)
+ if volume['attach_status'] == "attached":
+ msg = _("already attached")
+ raise exception.InvalidVolume(reason=msg)
+
+ def check_detach(self, context, volume):
+ # TODO(vish): abstract status checking?
+ if volume['status'] == "available":
+ msg = _("already detached")
+ raise exception.InvalidVolume(reason=msg)
+
+ def reserve_volume(self, context, volume):
+ cinderclient(context).volumes.reserve(volume['id'])
+
+ def unreserve_volume(self, context, volume):
+ cinderclient(context).volumes.reserve(volume['id'])
+
+ def attach(self, context, volume, instance_uuid, mountpoint):
+ cinderclient(context).volumes.attach(volume['id'],
+ instance_uuid,
+ mountpoint)
+
+ def detach(self, context, volume):
+ cinderclient(context).volumes.detach(volume['id'])
+
+ def initialize_connection(self, context, volume, connector):
+ return cinderclient(context).\
+ volumes.initialize_connection(volume['id'], connector)
+
+ def terminate_connection(self, context, volume, connector):
+ return cinderclient(context).\
+ volumes.terminate_connection(volume['id'], connector)
+
+ def create(self, context, size, name, description, snapshot=None,
+ volume_type=None, metadata=None, availability_zone=None):
+
+ item = cinderclient(context).volumes.create(size, snapshot,
+ name, description,
+ volume_type)
+
+ volume = _untranslate_volume_summary_view(context, item)
+ return _untranslate_volume_summary_view(context, item)
+
+ def delete(self, context, volume):
+ cinderclient(context).volumes.delete(volume['id'])
+
+ def update(self, context, volume, fields):
+ raise NotImplementedError()
+
+ def get_snapshot(self, context, snapshot_id):
+ item = cinderclient(context).volume_snapshots.get(snapshot_id)
+ return _untranslate_snapshot_summary_view(context, item)
+
+ def get_all_snapshots(self, context):
+ items = cinderclient(context).volume_snapshots.list(detailed=True)
+ rvals = []
+
+ for item in items:
+ rvals.append(_untranslate_snapshot_summary_view(context, item))
+
+ return rvals
+
+ def create_snapshot(self, context, volume, name, description):
+ item = cinderclient(context).volume_snapshots.create(volume['id'],
+ False,
+ name,
+ description)
+ return _untranslate_snapshot_summary_view(context, item)
+
+ def create_snapshot_force(self, context, volume, name, description):
+ item = cinderclient(context).volume_snapshots.create(volume['id'],
+ True,
+ name,
+ description)
+
+ return _untranslate_snapshot_summary_view(context, item)
+
+ def delete_snapshot(self, context, snapshot):
+ cinderclient(context).volume_snapshots.delete(snapshot['id'])
+
+ def get_volume_metadata(self, context, volume):
+ raise NotImplementedError()
+
+ def delete_volume_metadata(self, context, volume, key):
+ raise NotImplementedError()
+
+ def update_volume_metadata(self, context, volume, metadata, delete=False):
+ raise NotImplementedError()
+
+ def get_volume_metadata_value(self, volume, key):
+ raise NotImplementedError()