[libvirt] [PATCH v2] storage: ZFS support

Changes from the initial version: - Update docs/schemas and docs/storage.html.in with ZFS backend information - Drop StartPool and StopPool that does nothing and therefore not needed - Fix memory leak in 'tokens' variable - Fill volume key before creating a volume - Use volume's target.path as a key - Allow not to specify 'target' for pool because in any case it should be /dev/zvol/$poolname Few notes on Linux support: I've been playing around with http://zfsonlinux.org/. I noticed it missed '-H' switch to provide a machine-friendly output and created a feature request: https://github.com/zfsonlinux/zfs/issues/2522 It was quickly fixed, so I've updated from the git version and moved on.
From that point it worked well and the only thing I had to modify is not to pass 'volmode' parameter during volume creation (it's freebsd specific and it could be e.g. just cdev or a FreeBSD geom provider).
So, basically, for the current (limited) feature set the two things need to be checked on Linux: - zpool get support for -H switch - zfs volmode support for volume creation I'm open for suggestions what would be a good way to check it. For the '-H' thing, I was thinking about calling just 'zpool get -H' in StartPool. If the error says 'invalid option 'H'' (i.e. check if it just contains 'H'), then return an error that zfs version is too old. That's fragile, but probably less fragile than parsing usage that looks like that: usage: get [-pH] <"all" | property[,...]> <pool> ... As for the 'volmode', it's simple in terms of parsing, because e.g. 'zfs get' prints a nice set of parameters e.g.: volblocksize NO YES 512 to 128k, power of 2 volmode YES YES default | geom | dev | none volsize YES NO <size> So it should not be a problem to check it once and save in some sort of state struct inside of the ZFS backend. Roman Bogorodskiy (1): storage: ZFS support configure.ac | 43 +++++ docs/schemas/storagepool.rng | 20 +++ docs/storage.html.in | 34 ++++ include/libvirt/libvirt.h.in | 1 + po/POTFILES.in | 1 + src/Makefile.am | 8 + src/conf/storage_conf.c | 15 +- src/conf/storage_conf.h | 4 +- src/qemu/qemu_conf.c | 1 + src/storage/storage_backend.c | 6 + src/storage/storage_backend_zfs.c | 329 ++++++++++++++++++++++++++++++++++++++ src/storage/storage_backend_zfs.h | 29 ++++ src/storage/storage_driver.c | 1 + tools/virsh-pool.c | 3 + 14 files changed, 492 insertions(+), 3 deletions(-) create mode 100644 src/storage/storage_backend_zfs.c create mode 100644 src/storage/storage_backend_zfs.h -- 1.9.0

Implement ZFS storage backend driver. Currently supported only on FreeBSD because of ZFS limitations on Linux. Features supported: - pool-start, pool-stop - pool-info - vol-list - vol-create / vol-delete Pool definition looks like that: <pool type='zfs'> <name>myzfspool</name> <source> <name>actualpoolname</name> </source> </pool> The 'actualpoolname' value is a name of the pool on the system, such as shown by 'zpool list' command. Target makes no sense here because volumes path is always /dev/zvol/$poolname/$volname. Users has to create a pool on his own, this driver doesn't support pool creation currently. A volume could be used with Qemu by adding an entry like this: <disk type='volume' device='disk'> <driver name='qemu' type='raw'/> <source pool='myzfspool' volume='vol5'/> <target dev='hdc' bus='ide'/> </disk> --- configure.ac | 43 +++++ docs/schemas/storagepool.rng | 20 +++ docs/storage.html.in | 34 ++++ include/libvirt/libvirt.h.in | 1 + po/POTFILES.in | 1 + src/Makefile.am | 8 + src/conf/storage_conf.c | 15 +- src/conf/storage_conf.h | 4 +- src/qemu/qemu_conf.c | 1 + src/storage/storage_backend.c | 6 + src/storage/storage_backend_zfs.c | 329 ++++++++++++++++++++++++++++++++++++++ src/storage/storage_backend_zfs.h | 29 ++++ src/storage/storage_driver.c | 1 + tools/virsh-pool.c | 3 + 14 files changed, 492 insertions(+), 3 deletions(-) create mode 100644 src/storage/storage_backend_zfs.c create mode 100644 src/storage/storage_backend_zfs.h diff --git a/configure.ac b/configure.ac index f37c716..7e46460 100644 --- a/configure.ac +++ b/configure.ac @@ -1734,6 +1734,10 @@ AC_ARG_WITH([storage-gluster], [AS_HELP_STRING([--with-storage-gluster], [with Gluster backend for the storage driver @<:@default=check@:>@])], [],[with_storage_gluster=check]) +AC_ARG_WITH([storage-zfs], + [AS_HELP_STRING([--with-storage-zfs], + [with ZFS backend for the storage driver @<:@default=check@:>@])], + [],[with_storage_zfs=check]) if test "$with_libvirtd" = "no"; then with_storage_dir=no @@ -1746,6 +1750,7 @@ if test "$with_libvirtd" = "no"; then with_storage_rbd=no with_storage_sheepdog=no with_storage_gluster=no + with_storage_zfs=no fi if test "$with_storage_dir" = "yes" ; then AC_DEFINE_UNQUOTED([WITH_STORAGE_DIR], 1, [whether directory backend for storage driver is enabled]) @@ -1963,6 +1968,43 @@ if test "$with_storage_gluster" = "yes"; then fi AM_CONDITIONAL([WITH_STORAGE_GLUSTER], [test "$with_storage_gluster" = "yes"]) +if test "$with_storage_zfs" = "check"; then + with_storage_zfs=$with_freebsd +fi + +if test "$with_storage_zfs" = "yes" && test "$with_freebsd" = "no"; then + AC_MSG_ERROR([The ZFS storage driver can be enabled on FreeBSD only.]) +fi + +if test "$with_storage_zfs" = "yes" || + test "$with_storage_zfs" = "check"; then + AC_PATH_PROG([ZFS], [zfs], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG([ZPOOL], [zpool], [], [$PATH:/sbin:/usr/sbin]) + + if test "$with_storage_zfs" = "yes"; then + if test -z "$ZFS" || test -z "$ZPOOL"; then + AC_MSG_ERROR([We need zfs and zpool for ZFS storage driver]) + fi + else + if test -z "$ZFS" || test -z "$ZPOOL"; then + with_storage_zfs=no + fi + + if test "$with_storage_zfs" = "check"; then + with_storage_zfs=yes + fi + fi + + if test "$with_storage_zfs" = "yes"; then + AC_DEFINE_UNQUOTED([WITH_STORAGE_ZFS], 1, + [whether ZFS backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([ZFS], ["$ZFS"], [Location of zfs program]) + AC_DEFINE_UNQUOTED([ZPOOL], ["$ZPOOL"], [Location of zpool program]) + fi +fi +AM_CONDITIONAL([WITH_STORAGE_ZFS], + [test "$with_storage_zfs" = "yes"]) + if test "$with_storage_fs" = "yes" || test "$with_storage_gluster" = "yes"; then AC_PATH_PROG([GLUSTER_CLI], [gluster], [], [$PATH:/sbin:/usr/sbin]) @@ -2806,6 +2848,7 @@ AC_MSG_NOTICE([ Disk: $with_storage_disk]) AC_MSG_NOTICE([ RBD: $with_storage_rbd]) AC_MSG_NOTICE([Sheepdog: $with_storage_sheepdog]) AC_MSG_NOTICE([ Gluster: $with_storage_gluster]) +AC_MSG_NOTICE([ ZFS: $with_storage_zfs]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Security Drivers]) AC_MSG_NOTICE([]) diff --git a/docs/schemas/storagepool.rng b/docs/schemas/storagepool.rng index b2d1473..908cc11 100644 --- a/docs/schemas/storagepool.rng +++ b/docs/schemas/storagepool.rng @@ -22,6 +22,7 @@ <ref name='poolrbd'/> <ref name='poolsheepdog'/> <ref name='poolgluster'/> + <ref name='poolzfs'/> </choice> </element> </define> @@ -157,6 +158,17 @@ </interleave> </define> + <define name='poolzfs'> + <attribute name='type'> + <value>zfs</value> + </attribute> + <interleave> + <ref name='commonmetadata'/> + <ref name='sizing'/> + <ref name='sourcezfs'/> + </interleave> + </define> + <define name='sourceinfovendor'> <interleave> <optional> @@ -370,6 +382,14 @@ </element> </define> + <define name='sourcezfs'> + <element name='source'> + <interleave> + <ref name='sourceinfoname'/> + </interleave> + </element> + </define> + <define name='sourcefmtfs'> <optional> <element name='format'> diff --git a/docs/storage.html.in b/docs/storage.html.in index eb38b16..5db79c7 100644 --- a/docs/storage.html.in +++ b/docs/storage.html.in @@ -117,6 +117,9 @@ <li> <a href="#StorageBackendGluster">Gluster backend</a> </li> + <li> + <a href="#StorageBackendZFS">ZFS backend</a> + </li> </ul> <h2><a name="StorageBackendDir">Directory pool</a></h2> @@ -743,5 +746,36 @@ pool type. </p> + <h2><a name="StorageBackendZFS">ZFS pools</a></h2> + <p> + This provides a pool based on the ZFS filesystem. It is currently + supported on FreeBSD only. + + A pool has to be created before libvirt could start using it. That + could be done using <code>zpool create</code> command. Please refer to + the ZFS documentation for details on a pool creation. + + <span class="since">Since 1.2.8</span> + </p> + + <h3>Example pool input</h3> + <pre> + <pool type="zfs"> + <name>myzfspool</name> + <source> + <name>zpoolname</name> + </source> + </pool></pre> + + <h3>Valid pool format types</h3> + <p> + The ZFS volume pool does not use the pool format type element. + </p> + + <h3>Valid pool format types</h3> + <p> + The ZFS volume pool does not use the volume format type element. + </p> + </body> </html> diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index ad6785f..47ea695 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3170,6 +3170,7 @@ typedef enum { VIR_CONNECT_LIST_STORAGE_POOLS_RBD = 1 << 14, VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG = 1 << 15, VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER = 1 << 16, + VIR_CONNECT_LIST_STORAGE_POOLS_ZFS = 1 << 17, } virConnectListAllStoragePoolsFlags; int virConnectListAllStoragePools(virConnectPtr conn, diff --git a/po/POTFILES.in b/po/POTFILES.in index d10e892..2646bcc 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -148,6 +148,7 @@ src/storage/storage_backend_mpath.c src/storage/storage_backend_rbd.c src/storage/storage_backend_scsi.c src/storage/storage_backend_sheepdog.c +src/storage/storage_backend_zfs.c src/storage/storage_driver.c src/test/test_driver.c src/uml/uml_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index 982f63d..d5d06d1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -862,6 +862,9 @@ STORAGE_DRIVER_SHEEPDOG_SOURCES = \ STORAGE_DRIVER_GLUSTER_SOURCES = \ storage/storage_backend_gluster.h storage/storage_backend_gluster.c +STORAGE_DRIVER_ZFS_SOURCES = \ + storage/storage_backend_zfs.h storage/storage_backend_zfs.c + STORAGE_HELPER_DISK_SOURCES = \ storage/parthelper.c @@ -1514,6 +1517,10 @@ libvirt_driver_storage_impl_la_CFLAGS += $(GLUSTERFS_CFLAGS) libvirt_driver_storage_impl_la_LIBADD += $(GLUSTERFS_LIBS) endif WITH_STORAGE_GLUSTER +if WITH_STORAGE_ZFS +libvirt_driver_storage_impl_la_SOURCES += $(STORAGE_DRIVER_ZFS_SOURCES) +endif WITH_STORAGE_ZFS + if WITH_NODE_DEVICES # Needed to keep automake quiet about conditionals if WITH_DRIVER_MODULES @@ -1723,6 +1730,7 @@ EXTRA_DIST += \ $(STORAGE_DRIVER_RBD_SOURCES) \ $(STORAGE_DRIVER_SHEEPDOG_SOURCES) \ $(STORAGE_DRIVER_GLUSTER_SOURCES) \ + $(STORAGE_DRIVER_ZFS_SOURCES) \ $(NODE_DEVICE_DRIVER_SOURCES) \ $(NODE_DEVICE_DRIVER_HAL_SOURCES) \ $(NODE_DEVICE_DRIVER_UDEV_SOURCES) \ diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index e33eb1b..b61d8c0 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -61,7 +61,8 @@ VIR_ENUM_IMPL(virStoragePool, VIR_STORAGE_POOL_LAST, "dir", "fs", "netfs", "logical", "disk", "iscsi", - "scsi", "mpath", "rbd", "sheepdog", "gluster") + "scsi", "mpath", "rbd", + "sheepdog", "gluster", "zfs") VIR_ENUM_IMPL(virStoragePoolFormatFileSystem, VIR_STORAGE_POOL_FS_LAST, @@ -278,7 +279,13 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { .formatFromString = virStorageVolFormatDiskTypeFromString, .formatToString = virStorageVolFormatDiskTypeToString, }, - } + }, + {.poolType = VIR_STORAGE_POOL_ZFS, + .poolOptions = { + .flags = (VIR_STORAGE_POOL_SOURCE_NAME), + .defaultFormat = VIR_STORAGE_FILE_RAW, + }, + }, }; @@ -924,6 +931,10 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) if (virAsprintf(&target_path, "/dev/%s", ret->source.name) < 0) { goto error; } + } else if (ret->type == VIR_STORAGE_POOL_ZFS) { + if (virAsprintf(&target_path, "/dev/zvol/%s", ret->source.name) < 0) { + goto error; + } } else { target_path = virXPathString("string(./target/path)", ctxt); if (!target_path) { diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h index ecd0dfe..1276ac2 100644 --- a/src/conf/storage_conf.h +++ b/src/conf/storage_conf.h @@ -92,6 +92,7 @@ typedef enum { VIR_STORAGE_POOL_RBD, /* RADOS Block Device */ VIR_STORAGE_POOL_SHEEPDOG, /* Sheepdog device */ VIR_STORAGE_POOL_GLUSTER, /* Gluster device */ + VIR_STORAGE_POOL_ZFS, /* ZFS */ VIR_STORAGE_POOL_LAST, } virStoragePoolType; @@ -509,7 +510,8 @@ VIR_ENUM_DECL(virStoragePartedFs) VIR_CONNECT_LIST_STORAGE_POOLS_MPATH | \ VIR_CONNECT_LIST_STORAGE_POOLS_RBD | \ VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG | \ - VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER) + VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER | \ + VIR_CONNECT_LIST_STORAGE_POOLS_ZFS) # define VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ALL \ (VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE | \ diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 9636a87..05547a7 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1296,6 +1296,7 @@ qemuTranslateDiskSourcePool(virConnectPtr conn, case VIR_STORAGE_POOL_LOGICAL: case VIR_STORAGE_POOL_DISK: case VIR_STORAGE_POOL_SCSI: + case VIR_STORAGE_POOL_ZFS: if (!(def->src->path = virStorageVolGetPath(vol))) goto cleanup; diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 27b02cb..aad913e 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -88,6 +88,9 @@ #if WITH_STORAGE_GLUSTER # include "storage_backend_gluster.h" #endif +#if WITH_STORAGE_ZFS +# include "storage_backend_zfs.h" +#endif #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -125,6 +128,9 @@ static virStorageBackendPtr backends[] = { #if WITH_STORAGE_GLUSTER &virStorageBackendGluster, #endif +#if WITH_STORAGE_ZFS + &virStorageBackendZFS, +#endif NULL }; diff --git a/src/storage/storage_backend_zfs.c b/src/storage/storage_backend_zfs.c new file mode 100644 index 0000000..2aeefb5 --- /dev/null +++ b/src/storage/storage_backend_zfs.c @@ -0,0 +1,329 @@ +/* + * storage_backend_zfs.c: storage backend for ZFS handling + * + * Copyright (C) 2014 Roman Bogorodskiy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "storage_backend_zfs.h" +#include "virlog.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +VIR_LOG_INIT("storage.storage_backend_zfs"); + +/* + * Some common flags of zfs and zpool commands we use: + * -H -- don't print headers and separate fields by tab + * -p -- show exact numbers instead of human-readable, i.e. + * for size, show just a number instead of 2G etc + */ + + +static int +virStorageBackendZFSCheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + bool *isActive) +{ + char *devpath; + + if (virAsprintf(&devpath, "/dev/zvol/%s", + pool->def->source.name) == -1) + return -1; + *isActive = virFileIsDir(devpath); + VIR_FREE(devpath); + + return 0; +} + +static int +virStorageBackendZFSParseVol(virStoragePoolObjPtr pool, + const char *volume) +{ + int ret = -1; + char **tokens; + size_t count; + char **name_tokens = NULL; + char *vol_name; + bool is_new_vol = false; + virStorageVolDefPtr vol = NULL; + + if (!(tokens = virStringSplitCount(volume, "\t", 0, &count))) + return -1; + + if (count != 2) + goto cleanup; + + if (!(name_tokens = virStringSplit(tokens[0], "/", 2))) + goto cleanup; + + vol_name = name_tokens[1]; + + vol = virStorageVolDefFindByName(pool, vol_name); + + if (vol == NULL) { + if (VIR_ALLOC(vol) < 0) + goto cleanup; + + is_new_vol = true; + vol->type = VIR_STORAGE_VOL_BLOCK; + + if (VIR_STRDUP(vol->name, vol_name) < 0) + goto cleanup; + } + + if (!vol->key && VIR_STRDUP(vol->key, tokens[0]) < 0) + goto cleanup; + + if (vol->target.path == NULL) { + if (virAsprintf(&vol->target.path, "%s/%s", + pool->def->target.path, vol->name) < 0) + goto cleanup; + } + + if (virStrToLong_ull(tokens[1], NULL, 10, &vol->target.capacity) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed volsize reported")); + goto cleanup; + } + + if (is_new_vol && + VIR_APPEND_ELEMENT(pool->volumes.objs, pool->volumes.count, vol) < 0) + goto cleanup; + + ret = 0; + cleanup: + virStringFreeList(tokens); + virStringFreeList(name_tokens); + if (is_new_vol && (ret == -1)) + virStorageVolDefFree(vol); + return ret; +} + +static int +virStorageBackendZFSFindVols(virStoragePoolObjPtr pool) +{ + virCommandPtr cmd = NULL; + char *volumes_list = NULL; + char **lines = NULL; + size_t i; + + /** + * $ zfs list -Hp -t volume -o name,volsize -r test + * test/vol1 5368709120 + * test/vol3 1073741824 + * test/vol4 1572864000 + * $ + * + * Arguments description: + * -t volume -- we want to see only volumes + * -o name,volsize -- limit output to name and volume size + * -r -- we want to see all the childer of our pool + */ + cmd = virCommandNewArgList(ZFS, + "list", "-Hp", + "-t", "volume", "-r", + "-o", "name,volsize", + pool->def->source.name, + NULL); + virCommandSetOutputBuffer(cmd, &volumes_list); + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + if (!(lines = virStringSplit(volumes_list, "\n", 0))) + goto cleanup; + + for (i = 0; lines[i]; i++) { + if (STREQ(lines[i], "")) + continue; + + if (virStorageBackendZFSParseVol(pool, lines[i]) < 0) + continue; + } + + cleanup: + virCommandFree(cmd); + virStringFreeList(lines); + VIR_FREE(volumes_list); + + return 0; +} + +static int +virStorageBackendZFSRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED) +{ + virCommandPtr cmd = NULL; + char *zpool_props = NULL; + char **lines = NULL; + char **tokens = NULL; + size_t i; + + /** + * $ zpool get -Hp health,size,free,allocated test + * test health ONLINE - + * test size 199715979264 - + * test free 198899976704 - + * test allocated 816002560 - + * $ + * + * Here we just provide a list of properties we want to see + */ + cmd = virCommandNewArgList(ZPOOL, + "get", "-Hp", + "health,size,free,allocated", + pool->def->source.name, + NULL); + virCommandSetOutputBuffer(cmd, &zpool_props); + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + if (!(lines = virStringSplit(zpool_props, "\n", 0))) + goto cleanup; + + for (i = 0; lines[i]; i++) { + size_t count; + char *prop_name; + + if (STREQ(lines[i], "")) + continue; + + virStringFreeList(tokens); + if (!(tokens = virStringSplitCount(lines[i], "\t", 0, &count))) + goto cleanup; + + if (count != 4) + continue; + + prop_name = tokens[1]; + + if (STREQ(prop_name, "free") || STREQ(prop_name, "size") || + STREQ(prop_name, "allocated")) { + unsigned long long value; + if (virStrToLong_ull(tokens[2], NULL, 10, &value) < 0) + goto cleanup; + + if (STREQ(prop_name, "free")) + pool->def->available = value; + else if (STREQ(prop_name, "size")) + pool->def->capacity = value; + else if (STREQ(prop_name, "allocated")) + pool->def->allocation = value; + } + } + + /* Obtain a list of volumes */ + if (virStorageBackendZFSFindVols(pool) < 0) + goto cleanup; + + cleanup: + virCommandFree(cmd); + virStringFreeList(lines); + virStringFreeList(tokens); + VIR_FREE(zpool_props); + + return 0; +} + +static int +virStorageBackendZFSCreateVol(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + virCommandPtr cmd = NULL; + int ret = -1; + + vol->type = VIR_STORAGE_VOL_BLOCK; + + if (vol->target.path != NULL) { + /* A target path passed to CreateVol has no meaning */ + VIR_FREE(vol->target.path); + } + + if (virAsprintf(&vol->target.path, "%s/%s", + pool->def->target.path, vol->name) == -1) + return -1; + + if (VIR_STRDUP(vol->key, vol->target.path) < 0) + goto cleanup; + + /** + * $ zfs create -o volmode=dev -V 10240K test/volname + * + * -o volmode=dev -- we want to get volumes exposed as cdev + * devices. If we don't specify that zfs + * will lookup vfs.zfs.vol.mode sysctl value + * -V -- tells to create a volume with the specified size + */ + cmd = virCommandNewArgList(ZFS, "create", "-o", "volmode=dev", + "-V", NULL); + virCommandAddArgFormat(cmd, "%lluK", + VIR_DIV_UP(vol->target.capacity, 1024)); + virCommandAddArgFormat(cmd, "%s/%s", + pool->def->source.name, vol->name); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + if (virStorageBackendZFSFindVols(pool) < 0) + goto cleanup; + + ret = 0; + cleanup: + virCommandFree(cmd); + return ret; + +} + +static int +virStorageBackendZFSDeleteVol(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + unsigned int flags) +{ + int ret = -1; + virCommandPtr destroy_cmd = virCommandNewArgList(ZFS, "destroy", NULL); + + virCheckFlags(0, -1); + + virCommandAddArgFormat(destroy_cmd, "%s/%s", + pool->def->source.name, vol->name); + + if (virCommandRun(destroy_cmd, NULL) < 0) + goto cleanup; + + ret = 0; + cleanup: + virCommandFree(destroy_cmd); + return ret; +} + + +virStorageBackend virStorageBackendZFS = { + .type = VIR_STORAGE_POOL_ZFS, + + .checkPool = virStorageBackendZFSCheckPool, + .refreshPool = virStorageBackendZFSRefreshPool, + .createVol = virStorageBackendZFSCreateVol, + .deleteVol = virStorageBackendZFSDeleteVol, +}; diff --git a/src/storage/storage_backend_zfs.h b/src/storage/storage_backend_zfs.h new file mode 100644 index 0000000..4c34b59 --- /dev/null +++ b/src/storage/storage_backend_zfs.h @@ -0,0 +1,29 @@ +/* + * storage_backend_zfs.h: storage backend for ZFS handling + * + * Copyright (C) 2014 Roman Bogorodskiy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __VIR_STORAGE_BACKEND_ZFS_H__ +# define __VIR_STORAGE_BACKEND_ZFS_H__ + +# include "storage_backend.h" + +extern virStorageBackend virStorageBackendZFS; + +#endif /* __VIR_STORAGE_BACKEND_ZFS_H__ */ diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index efbe5ff..47ff8a8 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -1431,6 +1431,7 @@ storageVolLookupByPath(virConnectPtr conn, case VIR_STORAGE_POOL_GLUSTER: case VIR_STORAGE_POOL_RBD: case VIR_STORAGE_POOL_SHEEPDOG: + case VIR_STORAGE_POOL_ZFS: case VIR_STORAGE_POOL_LAST: if (VIR_STRDUP(stable_path, path) < 0) { virStoragePoolObjUnlock(pool); diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c index 7c40b5b..80313b1 100644 --- a/tools/virsh-pool.c +++ b/tools/virsh-pool.c @@ -1063,6 +1063,9 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) case VIR_STORAGE_POOL_GLUSTER: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER; break; + case VIR_STORAGE_POOL_ZFS: + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_ZFS; + break; case VIR_STORAGE_POOL_LAST: break; } -- 1.9.0

On 07/26/2014 06:13 PM, Roman Bogorodskiy wrote:
Implement ZFS storage backend driver. Currently supported only on FreeBSD because of ZFS limitations on Linux.
Features supported:
- pool-start, pool-stop - pool-info - vol-list - vol-create / vol-delete
Pool definition looks like that:
<pool type='zfs'> <name>myzfspool</name> <source> <name>actualpoolname</name> </source> </pool>
The 'actualpoolname' value is a name of the pool on the system, such as shown by 'zpool list' command. Target makes no sense here because volumes path is always /dev/zvol/$poolname/$volname.
Users has to create a pool on his own, this driver doesn't
s/Users/User/
support pool creation currently.
A volume could be used with Qemu by adding an entry like this:
<disk type='volume' device='disk'> <driver name='qemu' type='raw'/> <source pool='myzfspool' volume='vol5'/> <target dev='hdc' bus='ide'/> </disk> --- configure.ac | 43 +++++ docs/schemas/storagepool.rng | 20 +++ docs/storage.html.in | 34 ++++ include/libvirt/libvirt.h.in | 1 + po/POTFILES.in | 1 + src/Makefile.am | 8 + src/conf/storage_conf.c | 15 +- src/conf/storage_conf.h | 4 +- src/qemu/qemu_conf.c | 1 + src/storage/storage_backend.c | 6 + src/storage/storage_backend_zfs.c | 329 ++++++++++++++++++++++++++++++++++++++ src/storage/storage_backend_zfs.h | 29 ++++ src/storage/storage_driver.c | 1 + tools/virsh-pool.c | 3 + 14 files changed, 492 insertions(+), 3 deletions(-) create mode 100644 src/storage/storage_backend_zfs.c create mode 100644 src/storage/storage_backend_zfs.h
ACK Jan

Ján Tomko wrote:
On 07/26/2014 06:13 PM, Roman Bogorodskiy wrote:
Implement ZFS storage backend driver. Currently supported only on FreeBSD because of ZFS limitations on Linux. --- configure.ac | 43 +++++ docs/schemas/storagepool.rng | 20 +++ docs/storage.html.in | 34 ++++ include/libvirt/libvirt.h.in | 1 + po/POTFILES.in | 1 + src/Makefile.am | 8 + src/conf/storage_conf.c | 15 +- src/conf/storage_conf.h | 4 +- src/qemu/qemu_conf.c | 1 + src/storage/storage_backend.c | 6 + src/storage/storage_backend_zfs.c | 329 ++++++++++++++++++++++++++++++++++++++ src/storage/storage_backend_zfs.h | 29 ++++ src/storage/storage_driver.c | 1 + tools/virsh-pool.c | 3 + 14 files changed, 492 insertions(+), 3 deletions(-) create mode 100644 src/storage/storage_backend_zfs.c create mode 100644 src/storage/storage_backend_zfs.h
ACK
Pushed, thanks! Roman Bogorodskiy

Roman Bogorodskiy wrote:
Changes from the initial version: - Update docs/schemas and docs/storage.html.in with ZFS backend information - Drop StartPool and StopPool that does nothing and therefore not needed - Fix memory leak in 'tokens' variable - Fill volume key before creating a volume - Use volume's target.path as a key - Allow not to specify 'target' for pool because in any case it should be /dev/zvol/$poolname
Ping?
Few notes on Linux support:
As for the Linux support I think it doesn't make sense to include it until there's a release of zfsonlinux available that contains all the features required, but I'll keep testing on Linux as well to make sure it'd be easy to add Linux support once the release is available. Roman Bogorodskiy
participants (2)
-
Ján Tomko
-
Roman Bogorodskiy