From 85400fb9929b658b762ff2928007fcba501a7183 Mon Sep 17 00:00:00 2001 From: Josh Durgin Date: Tue, 7 Dec 2010 11:56:34 -0800 Subject: [PATCH] qemu: Add RBD support and some network disk fixes Changes common to all network disks: -Make source name optional in the domain schema, since NBD doesn't use it -Add a hostName type to the domain schema, and use it instead of genericName, which doesn't include . -Don't leak host names or ports -Set the source protocol in qemuParseCommandline Signed-off-by: Josh Durgin --- AUTHORS | 1 + docs/schemas/domain.rng | 11 +++- src/conf/domain_conf.c | 25 +++++-- src/conf/domain_conf.h | 1 + src/qemu/qemu_conf.c | 143 +++++++++++++++++++++++++++++++++++++--- 5 files changed, 166 insertions(+), 15 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7f5a87d5fb..2ee4f980c7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -139,6 +139,7 @@ Patches have also been contributed by: Hu Tao Laurent LĂ©onard MORITA Kazutaka + Josh Durgin [....send patches to get your name here....] diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 44638841d1..51aae14791 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -626,11 +626,13 @@ sheepdog - + + + - + @@ -2024,6 +2026,11 @@ 1 + + + [a-zA-Z0-9\.\-]+ + + -1 diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 4d11856e96..5b0fd55720 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -509,21 +509,34 @@ void virDomainInputDefFree(virDomainInputDefPtr def) void virDomainDiskDefFree(virDomainDiskDefPtr def) { + unsigned int i; + if (!def) return; VIR_FREE(def->serial); VIR_FREE(def->src); - VIR_FREE(def->hosts); VIR_FREE(def->dst); VIR_FREE(def->driverName); VIR_FREE(def->driverType); virStorageEncryptionFree(def->encryption); virDomainDeviceInfoClear(&def->info); + for (i = 0 ; i < def->nhosts ; i++) + virDomainDiskHostDefFree(&def->hosts[i]); + VIR_FREE(def); } +void virDomainDiskHostDefFree(virDomainDiskHostDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->name); + VIR_FREE(def->port); +} + void virDomainControllerDefFree(virDomainControllerDefPtr def) { if (!def) @@ -1644,7 +1657,12 @@ virDomainDiskDefParseXML(virCapsPtr caps, protocol); goto error; } - source = virXMLPropString(cur, "name"); + if (!(source = virXMLPropString(cur, "name")) && + def->protocol != VIR_DOMAIN_DISK_PROTOCOL_NBD) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("missing name for disk source")); + goto error; + } host = cur->children; while (host != NULL) { if (host->type == XML_ELEMENT_NODE && @@ -1877,8 +1895,7 @@ cleanup: VIR_FREE(target); VIR_FREE(source); while (nhosts > 0) { - VIR_FREE(hosts[nhosts - 1].name); - VIR_FREE(hosts[nhosts - 1].port); + virDomainDiskHostDefFree(&hosts[nhosts - 1]); nhosts--; } VIR_FREE(hosts); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index e62320db8b..a459a22fa6 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1071,6 +1071,7 @@ virDomainObjPtr virDomainFindByName(const virDomainObjListPtr doms, void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def); void virDomainInputDefFree(virDomainInputDefPtr def); void virDomainDiskDefFree(virDomainDiskDefPtr def); +void virDomainDiskHostDefFree(virDomainDiskHostDefPtr def); void virDomainControllerDefFree(virDomainControllerDefPtr def); void virDomainFSDefFree(virDomainFSDefPtr def); void virDomainNetDefFree(virDomainNetDefPtr def); diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index c14005eb26..fb0b29a244 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -4026,6 +4026,8 @@ qemudBuildCommandLine(virConnectPtr conn, int last_good_net = -1; bool hasHwVirt = false; virCommandPtr cmd; + bool has_rbd_hosts = false; + virBuffer rbd_hosts = VIR_BUFFER_INITIALIZER; uname_normalize(&ut); @@ -4566,6 +4568,7 @@ qemudBuildCommandLine(virConnectPtr conn, int bootable = 0; virDomainDiskDefPtr disk = def->disks[i]; int withDeviceArg = 0; + int j; /* Unless we have -device, then USB disks need special handling */ @@ -4615,6 +4618,27 @@ qemudBuildCommandLine(virConnectPtr conn, virCommandAddArg(cmd, optstr); VIR_FREE(optstr); + if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK && + disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_RBD) { + for (j = 0 ; j < disk->nhosts ; j++) { + if (!has_rbd_hosts) { + virBufferAddLit(&rbd_hosts, "-m "); + has_rbd_hosts = true; + } else { + virBufferAddLit(&rbd_hosts, ","); + } + virDomainDiskHostDefPtr host = &disk->hosts[j]; + if (host->port) { + virBufferVSprintf(&rbd_hosts, "%s:%s", + host->name, + host->port); + } else { + virBufferVSprintf(&rbd_hosts, "%s", + host->name); + } + } + } + if (withDeviceArg) { if (disk->bus == VIR_DOMAIN_DISK_BUS_FDC) { virCommandAddArg(cmd, "-global"); @@ -4637,6 +4661,7 @@ qemudBuildCommandLine(virConnectPtr conn, char dev[NAME_MAX]; char file[PATH_MAX]; virDomainDiskDefPtr disk = def->disks[i]; + int j; if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) { if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { @@ -4700,6 +4725,23 @@ qemudBuildCommandLine(virConnectPtr conn, break; case VIR_DOMAIN_DISK_PROTOCOL_RBD: snprintf(file, PATH_MAX, "rbd:%s,", disk->src); + for (j = 0 ; j < disk->nhosts ; j++) { + if (!has_rbd_hosts) { + virBufferAddLit(&rbd_hosts, "-m "); + has_rbd_hosts = true; + } else { + virBufferAddLit(&rbd_hosts, ","); + } + virDomainDiskHostDefPtr host = &disk->hosts[j]; + if (host->port) { + virBufferVSprintf(&rbd_hosts, "%s:%s", + host->name, + host->port); + } else { + virBufferVSprintf(&rbd_hosts, "%s", + host->name); + } + } break; case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: if (disk->nhosts == 0) @@ -4719,6 +4761,13 @@ qemudBuildCommandLine(virConnectPtr conn, } } + if (virBufferError(&rbd_hosts)) { + virBufferFreeAndReset(&rbd_hosts); + goto no_memory; + } + if (has_rbd_hosts) + virCommandAddEnvPair(cmd, "CEPH_ARGS", virBufferContentAndReset(&rbd_hosts)); + if (qemuCmdFlags & QEMUD_CMD_FLAG_FSDEV) { for (i = 0 ; i < def->nfss ; i++) { char *optstr; @@ -5488,6 +5537,7 @@ static int qemuStringToArgvEnv(const char *args, int envend; int i; const char *curr = args; + const char *start; const char **progenv = NULL; const char **progargv = NULL; @@ -5495,14 +5545,22 @@ static int qemuStringToArgvEnv(const char *args, while (curr && *curr != '\0') { char *arg; const char *next; - if (*curr == '\'') { - curr++; - next = strchr(curr, '\''); - } else if (*curr == '"') { - curr++; - next = strchr(curr, '"'); + + start = curr; + /* accept a space in CEPH_ARGS */ + if (STRPREFIX(curr, "CEPH_ARGS=-m ")) { + start += strlen("CEPH_ARGS=-m "); + } + if (*start == '\'') { + if (start == curr) + curr++; + next = strchr(start + 1, '\''); + } else if (*start == '"') { + if (start == curr) + curr++; + next = strchr(start + 1, '"'); } else { - next = strchr(curr, ' '); + next = strchr(start, ' '); } if (!next) next = strchr(curr, '\n'); @@ -5732,6 +5790,7 @@ qemuParseCommandLineDisk(virCapsPtr caps, char *host, *port; def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + def->protocol = VIR_DOMAIN_DISK_PROTOCOL_NBD; host = def->src + strlen("nbd:"); port = strchr(host, ':'); if (!port) { @@ -5763,6 +5822,7 @@ qemuParseCommandLineDisk(virCapsPtr caps, char *p = def->src; def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + def->protocol = VIR_DOMAIN_DISK_PROTOCOL_RBD; def->src = strdup(p + strlen("rbd:")); if (!def->src) { virReportOOMError(); @@ -5775,6 +5835,7 @@ qemuParseCommandLineDisk(virCapsPtr caps, char *port, *vdi; def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + def->protocol = VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG; def->src = strdup(p + strlen("sheepdog:")); if (!def->src) { virReportOOMError(); @@ -5890,7 +5951,8 @@ qemuParseCommandLineDisk(virCapsPtr caps, } if (!def->src && - def->device == VIR_DOMAIN_DISK_DEVICE_DISK) { + def->device == VIR_DOMAIN_DISK_DEVICE_DISK && + def->type != VIR_DOMAIN_DISK_TYPE_NETWORK) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("missing file parameter in drive '%s'"), val); virDomainDiskDefFree(def); @@ -6810,7 +6872,7 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, disk->src = NULL; break; case VIR_DOMAIN_DISK_PROTOCOL_RBD: - /* TODO: set monitor hostnames */ + /* handled later since the hosts for all disks are in CEPH_ARGS */ break; case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: /* disk->src must be [vdiname] or [host]:[port]:[vdiname] */ @@ -7137,6 +7199,69 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, } #undef WANT_VALUE + if (def->ndisks > 0) { + const char *ceph_args = qemuFindEnv(progenv, "CEPH_ARGS"); + if (ceph_args) { + char *hosts, *port, *saveptr, *token; + virDomainDiskDefPtr first_rbd_disk = NULL; + for (i = 0 ; i < def->ndisks ; i++) { + virDomainDiskDefPtr disk = def->disks[i]; + if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK && + disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_RBD) { + first_rbd_disk = disk; + break; + } + } + + if (!first_rbd_disk) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("CEPH_ARGS was set without an rbd disk")); + goto error; + } + + /* CEPH_ARGS should be: -m host1[:port1][,host2[:port2]]... */ + if (!STRPREFIX(ceph_args, "-m ")) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("could not parse CEPH_ARGS '%s'"), ceph_args); + goto error; + } + hosts = strdup(strchr(ceph_args, ' ') + 1); + if (!hosts) + goto no_memory; + first_rbd_disk->nhosts = 0; + token = strtok_r(hosts, ",", &saveptr); + while (token != NULL) { + if (VIR_REALLOC_N(first_rbd_disk->hosts, first_rbd_disk->nhosts + 1) < 0) { + VIR_FREE(hosts); + goto no_memory; + } + port = strchr(token, ':'); + if (port) { + *port++ = '\0'; + port = strdup(port); + if (!port) { + VIR_FREE(hosts); + goto no_memory; + } + } + first_rbd_disk->hosts[first_rbd_disk->nhosts].port = port; + first_rbd_disk->hosts[first_rbd_disk->nhosts].name = strdup(token); + if (!first_rbd_disk->hosts[first_rbd_disk->nhosts].name) { + VIR_FREE(hosts); + goto no_memory; + } + first_rbd_disk->nhosts++; + token = strtok_r(NULL, ",", &saveptr); + } + VIR_FREE(hosts); + + if (first_rbd_disk->nhosts == 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("found no rbd hosts in CEPH_ARGS '%s'"), ceph_args); + goto error; + } + } + } if (!nographics && def->ngraphics == 0) { virDomainGraphicsDefPtr sdl;