summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIsaku Yamahata <yamahata@valinux.co.jp>2011-05-13 23:27:35 +0900
committerIsaku Yamahata <yamahata@valinux.co.jp>2011-05-13 23:27:35 +0900
commit4f7cfba4a00f04b7c30c61da2946f183241a7c7f (patch)
treeca4c83e3d5573c02a02302bc3a350e4f9fce06ec
parentbbbea57cf6ab28c3ad1081041275e0d6d2bbd308 (diff)
volume/driver: implement basic snapshot
added basic support for snapshot to VolumeDriver base class. The implementation is not effective, but works. The effective implementation should be done by drived driver class.
-rw-r--r--nova/exception.py4
-rw-r--r--nova/volume/driver.py37
-rw-r--r--nova/volume/manager.py6
3 files changed, 43 insertions, 4 deletions
diff --git a/nova/exception.py b/nova/exception.py
index 39620ccc1..bd04435ed 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -271,6 +271,10 @@ class SnapshotNotFound(NotFound):
message = _("Snapshot %(snapshot_id)s could not be found.")
+class VolumeIsBusy(Error):
+ message = _("deleting volume %(volume_name)s that has snapshot")
+
+
class ExportDeviceNotFoundForVolume(NotFound):
message = _("No export device found for volume %(volume_id)s.")
diff --git a/nova/volume/driver.py b/nova/volume/driver.py
index ec7be37bf..a6cf2cb46 100644
--- a/nova/volume/driver.py
+++ b/nova/volume/driver.py
@@ -113,13 +113,21 @@ class VolumeDriver(object):
# TODO(ja): reclaiming space should be done lazy and low priority
self._copy_volume('/dev/zero', self.local_path(volume), size_in_g)
self._try_execute('sudo', 'lvremove', '-f', "%s/%s" %
- (FLAGS.volume_group, volume['name']))
+ (FLAGS.volume_group,
+ self._escape_snapshot(volume['name'])))
def _sizestr(self, size_in_g):
if int(size_in_g) == 0:
return '100M'
return '%sG' % size_in_g
+ # Linux LVM reserves name that starts with snapshot, so that
+ # such volume name can't be created. Mangle it.
+ def _escape_snapshot(self, snapshot_name):
+ if not snapshot_name.startswith('snapshot'):
+ return snapshot_name
+ return '_' + snapshot_name
+
def create_volume(self, volume):
"""Creates a logical volume. Can optionally return a Dictionary of
changes to the volume object to be persisted."""
@@ -130,20 +138,41 @@ class VolumeDriver(object):
if self._volume_not_present(volume['name']):
# If the volume isn't present, then don't attempt to delete
return True
+
+ # TODO(yamahata): lvm can't delete origin volume only without
+ # deleting derived snapshots. Can we do something fancy?
+ out, err = self._execute('sudo', 'lvdisplay', '--noheading',
+ '-C', '-o', 'Attr',
+ '%s/%s' % (FLAGS.volume_group,
+ volume['name']))
+ out = out.strip()
+ if (out[0] == 'o') or (out[0] == 'O'):
+ raise exception.VolumeIsBusy(volume_name=volume['name'])
+
self._delete_volume(volume, volume['size'])
def create_snapshot(self, snapshot):
"""Creates a snapshot."""
- raise NotImplementedError()
+ orig_lv_name = "%s/%s" % (FLAGS.volume_group, snapshot['volume_name'])
+ self._try_execute('sudo', 'lvcreate', '-L',
+ self._sizestr(snapshot['volume_size']),
+ '--name', self._escape_snapshot(snapshot['name']),
+ '--snapshot', orig_lv_name)
def delete_snapshot(self, snapshot):
"""Deletes a snapshot."""
- raise NotImplementedError()
+ if self._volume_not_present(self._escape_snapshot(snapshot['name'])):
+ # If the snapshot isn't present, then don't attempt to delete
+ return True
+
+ # TODO(yamahata): zeroing out the whole snapshot triggers COW.
+ # it's quite slow.
+ self._delete_volume(snapshot, snapshot['volume_size'])
def local_path(self, volume):
# NOTE(vish): stops deprecation warning
escaped_group = FLAGS.volume_group.replace('-', '--')
- escaped_name = volume['name'].replace('-', '--')
+ escaped_name = self._escape_snapshot(volume['name']).replace('-', '--')
return "/dev/mapper/%s-%s" % (escaped_group, escaped_name)
def ensure_export(self, context, volume):
diff --git a/nova/volume/manager.py b/nova/volume/manager.py
index 87fd3bf17..fd889633d 100644
--- a/nova/volume/manager.py
+++ b/nova/volume/manager.py
@@ -142,6 +142,12 @@ class VolumeManager(manager.SchedulerDependentManager):
self.driver.remove_export(context, volume_ref)
LOG.debug(_("volume %s: deleting"), volume_ref['name'])
self.driver.delete_volume(volume_ref)
+ except exception.VolumeIsBusy, e:
+ LOG.debug(_("volume %s: volume is busy"), volume_ref['name'])
+ self.driver.ensure_export(context, volume_ref)
+ self.db.volume_update(context, volume_ref['id'],
+ {'status': 'available'})
+ return True
except Exception:
self.db.volume_update(context,
volume_ref['id'],