summaryrefslogtreecommitdiffstats
path: root/nova
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 /nova
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.
Diffstat (limited to 'nova')
-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'],