volume/driver: implement basic snapshot/clone
added basic support for snapshot/clone to VolumeDriver. The implementation is not effective, but works. The effective implementation should be done by drived driver class.
This commit is contained in:
1
Authors
1
Authors
@@ -28,6 +28,7 @@ Gabe Westmaas <gabe.westmaas@rackspace.com>
|
||||
Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp>
|
||||
Hisaki Ohara <hisaki.ohara@intel.com>
|
||||
Ilya Alekseyev <ialekseev@griddynamics.com>
|
||||
Isaku Yamahata <yamahata@valinux.co.jp>
|
||||
Jason Koelker <jason@koelker.net>
|
||||
Jay Pipes <jaypipes@gmail.com>
|
||||
Jesse Andrews <anotherjesse@gmail.com>
|
||||
|
||||
@@ -79,6 +79,12 @@ class VolumeNotFound(NotFound):
|
||||
super(VolumeNotFound, self).__init__(message)
|
||||
|
||||
|
||||
class VolumeIsBusy(Error):
|
||||
def __init__(self, message, volume_id):
|
||||
self.volume_id = volume_id
|
||||
super(Error, self).__init__(message)
|
||||
|
||||
|
||||
class SnapshotNotFound(NotFound):
|
||||
def __init__(self, message, snapshot_id):
|
||||
self.snapshot_id = snapshot_id
|
||||
|
||||
@@ -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."""
|
||||
@@ -127,27 +135,51 @@ class VolumeDriver(object):
|
||||
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Creates a volume from a snapshot."""
|
||||
raise NotImplementedError()
|
||||
self._create_volume(volume['name'], self._sizestr(volume['size']))
|
||||
self._copy_volume(self.local_path(snapshot), self.local_path(volume),
|
||||
snapshot['volume_size'])
|
||||
|
||||
def delete_volume(self, volume):
|
||||
"""Deletes a logical volume."""
|
||||
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(
|
||||
_('deleting volume %s that has snapshot'), 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):
|
||||
|
||||
@@ -147,6 +147,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'],
|
||||
|
||||
Reference in New Issue
Block a user