Signed-off-by: Wido den Hollander<wido@xxxxxxxxx>
---
configure.ac | 20 ++
include/libvirt/libvirt.h.in | 1 +
src/Makefile.am | 9 +
src/conf/storage_conf.c | 197 ++++++++++---
src/conf/storage_conf.h | 16 +
src/storage/storage_backend.c | 6 +
src/storage/storage_backend_rbd.c | 465 ++++++++++++++++++++++++++++++
src/storage/storage_backend_rbd.h | 30 ++
tests/storagepoolxml2xmlin/pool-rbd.xml | 11 +
tests/storagepoolxml2xmlout/pool-rbd.xml | 15 +
tools/virsh.c | 7 +
11 files changed, 734 insertions(+), 43 deletions(-)
create mode 100644 src/storage/storage_backend_rbd.c
create mode 100644 src/storage/storage_backend_rbd.h
create mode 100644 tests/storagepoolxml2xmlin/pool-rbd.xml
create mode 100644 tests/storagepoolxml2xmlout/pool-rbd.xml
diff --git a/configure.ac b/configure.ac
index 32cc8d0..b693e5b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1743,6 +1743,8 @@ AC_ARG_WITH([storage-mpath],
AC_HELP_STRING([--with-storage-mpath], [with mpath backend for the storage driver @<:@default=check@:>@]),[],[with_storage_mpath=check])
AC_ARG_WITH([storage-disk],
AC_HELP_STRING([--with-storage-disk], [with GPartd Disk backend for the storage driver @<:@default=check@:>@]),[],[with_storage_disk=check])
+AC_ARG_WITH([storage-rbd],
+ AC_HELP_STRING([--with-storage-rbd], [with RADOS Block Device backend for the storage driver @<:@default=check@:>@]),[],[with_storage_rbd=check])
if test "$with_libvirtd" = "no"; then
with_storage_dir=no
@@ -1752,6 +1754,7 @@ if test "$with_libvirtd" = "no"; then
with_storage_scsi=no
with_storage_mpath=no
with_storage_disk=no
+ with_storage_rbd=no
fi
if test "$with_storage_dir" = "yes" ; then
AC_DEFINE_UNQUOTED([WITH_STORAGE_DIR], 1, [whether directory backend for storage driver is enabled])
@@ -1910,6 +1913,22 @@ if test "$with_storage_mpath" = "check"; then
fi
AM_CONDITIONAL([WITH_STORAGE_MPATH], [test "$with_storage_mpath" = "yes"])
+if test "$with_storage_rbd" = "yes" || test "$with_storage_rbd" = "check"; then
+ AC_CHECK_HEADER([rbd/librbd.h], [LIBRBD_FOUND=yes; break;])
+
+ LIBRBD_LIBS="-lrbd -lrados -lcrypto"
+
+ if test "$LIBRBD_FOUND" = "yes"; then
+ with_storage_rbd=yes
+ LIBS="$LIBS $LIBRBD_LIBS"
+ else
+ with_storage_rbd=no
+ fi
+
+ AC_DEFINE_UNQUOTED([WITH_STORAGE_RBD], 1, [wether RBD backend for storage driver is enabled])
+fi
+AM_CONDITIONAL([WITH_STORAGE_RBD], [test "$with_storage_rbd" = "yes"])
+
LIBPARTED_CFLAGS=
LIBPARTED_LIBS=
if test "$with_storage_disk" = "yes" ||
@@ -2673,6 +2692,7 @@ AC_MSG_NOTICE([ iSCSI: $with_storage_iscsi])
AC_MSG_NOTICE([ SCSI: $with_storage_scsi])
AC_MSG_NOTICE([ mpath: $with_storage_mpath])
AC_MSG_NOTICE([ Disk: $with_storage_disk])
+AC_MSG_NOTICE([ RBD: $with_storage_rbd])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Security Drivers])
AC_MSG_NOTICE([])
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 499dcd4..ee1d5ec 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2324,6 +2324,7 @@ typedef enum {
VIR_STORAGE_VOL_FILE = 0, /* Regular file based volumes */
VIR_STORAGE_VOL_BLOCK = 1, /* Block based volumes */
VIR_STORAGE_VOL_DIR = 2, /* Directory-passthrough based volume */
+ VIR_STORAGE_VOL_NETWORK = 3, /* Network volumes like RBD (RADOS Block Device) */
#ifdef VIR_ENUM_SENTINELS
VIR_STORAGE_VOL_LAST
diff --git a/src/Makefile.am b/src/Makefile.am
index a2aae9d..e4457c3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -498,6 +498,9 @@ STORAGE_DRIVER_MPATH_SOURCES = \
STORAGE_DRIVER_DISK_SOURCES = \
storage/storage_backend_disk.h storage/storage_backend_disk.c
+STORAGE_DRIVER_RBD_SOURCES = \
+ storage/storage_backend_rbd.h storage/storage_backend_rbd.c
+
STORAGE_HELPER_DISK_SOURCES = \
storage/parthelper.c
@@ -1040,6 +1043,11 @@ if WITH_STORAGE_DISK
libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_DISK_SOURCES)
endif
+if WITH_STORAGE_RBD
+libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_RBD_SOURCES)
+libvirt_la_LIBADD += $(LIBRBD_LIBS)
+endif
+
if WITH_NODE_DEVICES
# Needed to keep automake quiet about conditionals
if WITH_DRIVER_MODULES
@@ -1139,6 +1147,7 @@ EXTRA_DIST += \
$(STORAGE_DRIVER_SCSI_SOURCES) \
$(STORAGE_DRIVER_MPATH_SOURCES) \
$(STORAGE_DRIVER_DISK_SOURCES) \
+ $(STORAGE_DRIVER_RBD_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 bdf6218..2a0b5eb 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -52,7 +52,7 @@ VIR_ENUM_IMPL(virStoragePool,
VIR_STORAGE_POOL_LAST,
"dir", "fs", "netfs",
"logical", "disk", "iscsi",
- "scsi", "mpath")
+ "scsi", "mpath", "rbd")
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
VIR_STORAGE_POOL_FS_LAST,
@@ -110,6 +110,7 @@ enum {
VIR_STORAGE_POOL_SOURCE_ADAPTER = (1<<3),
VIR_STORAGE_POOL_SOURCE_NAME = (1<<4),
VIR_STORAGE_POOL_SOURCE_INITIATOR_IQN = (1<<5),
+ VIR_STORAGE_POOL_SOURCE_NETWORK = (1<<6),
};
@@ -194,6 +195,15 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
.formatToString = virStoragePoolFormatDiskTypeToString,
}
},
+ { .poolType = VIR_STORAGE_POOL_RBD,
+ .poolOptions = {
+ .flags = (VIR_STORAGE_POOL_SOURCE_NETWORK |
+ VIR_STORAGE_POOL_SOURCE_NAME),
+ },
+ .volOptions = {
+ .formatToString = virStoragePoolFormatDiskTypeToString,
+ }
+ },
{ .poolType = VIR_STORAGE_POOL_MPATH,
.volOptions = {
.formatToString = virStoragePoolFormatDiskTypeToString,
@@ -277,6 +287,11 @@ virStoragePoolSourceClear(virStoragePoolSourcePtr source)
return;
VIR_FREE(source->host.name);
+ for (i = 0 ; i< source->nhost ; i++) {
+ VIR_FREE(source->hosts[i].name);
+ }
+ VIR_FREE(source->hosts);
+
for (i = 0 ; i< source->ndevice ; i++) {
VIR_FREE(source->devices[i].freeExtents);
VIR_FREE(source->devices[i].path);
@@ -293,6 +308,12 @@ virStoragePoolSourceClear(virStoragePoolSourcePtr source)
VIR_FREE(source->auth.chap.login);
VIR_FREE(source->auth.chap.passwd);
}
+
+ if (source->authType == VIR_STORAGE_POOL_AUTH_CEPHX) {
+ VIR_FREE(source->auth.cephx.username);
+ VIR_FREE(source->auth.cephx.secret.uuid);
+ VIR_FREE(source->auth.cephx.secret.usage);
+ }
}
void
@@ -395,6 +416,27 @@ virStoragePoolDefParseAuthChap(xmlXPathContextPtr ctxt,
}
static int
+virStoragePoolDefParseAuthCephx(xmlXPathContextPtr ctxt,
+ virStoragePoolAuthCephxPtr auth) {
+ auth->username = virXPathString("string(./auth/@username)", ctxt);
+ if (auth->username == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing auth username attribute"));
+ return -1;
+ }
+
+ auth->secret.uuid = virXPathString("string(./auth/secret/@uuid)", ctxt);
+ auth->secret.usage = virXPathString("string(./auth/secret/@usage)", ctxt);
+ if (auth->secret.uuid == NULL&& auth->secret.usage == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing auth secret uuid or usage attribute"));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
virStoragePoolSourcePtr source,
int pool_type,
@@ -414,6 +456,12 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
}
source->name = virXPathString("string(./name)", ctxt);
+ if (pool_type == VIR_STORAGE_POOL_RBD&& source->name == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ _("%s"), "missing mandatory 'name' field for RBD pool name");
+ VIR_FREE(source->name);
+ goto cleanup;
+ }
if (options->formatFromString) {
char *format = virXPathString("string(./format/@type)", ctxt);
@@ -431,17 +479,39 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
VIR_FREE(format);
}
- source->host.name = virXPathString("string(./host/@name)", ctxt);
- port = virXPathString("string(./host/@port)", ctxt);
- if (port) {
- if (virStrToLong_i(port, NULL, 10,&source->host.port)< 0) {
- virStorageReportError(VIR_ERR_XML_ERROR,
- _("Invalid port number: %s"),
- port);
+ source->nhost = virXPathNodeSet("./host", ctxt,&nodeset);
+
+ if (source->nhost) {
+ if (VIR_ALLOC_N(source->hosts, source->nhost)< 0) {
+ virReportOOMError();
goto cleanup;
}
- }
+ for (i = 0 ; i< source->nhost ; i++) {
+ char *name = virXMLPropString(nodeset[i], "name");
+ if (name == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing storage pool host name"));
+ goto cleanup;
+ }
+ source->hosts[i].name = name;
+ if(i == 0&& source->nhost == 1)
+ source->host.name = name;
+
+ port = virXMLPropString(nodeset[i], "port");
+ if (port) {
+ if (virStrToLong_i(port, NULL, 10,&source->hosts[i].port)< 0) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ _("Invalid port number: %s"),
+ port);
+ goto cleanup;
+ } else {
+ if (i == 0&& source->nhost == 1)
+ virStrToLong_i(port, NULL, 10,&source->host.port);
+ }
+ }
+ }
+ }
source->initiator.iqn = virXPathString("string(./initiator/iqn/@name)", ctxt);
@@ -478,6 +548,8 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
} else {
if (STREQ(authType, "chap")) {
source->authType = VIR_STORAGE_POOL_AUTH_CHAP;
+ } else if (STREQ(authType, "ceph")) {
+ source->authType = VIR_STORAGE_POOL_AUTH_CEPHX;
} else {
virStorageReportError(VIR_ERR_XML_ERROR,
_("unknown auth type '%s'"),
@@ -491,6 +563,11 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
goto cleanup;
}
+ if (source->authType == VIR_STORAGE_POOL_AUTH_CEPHX) {
+ if (virStoragePoolDefParseAuthCephx(ctxt,&source->auth.cephx)< 0)
+ goto cleanup;
+ }
+
source->vendor = virXPathString("string(./vendor/@name)", ctxt);
source->product = virXPathString("string(./product/@name)", ctxt);
@@ -682,6 +759,15 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) {
}
}
+ if (options->flags& VIR_STORAGE_POOL_SOURCE_NETWORK) {
+ if (!ret->source.host.name&& ret->source.nhost< 1) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s",
+ _("missing storage pool source network host name"));
+ goto cleanup;
+ }
+ }
+
if (options->flags& VIR_STORAGE_POOL_SOURCE_DIR) {
if (!ret->source.dir) {
virStorageReportError(VIR_ERR_XML_ERROR,
@@ -717,20 +803,22 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) {
}
}
- if ((tmppath = virXPathString("string(./target/path)", ctxt)) == NULL) {
- virStorageReportError(VIR_ERR_XML_ERROR,
- "%s", _("missing storage pool target path"));
- goto cleanup;
- }
- ret->target.path = virFileSanitizePath(tmppath);
- VIR_FREE(tmppath);
- if (!ret->target.path)
- goto cleanup;
-
+ /* When we are working with a virtual disk we can skip the target path and permissions */
+ if (!(options->flags& VIR_STORAGE_POOL_SOURCE_NETWORK)) {
+ if ((tmppath = virXPathString("string(./target/path)", ctxt)) == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing storage pool target path"));
+ goto cleanup;
+ }
+ ret->target.path = virFileSanitizePath(tmppath);
+ VIR_FREE(tmppath);
+ if (!ret->target.path)
+ goto cleanup;
- if (virStorageDefParsePerms(ctxt,&ret->target.perms,
- "./target/permissions", 0700)< 0)
- goto cleanup;
+ if (virStorageDefParsePerms(ctxt,&ret->target.perms,
+ "./target/permissions", 0700)< 0)
+ goto cleanup;
+ }
return ret;
@@ -800,12 +888,15 @@ virStoragePoolSourceFormat(virBufferPtr buf,
int i, j;
virBufferAddLit(buf,"<source>\n");
- if ((options->flags& VIR_STORAGE_POOL_SOURCE_HOST)&&
- src->host.name) {
- virBufferAsprintf(buf, "<host name='%s'", src->host.name);
- if (src->host.port)
- virBufferAsprintf(buf, " port='%d'", src->host.port);
- virBufferAddLit(buf, "/>\n");
+ if ((options->flags& VIR_STORAGE_POOL_SOURCE_HOST ||
+ options->flags& VIR_STORAGE_POOL_SOURCE_NETWORK)&&
+ src->nhost) {
+ for (i = 0; i< src->nhost; i++) {
+ virBufferAsprintf(buf, "<host name='%s'", src->hosts[i].name);
+ if (src->hosts[i].port)
+ virBufferAsprintf(buf, " port='%d'", src->hosts[i].port);
+ virBufferAddLit(buf, "/>\n");
+ }
}
if ((options->flags& VIR_STORAGE_POOL_SOURCE_DEVICE)&&
@@ -860,6 +951,23 @@ virStoragePoolSourceFormat(virBufferPtr buf,
src->auth.chap.login,
src->auth.chap.passwd);
+ if (src->authType == VIR_STORAGE_POOL_AUTH_CEPHX) {
+ virBufferAsprintf(buf,"<auth username='%s' type='ceph'>\n",
+ src->auth.cephx.username);
+
+ virBufferAsprintf(buf," %s", "<secret");
+ if (src->auth.cephx.secret.uuid != NULL) {
+ virBufferAsprintf(buf," uuid='%s'", src->auth.cephx.secret.uuid);
+ }
+
+ if (src->auth.cephx.secret.usage != NULL) {
+ virBufferAsprintf(buf," usage='%s'", src->auth.cephx.secret.usage);
+ }
+ virBufferAsprintf(buf,"%s", "/>\n");
+
+ virBufferAsprintf(buf," %s", "</auth>\n");
+ }
+
if (src->vendor != NULL) {
virBufferEscapeString(buf,"<vendor name='%s'/>\n", src->vendor);
}
@@ -907,25 +1015,28 @@ virStoragePoolDefFormat(virStoragePoolDefPtr def) {
if (virStoragePoolSourceFormat(&buf, options,&def->source)< 0)
goto cleanup;
- virBufferAddLit(&buf,"<target>\n");
+ /* RBD devices are no local block devs nor files, so it doesn't have a target */
+ if (def->type != VIR_STORAGE_POOL_RBD) {
+ virBufferAddLit(&buf,"<target>\n");
- if (def->target.path)
- virBufferAsprintf(&buf,"<path>%s</path>\n", def->target.path);
+ if (def->target.path)
+ virBufferAsprintf(&buf,"<path>%s</path>\n", def->target.path);
- virBufferAddLit(&buf,"<permissions>\n");
- virBufferAsprintf(&buf,"<mode>0%o</mode>\n",
- def->target.perms.mode);
- virBufferAsprintf(&buf,"<owner>%d</owner>\n",
- def->target.perms.uid);
- virBufferAsprintf(&buf,"<group>%d</group>\n",
- def->target.perms.gid);
+ virBufferAddLit(&buf,"<permissions>\n");
+ virBufferAsprintf(&buf,"<mode>0%o</mode>\n",
+ def->target.perms.mode);
+ virBufferAsprintf(&buf,"<owner>%d</owner>\n",
+ def->target.perms.uid);
+ virBufferAsprintf(&buf,"<group>%d</group>\n",
+ def->target.perms.gid);
- if (def->target.perms.label)
- virBufferAsprintf(&buf,"<label>%s</label>\n",
- def->target.perms.label);
+ if (def->target.perms.label)
+ virBufferAsprintf(&buf,"<label>%s</label>\n",
+ def->target.perms.label);
- virBufferAddLit(&buf,"</permissions>\n");
- virBufferAddLit(&buf,"</target>\n");
+ virBufferAddLit(&buf,"</permissions>\n");
+ virBufferAddLit(&buf,"</target>\n");
+ }
virBufferAddLit(&buf,"</pool>\n");
if (virBufferError(&buf))
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
index 1ef9295..6b91ca5 100644
--- a/src/conf/storage_conf.h
+++ b/src/conf/storage_conf.h
@@ -120,6 +120,7 @@ enum virStoragePoolType {
VIR_STORAGE_POOL_ISCSI, /* iSCSI targets */
VIR_STORAGE_POOL_SCSI, /* SCSI HBA */
VIR_STORAGE_POOL_MPATH, /* Multipath devices */
+ VIR_STORAGE_POOL_RBD, /* RADOS Block Device */
VIR_STORAGE_POOL_LAST,
};
@@ -137,6 +138,7 @@ enum virStoragePoolDeviceType {
enum virStoragePoolAuthType {
VIR_STORAGE_POOL_AUTH_NONE,
VIR_STORAGE_POOL_AUTH_CHAP,
+ VIR_STORAGE_POOL_AUTH_CEPHX,
};
typedef struct _virStoragePoolAuthChap virStoragePoolAuthChap;
@@ -146,6 +148,15 @@ struct _virStoragePoolAuthChap {
char *passwd;
};
+typedef struct _virStoragePoolAuthCephx virStoragePoolAuthCephx;
+typedef virStoragePoolAuthCephx *virStoragePoolAuthCephxPtr;
+struct _virStoragePoolAuthCephx {
+ char *username;
+ struct {
+ char *uuid;
+ char *usage;
+ } secret;
+};
/*
* For remote pools, info on how to reach the host
@@ -215,6 +226,10 @@ struct _virStoragePoolSource {
/* An optional host */
virStoragePoolSourceHost host;
+ /* Or multiple hosts */
+ int nhost;
+ virStoragePoolSourceHostPtr hosts;
+
/* And either one or more devices ... */
int ndevice;
virStoragePoolSourceDevicePtr devices;
@@ -234,6 +249,7 @@ struct _virStoragePoolSource {
int authType; /* virStoragePoolAuthType */
union {
virStoragePoolAuthChap chap;
+ virStoragePoolAuthCephx cephx;
} auth;
/* Vendor of the source */
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c
index caac2f8..e2e9b51 100644
--- a/src/storage/storage_backend.c
+++ b/src/storage/storage_backend.c
@@ -77,6 +77,9 @@
#if WITH_STORAGE_DIR
# include "storage_backend_fs.h"
#endif
+#if WITH_STORAGE_RBD
+# include "storage_backend_rbd.h"
+#endif
#define VIR_FROM_THIS VIR_FROM_STORAGE
@@ -103,6 +106,9 @@ static virStorageBackendPtr backends[] = {
#if WITH_STORAGE_DISK
&virStorageBackendDisk,
#endif
+#if WITH_STORAGE_RBD
+&virStorageBackendRBD,
+#endif
NULL
};
diff --git a/src/storage/storage_backend_rbd.c b/src/storage/storage_backend_rbd.c
new file mode 100644
index 0000000..056059a
--- /dev/null
+++ b/src/storage/storage_backend_rbd.c
@@ -0,0 +1,465 @@
+/*
+ * storage_backend_rbd.c: storage backend for RBD (RADOS Block Device) handling
+ *
+ * Copyright (C) 2012 Wido den Hollander
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Wido den Hollander<wido@xxxxxxxxx>
+ */
+
+#include<config.h>
+
+#include "virterror_internal.h"
+#include "storage_backend_rbd.h"
+#include "storage_conf.h"
+#include "util.h"
+#include "memory.h"
+#include "logging.h"
+#include "base64.h"
+#include "rados/librados.h"
+#include "rbd/librbd.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+struct _virStorageBackendRBDState {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ time_t starttime;
+};
+
+typedef struct _virStorageBackendRBDState virStorageBackendRBDState;
+typedef virStorageBackendRBDState virStorageBackendRBDStatePtr;
+
+static int virStorageBackendRBDOpenRADOSConn(virStorageBackendRBDStatePtr *ptr,
+ virConnectPtr conn,
+ virStoragePoolObjPtr pool)
+{
+ int ret = -1;
+ unsigned char *secret_value;
+ size_t secret_value_size;
+ char *rados_key;
+ virBuffer mon_host = VIR_BUFFER_INITIALIZER;
+ virSecretPtr secret = NULL;
+
+ VIR_DEBUG("Found Cephx username: %s",
+ pool->def->source.auth.cephx.username);
+
+ if (pool->def->source.auth.cephx.username != NULL) {
+ VIR_DEBUG("Using cephx authorization");
+ if (rados_create(&ptr->cluster,
+ pool->def->source.auth.cephx.username)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to initialize RADOS"));
+ goto cleanup;
+ }
+
+ if (pool->def->source.auth.cephx.secret.uuid != NULL) {
+ VIR_DEBUG("Looking up secret by UUID: %s",
+ pool->def->source.auth.cephx.secret.uuid);
+ secret = virSecretLookupByUUIDString(conn,
+ pool->def->source.auth.cephx.secret.uuid);
+ }
+
+ if (pool->def->source.auth.cephx.secret.usage != NULL) {
+ VIR_DEBUG("Looking up secret by usage: %s",
+ pool->def->source.auth.cephx.secret.usage);
+ secret = virSecretLookupByUsage(conn, VIR_SECRET_USAGE_TYPE_CEPH,
+ pool->def->source.auth.cephx.secret.usage);
+ }
+
+ if (secret == NULL) {
+ virStorageReportError(VIR_ERR_NO_SECRET,
+ _("failed to find the secret"));
+ goto cleanup;
+ }
+
+ secret_value = virSecretGetValue(secret,&secret_value_size, 0);
+ base64_encode_alloc((char *)secret_value,
+ secret_value_size,&rados_key);
+ memset(secret_value, 0, secret_value_size);
+
+ if (rados_key == NULL) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to decode the RADOS key"));
+ goto cleanup;
+ }
+
+ VIR_DEBUG("Found cephx key: %s", rados_key);
+ if (rados_conf_set(ptr->cluster, "key", rados_key)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to set RADOS option: %s"),
+ "rados_key");
+ goto cleanup;
+ }
+
+ memset(rados_key, 0, strlen(rados_key));
+
+ if (rados_conf_set(ptr->cluster, "auth_supported", "cephx")< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to set RADOS option: %s"),
+ "auth_supported");
+ goto cleanup;
+ }
+ } else {
+ VIR_DEBUG("Not using cephx authorization");
+ if (rados_conf_set(ptr->cluster, "auth_supported", "none")< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to set RADOS option: %s"),
+ "auth_supported");
+ goto cleanup;
+ }
+ if (rados_create(&ptr->cluster, NULL)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RADOS cluster"));
+ goto cleanup;
+ }
+ }
+
+ VIR_DEBUG("Found %d RADOS cluster monitors in the pool configuration",
+ pool->def->source.nhost);
+
+ int i;
+ for (i = 0; i< pool->def->source.nhost; i++) {
+ if (pool->def->source.hosts[i].name != NULL&&
+ !pool->def->source.hosts[i].port) {
+ virBufferAsprintf(&mon_host, "%s:6789,",
+ pool->def->source.hosts[i].name);
+ } else if (pool->def->source.hosts[i].name != NULL&&
+ pool->def->source.hosts[i].port) {
+ virBufferAsprintf(&mon_host, "%s:%d,",
+ pool->def->source.hosts[i].name,
+ pool->def->source.hosts[i].port);
+ } else {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("received malformed monitor, check the XML definition"));
+ }
+ }
+
+ char *mon_buff = virBufferContentAndReset(&mon_host);
+ VIR_DEBUG("RADOS mon_host has been set to: %s", mon_buff);
+ if (rados_conf_set(ptr->cluster, "mon_host", mon_buff)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to set RADOS option: %s"),
+ "mon_host");
+ goto cleanup;
+ }
+
+ ptr->starttime = time(0);
+ if (rados_connect(ptr->cluster)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to connect to the RADOS monitor on: %s"),
+ virBufferContentAndReset(&mon_host));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(secret_value);
+ VIR_FREE(rados_key);
+ virSecretFree(secret);
+ virBufferFreeAndReset(&mon_host);
+ return ret;
+}
+
+static int virStorageBackendRBDCloseRADOSConn(virStorageBackendRBDStatePtr ptr)
+{
+ int ret = 0;
+
+ if (ptr.ioctx != NULL) {
+ VIR_DEBUG("Closing RADOS IoCTX");
+ rados_ioctx_destroy(ptr.ioctx);
+ ret = -1;
+ }
+
+ if (ptr.cluster != NULL) {
+ VIR_DEBUG("Closing RADOS connection");
+ rados_shutdown(ptr.cluster);
+ ret = -2;
+ }
+
+ time_t runtime = time(0) - ptr.starttime;
+ VIR_DEBUG("RADOS connection existed for %ld seconds", runtime);
+
+ return ret;
+}
+
+static int volStorageBackendRBDRefreshVolInfo(virStorageVolDefPtr vol,
+ virStoragePoolObjPtr pool,
+ virStorageBackendRBDStatePtr ptr)
+{
+ int ret = -1;
+ rbd_image_t image;
+ if (rbd_open(ptr.ioctx, vol->name,&image, NULL)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to open the RBD image '%s'"),
+ vol->name);
+ goto cleanup;
+ }
+
+ rbd_image_info_t info;
+ if (rbd_stat(image,&info, sizeof(info))< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to stat the RBD image"));
+ goto cleanup;
+ }
+
+ VIR_DEBUG("Refreshed RBD image %s/%s (size: %llu obj_size: %llu num_objs: %llu)",
+ pool->def->source.name, vol->name, (unsigned long long)info.size,
+ (unsigned long long)info.obj_size,
+ (unsigned long long)info.num_objs);
+
+ vol->capacity = info.size;
+ vol->allocation = info.obj_size * info.num_objs;
+ vol->type = VIR_STORAGE_VOL_NETWORK;
+
+ VIR_FREE(vol->target.path);
+ if (virAsprintf(&vol->target.path, "rbd:%s/%s",
+ pool->def->source.name,
+ vol->name) == -1) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ VIR_FREE(vol->key);
+ if (virAsprintf(&vol->key, "%s/%s",
+ pool->def->source.name,
+ vol->name) == -1) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ rbd_close(image);
+ return ret;
+}
+
+static int virStorageBackendRBDRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virStoragePoolObjPtr pool)
+{
+ size_t max_size = 1024;
+ int ret = -1;
+ virStorageBackendRBDStatePtr ptr;
+ ptr.cluster = NULL;
+ ptr.ioctx = NULL;
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool)< 0) {
+ goto cleanup;
+ }
+
+ if (rados_ioctx_create(ptr.cluster,
+ pool->def->source.name,&ptr.ioctx)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
+ pool->def->source.name);
+ goto cleanup;
+ }
+
+ struct rados_cluster_stat_t stat;
+ if (rados_cluster_stat(ptr.cluster,&stat)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to stat the RADOS cluster"));
+ goto cleanup;
+ }
+
+ struct rados_pool_stat_t poolstat;
+ if (rados_ioctx_pool_stat(ptr.ioctx,&poolstat)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to stat the RADOS pool '%s'"),
+ pool->def->source.name);
+ goto cleanup;
+ }
+
+ pool->def->capacity = stat.kb * 1024;
+ pool->def->available = stat.kb_avail * 1024;
+ pool->def->allocation = poolstat.num_bytes;
+
+ int num_images, i;
+ char *names, *name = NULL;
+
+ if (VIR_ALLOC_N(names, 1024)< 0)
+ goto cleanup;
+
+ int len = rbd_list(ptr.ioctx, names,&max_size);
+
+ for (i = 0, num_images = 0, name = names; name< names + len; i++) {
+
+ if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1)< 0) {
+ virStoragePoolObjClearVols(pool);
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ virStorageVolDefPtr vol;
+ if (VIR_ALLOC(vol)< 0)
+ goto cleanup;
+
+ vol->name = strdup(name);
+ if (vol->name == NULL)
+ goto cleanup;
+ name += strlen(name) + 1;
+
+ if (volStorageBackendRBDRefreshVolInfo(vol, pool, ptr)< 0)
+ goto cleanup;
+
+ pool->volumes.objs[pool->volumes.count++] = vol;
+ }
+
+ VIR_DEBUG("Refreshed RBD pool %s (kb: %llu kb_avail: %llu num_bytes: %llu num_images: %d)",
+ pool->def->source.name, (unsigned long long)stat.kb,
+ (unsigned long long)stat.kb_avail,
+ (unsigned long long)poolstat.num_bytes, pool->volumes.count);
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(names);
+ virStorageBackendRBDCloseRADOSConn(ptr);
+ return ret;
+}
+
+static int virStorageBackendRBDDeleteVol(virConnectPtr conn,
+ virStoragePoolObjPtr pool,
+ virStorageVolDefPtr vol,
+ unsigned int flags)
+{
+ int ret = -1;
+ virStorageBackendRBDStatePtr ptr;
+ ptr.cluster = NULL;
+ ptr.ioctx = NULL;
+
+ VIR_DEBUG("Removing RBD image %s/%s", pool->def->source.name, vol->name);
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool)< 0) {
+ goto cleanup;
+ }
+
+ if (rados_ioctx_create(ptr.cluster,
+ pool->def->source.name,&ptr.ioctx)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
+ pool->def->source.name);
+ goto cleanup;
+ }
+
+ if (rbd_remove(ptr.ioctx, vol->name)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to remove volume '%s/%s'"),
+ pool->def->source.name,
+ vol->name);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ virStorageBackendRBDCloseRADOSConn(ptr);
+ return ret;
+}
+
+static int virStorageBackendRBDCreateVol(virConnectPtr conn,
+ virStoragePoolObjPtr pool,
+ virStorageVolDefPtr vol)
+{
+ virStorageBackendRBDStatePtr ptr;
+ ptr.cluster = NULL;
+ ptr.ioctx = NULL;
+ int order = 0;
+ int ret = -1;
+
+ VIR_DEBUG("Creating RBD image %s/%s with size %llu",
+ pool->def->source.name,
+ vol->name, vol->capacity);
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool)< 0) {
+ goto cleanup;
+ }
+
+ if (rados_ioctx_create(ptr.cluster,
+ pool->def->source.name,&ptr.ioctx)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
+ pool->def->source.name);
+ goto cleanup;
+ }
+
+ if (vol->target.encryption != NULL) {
+ virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("storage pool does not support encrypted volumes"));
+ goto cleanup;
+ }
+
+ if (rbd_create(ptr.ioctx, vol->name, vol->capacity,&order)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create volume '%s/%s'"),
+ pool->def->source.name,
+ vol->name);
+ goto cleanup;
+ }
+
+ if (volStorageBackendRBDRefreshVolInfo(vol, pool, ptr)< 0) {
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ virStorageBackendRBDCloseRADOSConn(ptr);
+ return ret;
+}
+
+static int virStorageBackendRBDRefreshVol(virConnectPtr conn,
+ virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
+ virStorageVolDefPtr vol)
+{
+ virStorageBackendRBDStatePtr ptr;
+ ptr.cluster = NULL;
+ ptr.ioctx = NULL;
+ int ret = -1;
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool)< 0) {
+ goto cleanup;
+ }
+
+ if (rados_ioctx_create(ptr.cluster,
+ pool->def->source.name,&ptr.ioctx)< 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
+ pool->def->source.name);
+ goto cleanup;
+ }
+
+ if (volStorageBackendRBDRefreshVolInfo(vol, pool, ptr)< 0) {
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ virStorageBackendRBDCloseRADOSConn(ptr);
+ return ret;
+}
+
+virStorageBackend virStorageBackendRBD = {
+ .type = VIR_STORAGE_POOL_RBD,
+
+ .refreshPool = virStorageBackendRBDRefreshPool,
+ .createVol = virStorageBackendRBDCreateVol,
+ .refreshVol = virStorageBackendRBDRefreshVol,
+ .deleteVol = virStorageBackendRBDDeleteVol,
+};
diff --git a/src/storage/storage_backend_rbd.h b/src/storage/storage_backend_rbd.h
new file mode 100644
index 0000000..2ae2513
--- /dev/null
+++ b/src/storage/storage_backend_rbd.h
@@ -0,0 +1,30 @@
+/*
+ * storage_backend_rbd.h: storage backend for RBD (RADOS Block Device) handling
+ *
+ * Copyright (C) 2012 Wido den Hollander
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Wido den Hollander<wido@xxxxxxxxx>
+ */
+
+#ifndef __VIR_STORAGE_BACKEND_RBD_H__
+# define __VIR_STORAGE_BACKEND_RBD_H__
+
+# include "storage_backend.h"
+
+extern virStorageBackend virStorageBackendRBD;
+
+#endif /* __VIR_STORAGE_BACKEND_RBD_H__ */
diff --git a/tests/storagepoolxml2xmlin/pool-rbd.xml b/tests/storagepoolxml2xmlin/pool-rbd.xml
new file mode 100644
index 0000000..c9d4790
--- /dev/null
+++ b/tests/storagepoolxml2xmlin/pool-rbd.xml
@@ -0,0 +1,11 @@
+<pool type='rbd'>
+<name>ceph</name>
+<source>
+<name>rbd</name>
+<host name='localhost' port='6789'/>
+<host name='localhost' port='6790'/>
+<auth username='admin' type='ceph'>
+ <secret uuid='2ec115d7-3a88-3ceb-bc12-0ac909a6fd87' usage='admin'/>
+</auth>
+</source>
+</pool>
diff --git a/tests/storagepoolxml2xmlout/pool-rbd.xml b/tests/storagepoolxml2xmlout/pool-rbd.xml
new file mode 100644
index 0000000..fa7fb34
--- /dev/null
+++ b/tests/storagepoolxml2xmlout/pool-rbd.xml
@@ -0,0 +1,15 @@
+<pool type='rbd'>
+<name>ceph</name>
+<uuid>47c1faee-0207-e741-f5ae-d9b019b98fe2</uuid>
+<capacity unit='bytes'>0</capacity>
+<allocation unit='bytes'>0</allocation>
+<available unit='bytes'>0</available>
+<source>
+<host name='localhost' port='6789'/>
+<host name='localhost' port='6790'/>
+<name>rbd</name>
+<auth username='admin' type='ceph'>
+<secret uuid='2ec115d7-3a88-3ceb-bc12-0ac909a6fd87' usage='admin'/>
+</auth>
+</source>
+</pool>
diff --git a/tools/virsh.c b/tools/virsh.c
index 8ee25c3..632c75e 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -11894,6 +11894,10 @@ cmdVolInfo(vshControl *ctl, const vshCmd *cmd)
vshPrint(ctl, "%-15s %s\n", _("Type:"), _("dir"));
break;
+ case VIR_STORAGE_VOL_NETWORK:
+ vshPrint(ctl, "%-15s %s\n", _("Type:"), _("network"));
+ break;
+
default:
vshPrint(ctl, "%-15s %s\n", _("Type:"), _("unknown"));
}
@@ -19852,6 +19856,9 @@ vshShowVersion(vshControl *ctl ATTRIBUTE_UNUSED)
#ifdef WITH_STORAGE_LVM
vshPrint(ctl, " LVM");
#endif
+#ifdef WITH_STORAGE_RBD
+ vshPrint(ctl, " RBD");
+#endif
vshPrint(ctl, "\n");
vshPrint(ctl, "%s", _(" Miscellaneous:"));