diff options
| author | John Griffith <john.griffith@solidfire.com> | 2012-07-02 11:12:52 -0700 |
|---|---|---|
| committer | Vishvananda Ishaya <vishvananda@gmail.com> | 2012-07-03 12:00:40 -0700 |
| commit | d0e99facdca719a16b3b4c247bb1364c26ba3ef9 (patch) | |
| tree | bfed3e29a21e9923ea5b0fc556103428b4960880 /nova/volume | |
| parent | 0038e3818393415fc9628aef2747136a626682ee (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__.py | 7 | ||||
| -rw-r--r-- | nova/volume/cinder.py | 229 |
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() |
