VIR_STORAGE_POOL_LAST,
"dir", "fs", "netfs",
"logical", "disk", "iscsi",
- "scsi", "mpath")
+ "scsi", "mpath", "rbd")
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
VIR_STORAGE_POOL_FS_LAST,
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),
};
.formatToString = virStoragePoolFormatDiskTypeToString,
}
},
+ { .poolType = VIR_STORAGE_POOL_RBD,
+ .poolOptions = {
+ .flags = (VIR_STORAGE_POOL_SOURCE_HOST |
+ VIR_STORAGE_POOL_SOURCE_NETWORK),
+ },
+ .volOptions = {
+ .formatToString = virStoragePoolFormatDiskTypeToString,
+ }
+ },
{ .poolType = VIR_STORAGE_POOL_MPATH,
.volOptions = {
.formatToString = virStoragePoolFormatDiskTypeToString,
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.id);
+ VIR_FREE(source->auth.cephx.secret);
+ }
}
void
}
static int
+virStoragePoolDefParseAuthCephx(xmlXPathContextPtr ctxt,
+ virStoragePoolAuthCephxPtr auth) {
+ auth->id = virXPathString("string(./auth/@id)", ctxt);
+ if (auth->id == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing auth id attribute"));
+ return -1;
+ }
+
+ auth->secret = virXPathString("string(./auth/@secret)", ctxt);
+ if (auth->secret == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing auth secret attribute"));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
virStoragePoolSourcePtr source,
int pool_type,
int nsource, i;
virStoragePoolOptionsPtr options;
char *port = NULL;
+ char *prefer_ipv6 = NULL;
relnode = ctxt->node;
ctxt->node = node;
}
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);
goto cleanup;
}
}
-
+ prefer_ipv6 = virXPathString("string(./host/@prefer_ipv6)", ctxt);
+ if (prefer_ipv6) {
+ if (strcasecmp(prefer_ipv6, "yes") == 0 || strcasecmp(prefer_ipv6, "y") == 0 || strcasecmp(prefer_ipv6, "true") == 0) {
+ source->host.prefer_ipv6 = true;
+ }
+ }
source->initiator.iqn = virXPathString("string(./initiator/iqn/@name)", ctxt);
} else {
if (STREQ(authType, "chap")) {
source->authType = VIR_STORAGE_POOL_AUTH_CHAP;
+ } else if (STREQ(authType, "cephx")) {
+ source->authType = VIR_STORAGE_POOL_AUTH_CEPHX;
} else {
virStorageReportError(VIR_ERR_XML_ERROR,
_("unknown auth type '%s'"),
if (virStoragePoolDefParseAuthChap(ctxt, &source->auth.chap) < 0)
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);
}
}
+ /* 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"));
if (virStorageDefParsePerms(ctxt, &ret->target.perms,
"./target/permissions", 0700) < 0)
goto cleanup;
+ }
return ret;
src->auth.chap.login,
src->auth.chap.passwd);
+ if (src->authType == VIR_STORAGE_POOL_AUTH_CEPHX)
+ virBufferAsprintf(buf," <auth type='chephx' id='%s' secret='%s'/>\n",
+ src->auth.cephx.id,
+ src->auth.cephx.secret);
+
if (src->vendor != NULL) {
virBufferEscapeString(buf," <vendor name='%s'/>\n", src->vendor);
}
--- /dev/null
+/*
+ * storage_backend_rbd.c: storage backend for RBD (RADOS Block Device) handling
+ *
+ * Copyright (C) 2011 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@widodh.nl>
+ */
+
+#include <config.h>
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <stdio.h>
+#include <regex.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "virterror_internal.h"
+#include "storage_backend_rbd.h"
+#include "storage_conf.h"
+#include "util.h"
+#include "memory.h"
+#include "logging.h"
+#include "rados/librados.h"
+#include "rbd/librbd.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+/*
+ * We use the build in secret management from libvirt
+ * In all methods we use conn->secretDriver to retreive the credentials to connect to the Ceph cluster
+ * This information could then also be passed down to the domain/volume configuration
+ */
+
+struct _virStorageBackendRBDPtr {
+ rados_t cluster;
+ rados_ioctx_t ioctx ;
+};
+
+typedef struct _virStorageBackendRBDPtr virStorageBackendRBDPtr;
+
+static int virStorageBackendRBDOpenRADOSConn(virStorageBackendRBDPtr *ptr, virConnectPtr conn, virStoragePoolObjPtr pool) {
+
+ if (pool->def->source.auth.cephx.secret != NULL && pool->def->source.auth.cephx.id != NULL) {
+ unsigned char *value;
+ size_t value_size;
+ char *rados_key;
+
+ rados_create(&ptr->cluster, pool->def->source.auth.cephx.id);
+ rados_conf_set(ptr->cluster, "auth_supported", "cephx");
+
+ virSecretPtr secret;
+ secret = virSecretLookupByUUIDString(conn, pool->def->source.auth.cephx.secret);
+
+ value = virSecretGetValue(secret, &value_size, 0);
+ base64_encode_alloc((char *)value, value_size, &rados_key);
+ VIR_FREE(value);
+
+ rados_conf_set(ptr->cluster, "key", rados_key);
+
+ VIR_FREE(rados_key);
+
+ virSecretFree(secret);
+ } else {
+ rados_create(&ptr->cluster, NULL);
+ rados_conf_set(ptr->cluster, "auth_supported", "none");
+ }
+
+ char mon_host[256];
+ if (pool->def->source.host.name != NULL && pool->def->source.host.port == NULL) {
+ sprintf(mon_host, "%s:6789", pool->def->source.host.name);
+ } else if (pool->def->source.host.name != NULL && pool->def->source.host.port != NULL) {
+ sprintf(mon_host, "%s:%d", pool->def->source.host.name, pool->def->source.host.port);
+ } else {
+ /* We should never get here! A hostname should always be present! */
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Missing mandatory monitor hostname, this should not happen!"));
+ return -1;
+ }
+
+ rados_conf_set(ptr->cluster, "mon_host", mon_host);
+
+ if (pool->def->source.host.prefer_ipv6) {
+ rados_conf_set(ptr->cluster, "ms_bind_ipv6", "true");
+ }
+
+ if (rados_connect(ptr->cluster) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to connect to the RADOS cluster on hostname '%s'"),
+ mon_host);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int virStorageBackendRBDCloseRADOSConn(virStorageBackendRBDPtr ptr) {
+ if (ptr.ioctx != NULL) {
+ rados_ioctx_destroy(ptr.ioctx);
+ }
+
+ if (ptr.cluster != NULL) {
+ rados_shutdown(ptr.cluster);
+ }
+ return 0;
+}
+
+static int volStorageBackendRBDRefreshVolInfo(virStorageVolDefPtr vol, virStoragePoolObjPtr pool, virStorageBackendRBDPtr ptr) {
+
+ 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);
+ return -1;
+ }
+
+ 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"));
+ return -1;
+ }
+
+ 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, "%s/%s",
+ pool->def->source.name,
+ vol->name) == -1) {
+ virReportOOMError();
+ return -1;
+ }
+
+ VIR_FREE(vol->key);
+ vol->key = strdup(vol->target.path);
+ if (vol->key == NULL) {
+ virReportOOMError();
+ return -1;
+ }
+
+ rbd_close(image);
+}
+
+static int virStorageBackendRBDFindPoolSources(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, char **const groups, void *data) {
+ virStoragePoolSourceList sourceList;
+ char *retval = NULL;
+
+ memset(&sourceList, 0, sizeof(sourceList));
+ sourceList.type = VIR_STORAGE_POOL_RBD;
+ sourceList.nsources = 1;
+ retval = virStoragePoolSourceListFormat(&sourceList);
+ return retval;
+}
+
+static int virStorageBackendRBDRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool) {
+
+ virStorageBackendRBDPtr ptr;
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
+ return -1;
+ }
+
+ 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);
+ return -1;
+ }
+
+ 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"));
+ return -1;
+ }
+
+ 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);
+ return -1;
+ }
+
+ pool->def->capacity = stat.kb * 1024;
+ pool->def->available = stat.kb_avail * 1024;
+ pool->def->allocation = poolstat.num_bytes;
+
+ VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1);
+
+ int num_images, i;
+ char *names, *name;
+ size_t max_size = 1024;
+
+ names = (char *) malloc(sizeof(char *) * 1024);
+ int len = rbd_list(ptr.ioctx, names, &max_size);
+
+ for (i = 0, num_images = 0, name = names; name < names + len; i++) {
+ virStorageVolDefPtr vol;
+ VIR_ALLOC(vol);
+
+ vol->name = strdup(name);
+ name += strlen(name) + 1;
+
+ volStorageBackendRBDRefreshVolInfo(vol, pool, ptr);
+ pool->volumes.objs[pool->volumes.count++] = vol;
+ }
+
+ virStorageBackendRBDCloseRADOSConn(ptr);
+
+ return 0;
+}
+
+static int virStorageBackendRBDDeleteVol(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags) {
+ virStorageBackendRBDPtr ptr;
+ rados_ioctx_t ioctx;
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
+ return -1;
+ }
+
+ if (rados_ioctx_create(ptr.cluster, pool->def->source.name, &ioctx) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
+ pool->def->source.name);
+ return -1;
+ }
+
+ if (rbd_remove(ioctx, vol->name) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to remove volume '%s/%s'"),
+ pool->def->source.name,
+ vol->name);
+ return -1;
+ }
+
+ rados_ioctx_destroy(ioctx);
+ virStorageBackendRBDCloseRADOSConn(ptr);
+ return 0;
+}
+
+static int virStorageBackendRBDCreateVol(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol) {
+ virStorageBackendRBDPtr ptr;
+ rados_ioctx_t ioctx;
+ int order = 0;
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
+ return -1;
+ }
+
+ if (rados_ioctx_create(ptr.cluster, pool->def->source.name, &ioctx) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
+ pool->def->source.name);
+ return -1;
+ }
+
+ if (vol->target.encryption != NULL) {
+ virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ "%s", _("storage pool does not support encrypted volumes"));
+ return -1;
+ }
+
+ vol->type = VIR_STORAGE_VOL_NETWORK;
+
+ if (rbd_create(ioctx, vol->name, vol->capacity, &order) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create volume '%s/%s'"),
+ pool->def->source.name,
+ vol->name);
+ return -1;
+ }
+
+ rados_ioctx_destroy(ioctx);
+ virStorageBackendRBDCloseRADOSConn(ptr);
+ return 0;
+}
+
+static int virStorageBackendRBDRefreshVol(virConnectPtr conn, virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, virStorageVolDefPtr vol) {
+ virStorageBackendRBDPtr ptr;
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
+ return -1;
+ }
+
+ 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);
+ return -1;
+ }
+
+ volStorageBackendRBDRefreshVolInfo(vol, pool, ptr);
+ virStorageBackendRBDCloseRADOSConn(ptr);
+
+ return 0;
+}
+
+static int virStorageBackendRBDBuildVol(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, virStorageVolDefPtr inputvol) {
+ return 0;
+}
+
+static int virStorageBackendRBDBuildVolFrom(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, virStorageVolDefPtr inputvol, unsigned int flags) {
+ return 0;
+}
+
+virStorageBackend virStorageBackendRBD = {
+ .type = VIR_STORAGE_POOL_RBD,
+
+ .findPoolSources = virStorageBackendRBDFindPoolSources,
+ .refreshPool = virStorageBackendRBDRefreshPool,
+ .buildVol = virStorageBackendRBDBuildVol,
+ .buildVolFrom = virStorageBackendRBDBuildVolFrom,
+ .createVol = virStorageBackendRBDCreateVol,
+ .refreshVol = virStorageBackendRBDRefreshVol,
+ .deleteVol = virStorageBackendRBDDeleteVol,
+};