This patch adds network disk support to libvirt/QEMU. The currently supported protcols are nbd, rbd, and sheepdog. The XML syntax is like this: <disk type="network" device="disk"> <driver name="qemu" type="raw" /> <source protocol='rbd|sheepdog|nbd' name="...some image identifier..."> <host name="mon1.example.org" port="6000"> <host name="mon2.example.org" port="6000"> <host name="mon3.example.org" port="6000"> </source> <target dev="vda" bus="virtio" /> </disk> Signed-off-by: MORITA Kazutaka <morita.kazutaka@xxxxxxxxxxxxx> --- This patch addresses the discussion on https://www.redhat.com/archives/libvir-list/2010-November/msg00759.html Josh mentioned that the monitor hostnames of RBD can be set through the environment variables, but I couldn't find any documentations about it, so the monitors are not set in this patch. I hope someone who is familiar with RBD implements it. I appreciate any feedback. Thanks, Kazutaka docs/schemas/domain.rng | 31 +++++++++ src/conf/domain_conf.c | 68 +++++++++++++++++-- src/conf/domain_conf.h | 20 ++++++ src/qemu/qemu_conf.c | 169 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 279 insertions(+), 9 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index fb44335..81f4004 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -612,6 +612,37 @@ <ref name="diskspec"/> </interleave> </group> + <group> + <attribute name="type"> + <value>network</value> + </attribute> + <interleave> + <optional> + <element name="source"> + <attribute name="protocol"> + <choice> + <value>nbd</value> + <value>rbd</value> + <value>sheepdog</value> + </choice> + </attribute> + <attribute name="name"/> + <zeroOrMore> + <element name="host"> + <attribute name="name"> + <ref name="genericName"/> + </attribute> + <attribute name="port"> + <ref name="unsignedInt"/> + </attribute> + </element> + </zeroOrMore> + <empty/> + </element> + </optional> + <ref name="diskspec"/> + </interleave> + </group> <ref name="diskspec"/> </choice> </element> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 3f14cee..b9dbc61 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -113,7 +113,8 @@ VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST, VIR_ENUM_IMPL(virDomainDisk, VIR_DOMAIN_DISK_TYPE_LAST, "block", "file", - "dir") + "dir", + "network") VIR_ENUM_IMPL(virDomainDiskDevice, VIR_DOMAIN_DISK_DEVICE_LAST, "disk", @@ -142,6 +143,11 @@ VIR_ENUM_IMPL(virDomainDiskErrorPolicy, VIR_DOMAIN_DISK_ERROR_POLICY_LAST, "ignore", "enospace") +VIR_ENUM_IMPL(virDomainDiskProtocol, VIR_DOMAIN_DISK_PROTOCOL_LAST, + "nbd", + "rbd", + "sheepdog") + VIR_ENUM_IMPL(virDomainController, VIR_DOMAIN_CONTROLLER_TYPE_LAST, "ide", "fdc", @@ -508,6 +514,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->serial); VIR_FREE(def->src); + VIR_FREE(def->hosts); VIR_FREE(def->dst); VIR_FREE(def->driverName); VIR_FREE(def->driverType); @@ -1574,13 +1581,15 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlNodePtr node, int flags) { virDomainDiskDefPtr def; - xmlNodePtr cur; + xmlNodePtr cur, host; char *type = NULL; char *device = NULL; char *driverName = NULL; char *driverType = NULL; char *source = NULL; char *target = NULL; + char *protocol = NULL; + virDomainDiskHostDefPtr hosts = NULL; char *bus = NULL; char *cachetag = NULL; char *error_policy = NULL; @@ -1607,7 +1616,7 @@ virDomainDiskDefParseXML(virCapsPtr caps, cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { - if ((source == NULL) && + if ((source == NULL && hosts == NULL) && (xmlStrEqual(cur->name, BAD_CAST "source"))) { switch (def->type) { @@ -1620,6 +1629,30 @@ virDomainDiskDefParseXML(virCapsPtr caps, case VIR_DOMAIN_DISK_TYPE_DIR: source = virXMLPropString(cur, "dir"); break; + case VIR_DOMAIN_DISK_TYPE_NETWORK: + protocol = virXMLPropString(cur, "protocol"); + if (protocol == NULL) { + virDomainReportError(VIR_ERR_XML_ERROR, + "%s", _("missing protocol type")); + break; + } + def->protocol = virDomainDiskProtocolTypeFromString(protocol); + source = virXMLPropString(cur, "name"); + host = cur->children; + while (host != NULL) { + if (host->type == XML_ELEMENT_NODE && + xmlStrEqual(host->name, BAD_CAST "host")) { + if (VIR_REALLOC_N(hosts, def->nhosts + 1) < 0) { + virReportOOMError(); + goto error; + } + hosts[def->nhosts].name = virXMLPropString(host, "name"); + hosts[def->nhosts].port = virXMLPropString(host, "port"); + def->nhosts++; + } + host = host->next; + } + break; default: virDomainReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected disk type %s"), @@ -1685,7 +1718,7 @@ virDomainDiskDefParseXML(virCapsPtr caps, /* Only CDROM and Floppy devices are allowed missing source path * to indicate no media present */ - if (source == NULL && + if (source == NULL && hosts == NULL && def->device != VIR_DOMAIN_DISK_DEVICE_CDROM && def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY) { virDomainReportError(VIR_ERR_NO_SOURCE, @@ -1791,6 +1824,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, source = NULL; def->dst = target; target = NULL; + def->hosts = hosts; + hosts = NULL; def->driverName = driverName; driverName = NULL; def->driverType = driverType; @@ -1819,6 +1854,8 @@ cleanup: VIR_FREE(type); VIR_FREE(target); VIR_FREE(source); + VIR_FREE(hosts); + VIR_FREE(protocol); VIR_FREE(device); VIR_FREE(driverType); VIR_FREE(driverName); @@ -5887,7 +5924,7 @@ virDomainDiskDefFormat(virBufferPtr buf, virBufferVSprintf(buf, "/>\n"); } - if (def->src) { + if (def->src || def->nhosts > 0) { switch (def->type) { case VIR_DOMAIN_DISK_TYPE_FILE: virBufferEscapeString(buf, " <source file='%s'/>\n", @@ -5901,6 +5938,27 @@ virDomainDiskDefFormat(virBufferPtr buf, virBufferEscapeString(buf, " <source dir='%s'/>\n", def->src); break; + case VIR_DOMAIN_DISK_TYPE_NETWORK: + virBufferVSprintf(buf, " <source protocol='%s'", + virDomainDiskProtocolTypeToString(def->protocol)); + if (def->src) { + virBufferEscapeString(buf, " name='%s'", def->src); + } + if (def->nhosts == 0) { + virBufferVSprintf(buf, "/>\n"); + } else { + int i; + + virBufferVSprintf(buf, ">\n"); + for (i = 0; i < def->nhosts; i++) { + virBufferEscapeString(buf, " <host name='%s'", + def->hosts[i].name); + virBufferEscapeString(buf, " port='%s'/>\n", + def->hosts[i].port); + } + virBufferVSprintf(buf, " </source>\n"); + } + break; default: virDomainReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected disk type %s"), diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 899b19f..6c97289 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -120,6 +120,7 @@ enum virDomainDiskType { VIR_DOMAIN_DISK_TYPE_BLOCK, VIR_DOMAIN_DISK_TYPE_FILE, VIR_DOMAIN_DISK_TYPE_DIR, + VIR_DOMAIN_DISK_TYPE_NETWORK, VIR_DOMAIN_DISK_TYPE_LAST }; @@ -164,6 +165,21 @@ enum virDomainDiskErrorPolicy { VIR_DOMAIN_DISK_ERROR_POLICY_LAST }; +enum virDomainDiskProtocol { + VIR_DOMAIN_DISK_PROTOCOL_NBD, + VIR_DOMAIN_DISK_PROTOCOL_RBD, + VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG, + + VIR_DOMAIN_DISK_PROTOCOL_LAST +}; + +typedef struct _virDomainDiskHostDef virDomainDiskHostDef; +typedef virDomainDiskHostDef *virDomainDiskHostDefPtr; +struct _virDomainDiskHostDef { + char *name; + char *port; +}; + /* Stores the virtual disk configuration */ typedef struct _virDomainDiskDef virDomainDiskDef; typedef virDomainDiskDef *virDomainDiskDefPtr; @@ -173,6 +189,9 @@ struct _virDomainDiskDef { int bus; char *src; char *dst; + int protocol; + int nhosts; + virDomainDiskHostDefPtr hosts; char *driverName; char *driverType; char *serial; @@ -1237,6 +1256,7 @@ VIR_ENUM_DECL(virDomainDiskDevice) VIR_ENUM_DECL(virDomainDiskBus) VIR_ENUM_DECL(virDomainDiskCache) VIR_ENUM_DECL(virDomainDiskErrorPolicy) +VIR_ENUM_DECL(virDomainDiskProtocol) VIR_ENUM_DECL(virDomainController) VIR_ENUM_DECL(virDomainControllerModel) VIR_ENUM_DECL(virDomainFS) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 35caccc..63abd75 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -2714,7 +2714,7 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk, break; } - if (disk->src) { + if (disk->src || disk->nhosts > 0) { if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { /* QEMU only supports magic FAT format for now */ if (disk->driverType && @@ -2733,6 +2733,24 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk, virBufferVSprintf(&opt, "file=fat:floppy:%s,", disk->src); else virBufferVSprintf(&opt, "file=fat:%s,", disk->src); + } else if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { + switch (disk->protocol) { + case VIR_DOMAIN_DISK_PROTOCOL_NBD: + virBufferVSprintf(&opt, "file=nbd:%s:%s,", + disk->hosts->name, disk->hosts->port); + break; + case VIR_DOMAIN_DISK_PROTOCOL_RBD: + virBufferVSprintf(&opt, "file=rbd:%s,", disk->src); + break; + case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: + if (disk->nhosts > 0) + virBufferVSprintf(&opt, "file=sheepdog:%s:%s:%s,", + disk->hosts->name, disk->hosts->port, + disk->src); + else + virBufferVSprintf(&opt, "file=sheepdog:%s,", disk->src); + break; + } } else { virBufferVSprintf(&opt, "file=%s,", disk->src); } @@ -4722,6 +4740,24 @@ int qemudBuildCommandLine(virConnectPtr conn, snprintf(file, PATH_MAX, "fat:floppy:%s", disk->src); else snprintf(file, PATH_MAX, "fat:%s", disk->src); + } else if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { + switch (disk->protocol) { + case VIR_DOMAIN_DISK_PROTOCOL_NBD: + snprintf(file, PATH_MAX, "nbd:%s:%s,", + disk->hosts->name, disk->hosts->port); + break; + case VIR_DOMAIN_DISK_PROTOCOL_RBD: + snprintf(file, PATH_MAX, "rbd:%s,", disk->src); + break; + case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: + if (disk->nhosts > 0) + snprintf(file, PATH_MAX, "sheepdog:%s:%s:%s,", + disk->hosts->name, disk->hosts->port, + disk->src); + else + snprintf(file, PATH_MAX, "sheepdog:%s,", disk->src); + break; + } } else { snprintf(file, PATH_MAX, "%s", disk->src); } @@ -5794,7 +5830,67 @@ qemuParseCommandLineDisk(virCapsPtr caps, values[i] = NULL; if (STRPREFIX(def->src, "/dev/")) def->type = VIR_DOMAIN_DISK_TYPE_BLOCK; - else + else if (STRPREFIX(def->src, "nbd:")) { + char *host, *port; + + def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + host = def->src + strlen("nbd:"); + port = strchr(host, ':'); + if (!port) { + def = NULL; + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse nbd filename '%s'"), def->src); + goto cleanup; + } + *port++ = '\0'; + if (VIR_ALLOC(def->hosts) < 0) { + virReportOOMError(); + goto cleanup; + } + def->nhosts = 1; + def->hosts->name = strdup(host); + def->hosts->port = strdup(port); + + VIR_FREE(def->src); + def->src = NULL; + } else if (STRPREFIX(def->src, "rbd:")) { + char *p = def->src; + + def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + def->src = strdup(p + strlen("rbd:")); + + VIR_FREE(p); + } else if (STRPREFIX(def->src, "sheepdog:")) { + char *p = def->src; + char *port, *vdi; + + def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + def->src = strdup(p + strlen("sheepdog:")); + + /* def->src must be [vdiname] or [host]:[port]:[vdiname] */ + port = strchr(def->src, ':'); + if (port) { + *port++ = '\0'; + vdi = strchr(port, ':'); + if (!vdi) { + def = NULL; + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse sheepdog filename '%s'"), p); + goto cleanup; + } + *vdi++ = '\0'; + if (VIR_ALLOC(def->hosts) < 0) { + virReportOOMError(); + goto cleanup; + } + def->nhosts = 1; + def->hosts->name = def->src; + def->hosts->port = strdup(port); + def->src = strdup(vdi); + } + + VIR_FREE(p); + } else def->type = VIR_DOMAIN_DISK_TYPE_FILE; } else { def->type = VIR_DOMAIN_DISK_TYPE_FILE; @@ -6731,7 +6827,19 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, if (STRPREFIX(val, "/dev/")) disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK; - else + else if (STRPREFIX(val, "nbd:")) { + disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_NBD; + val += strlen("nbd:"); + } else if (STRPREFIX(val, "rbd:")) { + disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_RBD; + val += strlen("rbd:"); + } else if (STRPREFIX(val, "sheepdog:")) { + disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG; + val += strlen("sheepdog:"); + } else disk->type = VIR_DOMAIN_DISK_TYPE_FILE; if (STREQ(arg, "-cdrom")) { disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; @@ -6751,7 +6859,60 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, disk->dst = strdup(arg + 1); } disk->src = strdup(val); - if (!disk->src || + + if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { + char *host, *port; + + switch (disk->protocol) { + case VIR_DOMAIN_DISK_PROTOCOL_NBD: + host = disk->src; + port = strchr(host, ':'); + if (!port) { + def = NULL; + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse nbd filename '%s'"), disk->src); + goto error; + } + *port++ = '\0'; + if (VIR_ALLOC(disk->hosts) < 0) { + virReportOOMError(); + goto error; + } + disk->nhosts = 1; + disk->hosts->name = host; + disk->hosts->port = strdup(port); + disk->src = NULL; + break; + case VIR_DOMAIN_DISK_PROTOCOL_RBD: + break; + case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: + /* disk->src must be [vdiname] or [host]:[port]:[vdiname] */ + port = strchr(disk->src, ':'); + if (port) { + char *vdi; + + *port++ = '\0'; + vdi = strchr(port, ':'); + if (!vdi) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse sheepdog filename '%s'"), val); + goto error; + } + *vdi++ = '\0'; + if (VIR_ALLOC(disk->hosts) < 0) { + virReportOOMError(); + goto error; + } + disk->nhosts = 1; + disk->hosts->name = disk->src; + disk->hosts->port = strdup(port); + disk->src = strdup(vdi); + } + break; + } + } + + if (!(disk->src || disk->nhosts > 0) || !disk->dst) { virDomainDiskDefFree(disk); goto no_memory; -- 1.7.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list