openstack team mailing list archive
-
openstack team
-
Mailing list archive
-
Message #14955
Volume with ZVol : opinion on code
-
To:
openstack <openstack@xxxxxxxxxxxxxxxxxxx>
-
From:
Nicolas de BONFILS <openstack@xxxxxxxxxxxxxxxxxxx>
-
Date:
Fri, 20 Jul 2012 20:33:38 +0200
-
In-reply-to:
<50099E5D.4050108@nicolas2bonfils.com>
-
User-agent:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:15.0) Gecko/20120703 Thunderbird/15.0a2
Hi,
Following my ideas about using ZFS (ZVol) instead of LVM for the volume
(https://lists.launchpad.net/openstack/msg13009.html). I write a piece
of code : in attachment or on github/nicolas2bonfils
<https://github.com/nicolas2bonfils/cinder/blob/master/cinder/volume/driver_zvol.py>
(for line comments or push request).
I write and test the code with ZFSOnLinux <http://zfsonlinux.org/> and
Debian sid.
What it does :
* create ZVol instead of LVM,
* use ISCSI sharing from the existing cinder driver,
* use ZFS snapshot for the snapshot feature,
* is working for create/delete volume,
* can create/delete native ZFS snapshot (see below for the linked
problem).
What need to be improve :
* check if native ZFS snapshot can be use for volume snapshot (same
use/meaning ?),
* when deleting a volume with snapshot (ZFS one), do we delete the
derived snapshot or export them as independent volume.
What it does not (yet) :
* share with native ZFS ISCSI (due to ZFSOnLinux limitation)
Thanks for all your ideas, advice, ...
--- Nicolas de Bonfils
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Nicolas de Bonfils openstack@xxxxxxxxxxxxxxxxxxx
#
# 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.
#TODO(nicolas) : i18n the log message
from cinder.volume.driver import *
class ZVolDriver(ISCSIDriver):
"""
Drivers for volumes using Zvol instead of LVM.
Tested with ZFSOnLinux on a Debian sid system
"""
def __init__(self, *args, **kwargs):
#TODO(nicolas) : use configuration flag
self.zfs_bin = "/sbin/zfs"
self.zpool_bin = "/sbin/zpool"
super(ZVolDriver, self).__init__(*args, **kwargs)
def check_for_setup_error(self):
"""Returns an error if prerequisites aren't met"""
out, err = self._execute('%s' % self.zfs_bin, 'list', run_as_root=True)
volume_groups = out.split()
if not FLAGS.volume_group in volume_groups:
raise exception.Error(_("zfs base volume group '%s' doesn't exist")
% FLAGS.volume_group)
def _create_volume(self, volume_name, sizestr):
LOG.debug('Create volume with command "%s create -V %s %s/%s"' % (self.zfs_bin, sizestr, FLAGS.volume_group, volume_name))
self._try_execute('%s' % self.zfs_bin, 'create',
'-V', sizestr,
'%s/%s' % (FLAGS.volume_group, volume_name),
run_as_root=True)
def _copy_volume(self, srcstr, deststr, size_in_g):
LOG.debug('copy volume with method "dd if=%s of=%s count=%d bs=1M"' % (srcstr, deststr, (size_in_g * 1024)))
self._execute('dd', 'if=%s' % srcstr, 'of=%s' % deststr,
'count=%d' % (size_in_g * 1024), 'bs=1M',
run_as_root=True)
def _volume_not_present(self, volume_name):
path_name = '%s/%s' % (FLAGS.volume_group, volume_name)
out, err = self._execute('%s' % self.zfs_bin, 'list', run_as_root=True)
volume_search = out.split()
LOG.debug('List volume with command "%s list"' % self.zfs_bin)
LOG.debug(volume_search)
return not path_name in volume_search
def _delete_volume(self, volume, size_in_g):
"""Deletes a zvol."""
# zero out old volumes to prevent data leaking between users
# TODO(ja): reclaiming space should be done lazy and low priority
self._copy_volume('/dev/zero', self.local_path(volume), size_in_g)
LOG.debug('Destroy volume with command "%s destroy %s/%s"' % (self.zfs_bin, FLAGS.volume_group, volume['name']))
self._try_execute('%s' % self.zfs_bin, 'destroy',
'%s/%s' % (FLAGS.volume_group, volume['name']),
run_as_root=True)
def delete_volume(self, volume):
"""Deletes a zvol."""
if self._volume_not_present(volume['name']):
# If the volume isn't present, then don't attempt to delete
return True
# When snapshots exist, what to do ?
# remove them or export them as new independent volume
self._delete_volume(volume, volume['size'])
def create_snapshot(self, snapshot):
"""Creates a snapshot."""
LOG.debug('Snapshot volume with command "%s snapshot %s/%s"' % (self.zfs_bin,
FLAGS.volume_group,
self._snapshot_full_name(snapshot)))
self._try_execute('%s' % self.zfs_bin, 'snapshot',
'%s/%s' % (FLAGS.volume_group, self._snapshot_full_name(snapshot)),
run_as_root=True)
def delete_snapshot(self, snapshot):
"""Deletes a snapshot."""
if self._volume_not_present(self._snapshot_full_name(snapshot)):
# If the snapshot isn't present, then don't attempt to delete
return True
self._delete_volume(snapshot, snapshot['volume_size'])
def local_path(self, volume):
"""Give full path to the system /dev object"""
full_name = ""
# if we got a volume name, then it's a snapshot so create the full path
# otherwise it a "regular" volume
# it's a hack to know if volume "respond to" volume_name key.
#FIXME(nicolas) : volume is sqlalchemy object, how to query existence for a property/method ?
try:
volume_name = volume['volume_name']
full_name = self._snapshot_full_name(volume)
except Exception as e:
full_name = volume['name']
return "/dev/zvol/%s/%s" % (FLAGS.volume_group, full_name)
def _snapshot_full_name(self, snapshot):
"""Give the snapshot full name : volume name + snapshot name with '@' in the middle """
return '%s@%s' % (snapshot['volume_name'], snapshot['name'])