[libvirt] [RFC PATCH 0/n] Glue domain and storage

This is the 4th part to implement NPIV migration support [1]. Part 1: https://www.redhat.com/archives/libvir-list/2013-January/msg00859.html Part 2: (Already ACKed by Michal) https://www.redhat.com/archives/libvir-list/2013-January/msg00859.html Part 3: https://www.redhat.com/archives/libvir-list/2013-January/msg01012.html This set is based on above 3 ones. The subject "Glue domain and storage" is a bit bigger than what this series does. This set only tries to support using the storage for domain disks. And the storage might be further used by disk backing chains, or future scsi-host passthrough, etc, if the pricinple is correct. However, this can setup the basis, which can be extended for future stuffs. This introduces new XMLs to specify the disk source with storage like (See PATCH 3/n for more details): <disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <pool uuid|name="$var"/> <volume name|path|key='$var'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk> And before domain starting, the source represented by storage is translated into the real underlying source. Disk of type 'network' is not supported yet. But I'd like post the patches as soon as possible to make sure I'm not on the wrong road. Osier Yang (6): eng: Remove the duplicate definition rng: Change the datatype for volume name for common use Introduce new XMLs to specify disk source using storage pool/vol New files to glue domain and storage together Implement translateDiskSourcePool qemu: Translate the source when starting domain docs/formatdomain.html.in | 31 ++- docs/schemas/basictypes.rng | 6 + docs/schemas/domaincommon.rng | 156 +++++--- docs/schemas/storagepool.rng | 10 +- docs/schemas/storagevol.rng | 8 +- src/Makefile.am | 3 +- src/conf/domain_conf.c | 426 ++++++++++++++++---- src/conf/domain_conf.h | 46 +++ src/conf/domain_storage.c | 45 ++ src/conf/domain_storage.h | 37 ++ src/libvirt_private.syms | 6 + src/qemu/qemu_driver.c | 7 + src/storage/storage_driver.c | 207 ++++++++++ .../qemuxml2argv-disk-source-pool.xml | 36 ++ tests/qemuxml2xmltest.c | 1 + 15 files changed, 865 insertions(+), 160 deletions(-) create mode 100644 src/conf/domain_storage.c create mode 100644 src/conf/domain_storage.h create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-disk-source-pool.xml [1] https://www.redhat.com/archives/libvir-list/2012-November/msg00826.html Regards, Osier

The RE for data type "name" storagepool.rng uses is same with "genericName" in basictypes.rng. --- docs/schemas/storagepool.rng | 10 ++-------- 1 files changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/schemas/storagepool.rng b/docs/schemas/storagepool.rng index 983f664..165e276 100644 --- a/docs/schemas/storagepool.rng +++ b/docs/schemas/storagepool.rng @@ -144,7 +144,7 @@ <define name='commonmetadata'> <element name='name'> - <ref name='name'/> + <ref name='genericName'/> </element> <optional> <element name='uuid'> @@ -230,7 +230,7 @@ <attribute name='path'> <choice> <ref name='absFilePath'/> - <ref name='name'/> + <ref name='genericName'/> </choice> </attribute> <choice> @@ -519,12 +519,6 @@ </element> </define> - <define name='name'> - <data type='string'> - <param name="pattern">[a-zA-Z0-9_\+\-]+</param> - </data> - </define> - <define name="PortNumber"> <data type="short"> <param name="minInclusive">-1</param> -- 1.7.7.6

On 01/23/2013 04:04 AM, Osier Yang wrote:
The RE for data type "name" storagepool.rng uses is same with "genericName" in basictypes.rng. --- docs/schemas/storagepool.rng | 10 ++-------- 1 files changed, 2 insertions(+), 8 deletions(-)
ACK; this one can go in now. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 2013年01月26日 06:48, Eric Blake wrote:
On 01/23/2013 04:04 AM, Osier Yang wrote:
The RE for data type "name" storagepool.rng uses is same with "genericName" in basictypes.rng. --- docs/schemas/storagepool.rng | 10 ++-------- 1 files changed, 2 insertions(+), 8 deletions(-)
ACK; this one can go in now.
Thanks, pushed.

The "volName" will be used by later patch. --- docs/schemas/basictypes.rng | 6 ++++++ docs/schemas/storagevol.rng | 8 +------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng index 38cab16..15bae4e 100644 --- a/docs/schemas/basictypes.rng +++ b/docs/schemas/basictypes.rng @@ -219,4 +219,10 @@ </data> </define> + <define name='volName'> + <data type='string'> + <param name="pattern">[a-zA-Z0-9_\+\-\.]+</param> + </data> + </define> + </grammar> diff --git a/docs/schemas/storagevol.rng b/docs/schemas/storagevol.rng index 8335b61..10b7847 100644 --- a/docs/schemas/storagevol.rng +++ b/docs/schemas/storagevol.rng @@ -13,7 +13,7 @@ <define name='vol'> <element name='volume'> <element name='name'> - <ref name='name'/> + <ref name='volName'/> </element> <optional> <element name='key'> @@ -209,10 +209,4 @@ </optional> </define> - <define name='name'> - <data type='string'> - <param name="pattern">[a-zA-Z0-9_\+\-\.]+</param> - </data> - </define> - </grammar> -- 1.7.7.6

On 01/23/2013 04:04 AM, Osier Yang wrote:
The "volName" will be used by later patch. --- docs/schemas/basictypes.rng | 6 ++++++ docs/schemas/storagevol.rng | 8 +------- 2 files changed, 7 insertions(+), 7 deletions(-)
ACK; this one can go in now. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 2013年01月26日 06:49, Eric Blake wrote:
On 01/23/2013 04:04 AM, Osier Yang wrote:
The "volName" will be used by later patch. --- docs/schemas/basictypes.rng | 6 ++++++ docs/schemas/storagevol.rng | 8 +------- 2 files changed, 7 insertions(+), 7 deletions(-)
ACK; this one can go in now.
Thanks, pushed.

With this patch, one can specify the disk source using storage pool/volume like: <disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume key='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk> <disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume path='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk> <disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <pool uuid|name="$var"/> <volume name='foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk> Element <pool> is optional when the volume is specified by "path" or "key", but mandatory if the volume is specified by "name", since the volume name is not unique even locally (on host). docs/formatdomain.html.in: * Add documents for new XMLs docs/schemas/domaincommon.rng: * Add rng for new XMLs; * Abstract schema for network disk source as "diskSourceNetwork" src/conf/domain_conf.h: * New member source_type to indicate the disk source type * New struct for pool type disk source (virDomainDiskSourcePoolDef) * New enum virDomainDiskSourceType for disk source type (default|pool) * New enum virDomainDiskSourcePoolType to represent how the pool is specified (name|uuid) * New enum virDomainDiskSourceVolType to represent how the volume is specified (name|path|key) * VIR_ENUM_DECL eum virDomainDiskSourceType src/conf/domain_conf.c: * VIR_ENUM_IMPL enum virDomainDiskSourceType * New helper virDomainDiskSourcePoolDefParse to parse the 'pool' type source. * New helper virDomainDiskSourcePoolDefFree to free the 'pool' type source def * New helper virDomainDiskSourceDefFormat to format the disk source. src/libvirt_private.syms: * Export the symbols produced by enum virDomainDiskSourceType tests/qemuxml2argvdata/qemuxml2argv-disk-source-pool.xml: tests/qemuxml2xmltest.c: * New test --- docs/formatdomain.html.in | 31 ++- docs/schemas/domaincommon.rng | 156 +++++--- src/conf/domain_conf.c | 426 ++++++++++++++++---- src/conf/domain_conf.h | 46 +++ src/libvirt_private.syms | 2 + .../qemuxml2argv-disk-source-pool.xml | 36 ++ tests/qemuxml2xmltest.c | 1 + 7 files changed, 554 insertions(+), 144 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-disk-source-pool.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index bb0b199..f418edb 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1367,6 +1367,17 @@ <blockio logical_block_size='512' physical_block_size='4096'/> <target dev='hda' bus='ide'/> </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='raw'/> + <source type='pool'/> + <pool name='nfspool0'/> + <volume name='nfspool0-vol0'/> + </source> + <geometry cyls='16383' heads='16' secs='63' trans='lba'/> + <blockio logical_block_size='512' physical_block_size='4096'/> + <target dev='hda' bus='ide'/> + </disk> + </devices> ...</pre> @@ -1424,9 +1435,23 @@ "network" attribute since 0.8.7; "snapshot" since 0.9.5</span></dd> <dt><code>source</code></dt> - <dd>If the disk <code>type</code> is "file", then - the <code>file</code> attribute specifies the fully-qualified - path to the file holding the disk. If the disk + <dd>Attribute <code>type</code> (<span class="since">since 1.0.3</span>), + specifies the disk source type, the only valid value is 'pool'. + 'pool' source type has two sub-elements: <code>pool</code> and + <code>volume</code> (both <span class="since">since 1.0.3</span>). + Element <code>pool</code> specifies the storage pool (managed by + libvirt) where the disk source resides, it has two exclusive + attributes: <code>name</code> and <code>uuid</code>. Element + <code>volume</code> specifies the storage volume (managed by libvirt) + that which as the disk source, it has three exclusive attributes: + <code>name</code>, <code>path</code>, and <code>key</code>. + <code>pool</code> must be specified by either <code>name</code> or + <code>uuid</code> if <code>volume</code> is specified by + <code>name</code>. + Except the 'pool' type, one can also specify the disk source + straightforward with following methods: If the disk <code>type</code> + is "file", then the <code>file</code> attribute specifies the + fully-qualified path to the file holding the disk. If the disk <code>type</code> is "block", then the <code>dev</code> attribute specifies the path to the host device to serve as the disk. With both "file" and "block", one or more optional diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 67ae864..b221647 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -1018,11 +1018,14 @@ <interleave> <optional> <element name="source"> - <optional> - <attribute name="file"> - <ref name="absFilePath"/> - </attribute> - </optional> + <choice> + <optional> + <attribute name="file"> + <ref name="absFilePath"/> + </attribute> + </optional> + <ref name="diskSourcePool"/> + </choice> <optional> <ref name="startupPolicy"/> </optional> @@ -1041,9 +1044,12 @@ <interleave> <optional> <element name="source"> - <attribute name="dev"> - <ref name="absFilePath"/> - </attribute> + <choice> + <attribute name="dev"> + <ref name="absFilePath"/> + </attribute> + <ref name="diskSourcePool"/> + </choice> <optional> <ref name='devSeclabel'/> </optional> @@ -1059,9 +1065,12 @@ <interleave> <optional> <element name="source"> - <attribute name="dir"> - <ref name="absFilePath"/> - </attribute> + <choice> + <attribute name="dir"> + <ref name="absFilePath"/> + </attribute> + <ref name="diskSourcePool"/> + </choice> <empty/> </element> </optional> @@ -1075,48 +1084,10 @@ <interleave> <optional> <element name="source"> - <attribute name="protocol"> - <choice> - <value>nbd</value> - <value>rbd</value> - <value>sheepdog</value> - <value>gluster</value> - </choice> - </attribute> - <optional> - <attribute name="name"/> - </optional> - <zeroOrMore> - <element name="host"> - <choice> - <group> - <optional> - <attribute name="transport"> - <choice> - <value>tcp</value> - <value>rdma</value> - </choice> - </attribute> - </optional> - <attribute name="name"> - <ref name="dnsName"/> - </attribute> - <attribute name="port"> - <ref name="unsignedInt"/> - </attribute> - </group> - <group> - <attribute name="transport"> - <value>unix</value> - </attribute> - <attribute name="socket"> - <ref name="absFilePath"/> - </attribute> - </group> - </choice> - </element> - </zeroOrMore> - <empty/> + <choice> + <ref name="diskSourceNetwork"/> + <ref name="diskSourcePool"/> + </choice> </element> </optional> <ref name="diskspec"/> @@ -1160,6 +1131,85 @@ </optional> </element> </define> + <define name="diskSourceNetwork"> + <attribute name="protocol"> + <choice> + <value>nbd</value> + <value>rbd</value> + <value>sheepdog</value> + <value>gluster</value> + </choice> + </attribute> + <optional> + <attribute name="name"/> + </optional> + <zeroOrMore> + <element name="host"> + <choice> + <group> + <optional> + <attribute name="transport"> + <choice> + <value>tcp</value> + <value>rdma</value> + </choice> + </attribute> + </optional> + <attribute name="name"> + <ref name="dnsName"/> + </attribute> + <attribute name="port"> + <ref name="unsignedInt"/> + </attribute> + </group> + <group> + <attribute name="transport"> + <value>unix</value> + </attribute> + <attribute name="socket"> + <ref name="absFilePath"/> + </attribute> + </group> + </choice> + </element> + </zeroOrMore> + <empty/> + </define> + <define name="diskSourcePool"> + <optional> + <attribute name="type"> + <value>pool</value> + </attribute> + </optional> + <optional> + <element name="pool"> + <choice> + <attribute name="uuid"> + <ref name="UUID"/> + </attribute> + <attribute name="name"> + <ref name="genericName"/> + </attribute> + </choice> + </element> + </optional> + <optional> + <element name="volume"> + <choice> + <attribute name="name"> + <ref name="volName"/> + </attribute> + <attribute name="key"> + <text/> + </attribute> + <attribute name="path"> + <data type='anyURI'/> + </attribute> + </choice> + </element> + </optional> + </define> + <define name="geometry"> <element name="geometry"> <attribute name="cyls"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0b9ba13..4a66cd3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -692,6 +692,12 @@ VIR_ENUM_IMPL(virDomainNumatuneMemPlacementMode, "static", "auto"); +VIR_ENUM_IMPL(virDomainDiskSourceType, + VIR_DOMAIN_DISK_SOURCE_TYPE_LAST, + "default", + "pool"); + + #define VIR_DOMAIN_XML_WRITE_FLAGS VIR_DOMAIN_XML_SECURE #define VIR_DOMAIN_XML_READ_FLAGS VIR_DOMAIN_XML_INACTIVE @@ -987,6 +993,25 @@ void virDomainLeaseDefFree(virDomainLeaseDefPtr def) VIR_FREE(def); } +static void +virDomainDiskSourcePoolDefFree(virDomainDiskSourcePoolDefPtr def) +{ + if (!def) + return; + + if (def->poolType == VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NAME) + VIR_FREE(def->pool.name); + + if (def->volType == VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_NAME) + VIR_FREE(def->vol.name); + else if (def->volType == VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_PATH) + VIR_FREE(def->vol.path); + else if (def->volType == VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_KEY) + VIR_FREE(def->vol.key); + + VIR_FREE(def); +} + void virDomainDiskDefFree(virDomainDiskDefPtr def) { unsigned int i; @@ -996,6 +1021,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->serial); VIR_FREE(def->src); + virDomainDiskSourcePoolDefFree(def->srcpool); VIR_FREE(def->dst); VIR_FREE(def->driverName); virStorageFileFreeMetadata(def->backingChain); @@ -3575,6 +3601,112 @@ cleanup: goto cleanup; } +static virDomainDiskSourcePoolDefPtr +virDomainDiskSourcePoolDefParse(xmlNodePtr node) +{ + virDomainDiskSourcePoolDefPtr srcpool = NULL; + xmlNodePtr child; + virDomainDiskSourcePoolDefPtr ret = NULL; + + if (VIR_ALLOC(srcpool) < 0) { + virReportOOMError(); + goto cleanup; + } + + child = node->children; + while (child != NULL) { + if (child->type != XML_ELEMENT_NODE) { + child = child->next; + continue; + } + + if (xmlStrEqual(child->name, BAD_CAST "pool")) { + char *name = NULL; + char *uuid = NULL; + + name = virXMLPropString(child, "name"); + uuid = virXMLPropString(child, "uuid"); + + if (name && uuid) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Only one of 'name' or 'uuid' " + "is allowed")); + VIR_FREE(name); + VIR_FREE(uuid); + goto cleanup; + } + + if (name) { + srcpool->poolType = VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NAME; + srcpool->pool.name = name; + } else if (uuid) { + if (virUUIDParse(uuid, + srcpool->pool.uuid) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("malformed uuid %s"), uuid); + VIR_FREE(uuid); + goto cleanup; + } + VIR_FREE(uuid); + + srcpool->poolType = VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_UUID; + } + } else if (xmlStrEqual(child->name, BAD_CAST "volume")) { + char *name = NULL; + char *key = NULL; + char *path = NULL; + + name = virXMLPropString(child, "name"); + key = virXMLPropString(child, "key"); + path = virXMLPropString(child, "path"); + + if ((name && key) || + (name && path) || + (key && path)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("only one of 'name', 'key', 'path' is allowed")); + VIR_FREE(name); + VIR_FREE(key); + VIR_FREE(path); + } + + if (name) { + srcpool->volType = VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_NAME; + srcpool->vol.name = name; + } else if (key) { + srcpool->volType = VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_KEY; + srcpool->vol.key = key; + } else if (path) { + srcpool->volType = VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_PATH; + srcpool->vol.name = path; + } + } + child = child->next; + } + + if (srcpool->volType == VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_NAME && + srcpool->poolType == VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NONE) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'pool' must be specified if 'volume' " + "is specified by 'name'")); + goto cleanup; + } + + if (srcpool->poolType == VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NONE && + srcpool->volType == VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_NONE) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Neither 'pool' nor 'volume' is specified")); + goto cleanup; + } + + ret = srcpool; + +cleanup: + if (!ret) + virDomainDiskSourcePoolDefFree(srcpool); + return ret; +} + #define VENDOR_LEN 8 #define PRODUCT_LEN 16 @@ -3633,6 +3765,7 @@ virDomainDiskDefParseXML(virCapsPtr caps, char *wwn = NULL; char *vendor = NULL; char *product = NULL; + virDomainDiskSourcePoolDefPtr srcpool = NULL; if (VIR_ALLOC(def) < 0) { virReportOOMError(); @@ -3668,11 +3801,31 @@ virDomainDiskDefParseXML(virCapsPtr caps, cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { - if (!source && !hosts && + if (!source && !hosts && !srcpool && xmlStrEqual(cur->name, BAD_CAST "source")) { - + char *source_type = NULL; sourceNode = cur; + if ((source_type = virXMLPropString(cur, "type"))) { + if ((def->source_type = + virDomainDiskSourceTypeTypeFromString(source_type)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Unknown disk source type '%s'"), + source_type); + VIR_FREE(source_type); + goto error; + } + VIR_FREE(source_type); + + if (!(srcpool = virDomainDiskSourcePoolDefParse(cur))) + goto error; + + if (def->type == VIR_DOMAIN_DISK_TYPE_FILE) + startupPolicy = virXMLPropString(cur, "startupPolicy"); + + continue; + } + switch (def->type) { case VIR_DOMAIN_DISK_TYPE_FILE: source = virXMLPropString(cur, "file"); @@ -4052,7 +4205,7 @@ virDomainDiskDefParseXML(virCapsPtr caps, /* Only CDROM and Floppy devices are allowed missing source path * to indicate no media present */ - if (source == NULL && hosts == NULL && + if (!source && !hosts && !srcpool && def->device != VIR_DOMAIN_DISK_DEVICE_CDROM && def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY) { virReportError(VIR_ERR_NO_SOURCE, @@ -4074,9 +4227,40 @@ virDomainDiskDefParseXML(virCapsPtr caps, } if (target == NULL) { - virReportError(VIR_ERR_NO_TARGET, - source ? "%s" : NULL, source); - goto error; + if (def->source_type == VIR_DOMAIN_DISK_SOURCE_TYPE_POOL) { + virBuffer buf = VIR_BUFFER_INITIALIZER; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *str; + + if (srcpool->poolType == VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_UUID) { + virUUIDFormat(def->srcpool->pool.uuid, uuidstr); + virBufferAsprintf(&buf, "pool uuid='%s', ", uuidstr); + } else if (srcpool->poolType == VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NAME) { + virBufferAsprintf(&buf, "pool name='%s', ", srcpool->pool.name); + } + + if (srcpool->volType == VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_NAME) + virBufferAsprintf(&buf, "volume name='%s', ", srcpool->vol.name); + else if (srcpool->volType == VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_PATH) + virBufferAsprintf(&buf, "volume path='%s', ", srcpool->vol.path); + else if (srcpool->volType == VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_KEY) + virBufferAsprintf(&buf, "volume key='%s', ", srcpool->vol.key); + + if (virBufferError(&buf)) { + virReportOOMError(); + virBufferFreeAndReset(&buf); + goto error; + } + + str = virBufferContentAndReset(&buf); + virReportError(VIR_ERR_NO_TARGET, "%s", str); + VIR_FREE(str); + goto error; + } else { + virReportError(VIR_ERR_NO_TARGET, + source ? "%s" : NULL, source); + goto error; + } } if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY && @@ -4325,6 +4509,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, hosts = NULL; def->nhosts = nhosts; nhosts = 0; + def->srcpool = srcpool; + srcpool = NULL; def->auth.username = authUsername; authUsername = NULL; def->driverName = driverName; @@ -4385,6 +4571,7 @@ cleanup: VIR_FREE(sgio); VIR_FREE(target); VIR_FREE(source); + virDomainDiskSourcePoolDefFree(srcpool); VIR_FREE(tray); VIR_FREE(trans); while (nhosts > 0) { @@ -12153,6 +12340,149 @@ static void virDomainDiskBlockIoDefFormat(virBufferPtr buf, } } +static int +virDomainDiskSourceDefFormat(virBufferPtr buf, + virDomainDiskDefPtr def) +{ + int n; + const char *startupPolicy = virDomainStartupPolicyTypeToString(def->startupPolicy); + + + if (def->source_type == VIR_DOMAIN_DISK_SOURCE_TYPE_POOL) { + virBufferAddLit(buf, " <source"); + virBufferAsprintf(buf, " type='%s'", + virDomainDiskSourceTypeTypeToString(def->source_type)); + + if (def->type == VIR_DOMAIN_DISK_TYPE_FILE) { + if (def->startupPolicy) + virBufferEscapeString(buf, " startupPolicy='%s'", + startupPolicy); + } + + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 8); + + if (def->srcpool->poolType != + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NONE) { + virBufferAddLit(buf, "<pool"); + if (def->srcpool->poolType == + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_UUID) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(def->srcpool->pool.uuid, uuidstr); + virBufferAsprintf(buf, " uuid='%s'/>\n", uuidstr); + } else if (def->srcpool->poolType == + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NAME) { + virBufferAsprintf(buf, " name='%s'/>\n", def->srcpool->pool.name); + } + } + + if (def->srcpool->volType != + VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_NONE) { + virBufferAddLit(buf, "<volume"); + if (def->srcpool->volType == + VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_NAME) { + virBufferAsprintf(buf, " name='%s'/>\n", def->srcpool->vol.name); + } else if (def->srcpool->volType == + VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_PATH) { + virBufferAsprintf(buf, " path='%s'/>\n", def->srcpool->vol.path); + } else if (def->srcpool->volType == + VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_KEY) { + virBufferAsprintf(buf, " key='%s'/>\n", def->srcpool->vol.key); + } + } + + if ((def->type == VIR_DOMAIN_DISK_TYPE_FILE || + def->type == VIR_DOMAIN_DISK_TYPE_BLOCK) && + def->nseclabels) { + for (n = 0; n < def->nseclabels; n++) + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + } + virBufferAdjustIndent(buf, -8); + virBufferAddLit(buf, " </source>\n"); + return 0; + } + + if (def->src || def->nhosts > 0 || + def->startupPolicy) { + switch (def->type) { + case VIR_DOMAIN_DISK_TYPE_FILE: + if (def->src) + virBufferEscapeString(buf, " <source file='%s'", def->src); + if (def->startupPolicy) + virBufferEscapeString(buf, " startupPolicy='%s'", + startupPolicy); + if (def->nseclabels) { + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 8); + for (n = 0; n < def->nseclabels; n++) + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + virBufferAdjustIndent(buf, -8); + virBufferAddLit(buf, " </source>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } + break; + case VIR_DOMAIN_DISK_TYPE_BLOCK: + virBufferEscapeString(buf, " <source dev='%s'", + def->src); + if (def->nseclabels) { + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 8); + for (n = 0; n < def->nseclabels; n++) + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + virBufferAdjustIndent(buf, -8); + virBufferAddLit(buf, " </source>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } + break; + case VIR_DOMAIN_DISK_TYPE_DIR: + virBufferEscapeString(buf, " <source dir='%s'/>\n", + def->src); + break; + case VIR_DOMAIN_DISK_TYPE_NETWORK: + virBufferAsprintf(buf, " <source protocol='%s'", + virDomainDiskProtocolTypeToString(def->protocol)); + if (def->src) { + virBufferEscapeString(buf, " name='%s'", def->src); + } + if (def->nhosts == 0) { + virBufferAddLit(buf, "/>\n"); + } else { + int i; + + virBufferAddLit(buf, ">\n"); + for (i = 0; i < def->nhosts; i++) { + virBufferAddLit(buf, " <host"); + if (def->hosts[i].name) { + virBufferEscapeString(buf, " name='%s'", def->hosts[i].name); + } + if (def->hosts[i].port) { + virBufferEscapeString(buf, " port='%s'", + def->hosts[i].port); + } + if (def->hosts[i].transport) { + virBufferAsprintf(buf, " transport='%s'", + virDomainDiskProtocolTransportTypeToString(def->hosts[i].transport)); + } + if (def->hosts[i].socket) { + virBufferEscapeString(buf, " socket='%s'", def->hosts[i].socket); + } + virBufferAddLit(buf, "/>\n"); + } + virBufferAddLit(buf, " </source>\n"); + } + break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected disk type %s"), + virDomainDiskTypeToString(def->type)); + return -1; + } + } + + return 0; +} static int virDomainDiskDefFormat(virBufferPtr buf, @@ -12169,10 +12499,8 @@ virDomainDiskDefFormat(virBufferPtr buf, const char *ioeventfd = virDomainIoEventFdTypeToString(def->ioeventfd); const char *event_idx = virDomainVirtioEventIdxTypeToString(def->event_idx); const char *copy_on_read = virDomainVirtioEventIdxTypeToString(def->copy_on_read); - const char *startupPolicy = virDomainStartupPolicyTypeToString(def->startupPolicy); const char *sgio = virDomainDiskSGIOTypeToString(def->sgio); - int n; char uuidstr[VIR_UUID_STRING_BUFLEN]; if (!type) { @@ -12268,86 +12596,8 @@ virDomainDiskDefFormat(virBufferPtr buf, virBufferAddLit(buf, " </auth>\n"); } - if (def->src || def->nhosts > 0 || - def->startupPolicy) { - switch (def->type) { - case VIR_DOMAIN_DISK_TYPE_FILE: - virBufferAddLit(buf," <source"); - if (def->src) - virBufferEscapeString(buf, " file='%s'", def->src); - if (def->startupPolicy) - virBufferEscapeString(buf, " startupPolicy='%s'", - startupPolicy); - if (def->nseclabels) { - virBufferAddLit(buf, ">\n"); - virBufferAdjustIndent(buf, 8); - for (n = 0; n < def->nseclabels; n++) - virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); - virBufferAdjustIndent(buf, -8); - virBufferAddLit(buf, " </source>\n"); - } else { - virBufferAddLit(buf, "/>\n"); - } - break; - case VIR_DOMAIN_DISK_TYPE_BLOCK: - virBufferEscapeString(buf, " <source dev='%s'", - def->src); - if (def->nseclabels) { - virBufferAddLit(buf, ">\n"); - virBufferAdjustIndent(buf, 8); - for (n = 0; n < def->nseclabels; n++) - virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); - virBufferAdjustIndent(buf, -8); - virBufferAddLit(buf, " </source>\n"); - } else { - virBufferAddLit(buf, "/>\n"); - } - break; - case VIR_DOMAIN_DISK_TYPE_DIR: - virBufferEscapeString(buf, " <source dir='%s'/>\n", - def->src); - break; - case VIR_DOMAIN_DISK_TYPE_NETWORK: - virBufferAsprintf(buf, " <source protocol='%s'", - virDomainDiskProtocolTypeToString(def->protocol)); - if (def->src) { - virBufferEscapeString(buf, " name='%s'", def->src); - } - if (def->nhosts == 0) { - virBufferAddLit(buf, "/>\n"); - } else { - int i; - - virBufferAddLit(buf, ">\n"); - for (i = 0; i < def->nhosts; i++) { - virBufferAddLit(buf, " <host"); - if (def->hosts[i].name) { - virBufferEscapeString(buf, " name='%s'", def->hosts[i].name); - } - if (def->hosts[i].port) { - virBufferEscapeString(buf, " port='%s'", - def->hosts[i].port); - } - if (def->hosts[i].transport) { - virBufferAsprintf(buf, " transport='%s'", - virDomainDiskProtocolTransportTypeToString(def->hosts[i].transport)); - } - if (def->hosts[i].socket) { - virBufferEscapeString(buf, " socket='%s'", def->hosts[i].socket); - } - virBufferAddLit(buf, "/>\n"); - } - virBufferAddLit(buf, " </source>\n"); - } - break; - default: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected disk type %s"), - virDomainDiskTypeToString(def->type)); - return -1; - } - } - + if (virDomainDiskSourceDefFormat(buf, def) < 0) + return -1; virDomainDiskGeometryDefFormat(buf, def); virDomainDiskBlockIoDefFormat(buf, def); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ce36003..1909f62 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -585,9 +585,53 @@ struct _virDomainBlockIoTuneInfo { }; typedef virDomainBlockIoTuneInfo *virDomainBlockIoTuneInfoPtr; + +enum virDomainDiskSourceType { + VIR_DOMAIN_DISK_SOURCE_TYPE_DEFAULT = 0, + VIR_DOMAIN_DISK_SOURCE_TYPE_POOL, + + VIR_DOMAIN_DISK_SOURCE_TYPE_LAST +}; + +enum virDomainDiskSourcePoolType { + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NONE = 0, + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NAME, + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_UUID, + + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_LAST +}; + +enum virDomainDiskSourceVolType { + VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_NONE = 0, + VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_NAME, + VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_PATH, + VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_KEY, + + VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_LAST +}; + +typedef struct _virDomainDiskSourcePoolDef virDomainDiskSourcePoolDef; +struct _virDomainDiskSourcePoolDef { + int poolType; /* enum virDomainDiskSourcePoolType */ + int volType; /* enum virDomainDiskSourceVolType */ + + union { + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; + } pool; + + union { + char *name; + char *key; + char *path; + } vol; +}; +typedef virDomainDiskSourcePoolDef *virDomainDiskSourcePoolDefPtr; + /* Stores the virtual disk configuration */ struct _virDomainDiskDef { int type; + int source_type; /* enum virDomainDiskSourceType */ int device; int bus; char *src; @@ -596,6 +640,7 @@ struct _virDomainDiskDef { int protocol; size_t nhosts; virDomainDiskHostDefPtr hosts; + virDomainDiskSourcePoolDefPtr srcpool; struct { char *username; int secretType; /* enum virDomainDiskSecretType */ @@ -2252,6 +2297,7 @@ VIR_ENUM_DECL(virDomainDiskTray) VIR_ENUM_DECL(virDomainIoEventFd) VIR_ENUM_DECL(virDomainVirtioEventIdx) VIR_ENUM_DECL(virDomainDiskCopyOnRead) +VIR_ENUM_DECL(virDomainDiskSourceType) VIR_ENUM_DECL(virDomainController) VIR_ENUM_DECL(virDomainControllerModelSCSI) VIR_ENUM_DECL(virDomainControllerModelUSB) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 521f8e0..d14be41 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -368,6 +368,8 @@ virDomainDiskProtocolTransportTypeFromString; virDomainDiskProtocolTransportTypeToString; virDomainDiskRemove; virDomainDiskRemoveByName; +virDomainDiskSourceTypeTypeFromString; +virDomainDiskSourceTypeTypeToString; virDomainDiskTypeFromString; virDomainDiskTypeToString; virDomainEmulatorPinAdd; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-source-pool.xml b/tests/qemuxml2argvdata/qemuxml2argv-disk-source-pool.xml new file mode 100644 index 0000000..5913403 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-source-pool.xml @@ -0,0 +1,36 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='cdrom'> + <source type='pool'> + <pool uuid='92c7b3b8-a290-afd2-5b8b-1336579b8457'/> + <volume name='blk-pool0-vol0'/> + </source> + <target dev='hda' bus='ide'/> + <readonly/> + <address type='drive' controller='0' bus='0' target='0' unit='1'/> + </disk> + <disk type='file' device='disk'> + <source file='/tmp/idedisk.img'/> + <target dev='hdc' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='2'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='ide' index='1'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 160e958..7e174dd 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -241,6 +241,7 @@ mymain(void) DO_TEST("disk-scsi-lun-passthrough-sgio"); DO_TEST("disk-scsi-disk-vpd"); + DO_TEST("disk-source-pool"); /* These tests generate different XML */ DO_TEST_DIFFERENT("balloon-device-auto"); -- 1.7.7.6

On Wed, Jan 23, 2013 at 07:04:35PM +0800, Osier Yang wrote:
With this patch, one can specify the disk source using storage pool/volume like:
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume key='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume path='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <pool uuid|name="$var"/> <volume name='foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
If you're going to introduce new schema for <source>, then you must introduce a new disk type value.ie a <disk type='file'> must always use the <source file='...'/> XML syntax, otherwise you cause backwards compatibility problems for applications What you need here is a <disk type='volume'/> for your new schema. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 2013年01月23日 22:18, Daniel P. Berrange wrote:
On Wed, Jan 23, 2013 at 07:04:35PM +0800, Osier Yang wrote:
With this patch, one can specify the disk source using storage pool/volume like:
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume key='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume path='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <pool uuid|name="$var"/> <volume name='foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
If you're going to introduce new schema for<source>, then you must introduce a new disk type value.ie a <disk type='file'> must always use the<source file='...'/> XML syntax, otherwise you cause backwards compatibility problems for applications
Oh, yes. I need a v2.
What you need here is a<disk type='volume'/> for your new schema.
But before I make up the v2, do you see other design problem on the set? Thanks. Osier

On Wed, Jan 23, 2013 at 10:55:25PM +0800, Osier Yang wrote:
On 2013年01月23日 22:18, Daniel P. Berrange wrote:
On Wed, Jan 23, 2013 at 07:04:35PM +0800, Osier Yang wrote:
With this patch, one can specify the disk source using storage pool/volume like:
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume key='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume path='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <pool uuid|name="$var"/> <volume name='foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
If you're going to introduce new schema for<source>, then you must introduce a new disk type value.ie a <disk type='file'> must always use the<source file='...'/> XML syntax, otherwise you cause backwards compatibility problems for applications
Oh, yes. I need a v2.
What you need here is a<disk type='volume'/> for your new schema.
But before I make up the v2, do you see other design problem on the set? Thanks.
I'm wondering if it is really requires to allow so many different options for specifyin the pool & volume. For <interface type='network'> we were fine simply using the 'name' and ignoring UUID. I cna't help thinking that for storage we can similarly just use the pool name and volume name Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 2013年01月23日 22:58, Daniel P. Berrange wrote:
On Wed, Jan 23, 2013 at 10:55:25PM +0800, Osier Yang wrote:
On 2013年01月23日 22:18, Daniel P. Berrange wrote:
On Wed, Jan 23, 2013 at 07:04:35PM +0800, Osier Yang wrote:
With this patch, one can specify the disk source using storage pool/volume like:
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume key='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume path='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <pool uuid|name="$var"/> <volume name='foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
If you're going to introduce new schema for<source>, then you must introduce a new disk type value.ie a <disk type='file'> must always use the<source file='...'/> XML syntax, otherwise you cause backwards compatibility problems for applications
Oh, yes. I need a v2.
What you need here is a<disk type='volume'/> for your new schema.
But before I make up the v2, do you see other design problem on the set? Thanks.
I'm wondering if it is really requires to allow so many different options for specifyin the pool& volume. For<interface type='network'> we were fine simply using the 'name' and ignoring UUID. I cna't help thinking that for storage we can similarly just use the pool name and volume name
This was my hesitating too when on the half road. But to post the RFC earlier, and considering it's at least not a bad thing, as we provide all the interfaces, so I went on with it. I think it makes no big difference if we simply use pool name and volume name, but what I'm not sure is if the users will want the uuid for pool, and path/key for volume (using path/key is convenient as the pool is not even neccessary). Osier

On 01/23/2013 10:24 AM, Osier Yang wrote:
On 2013年01月23日 22:58, Daniel P. Berrange wrote:
On Wed, Jan 23, 2013 at 10:55:25PM +0800, Osier Yang wrote:
On 2013年01月23日 22:18, Daniel P. Berrange wrote:
On Wed, Jan 23, 2013 at 07:04:35PM +0800, Osier Yang wrote:
With this patch, one can specify the disk source using storage pool/volume like:
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume key='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume path='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <pool uuid|name="$var"/> <volume name='foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
If you're going to introduce new schema for<source>, then you must introduce a new disk type value.ie a <disk type='file'> must always use the<source file='...'/> XML syntax, otherwise you cause backwards compatibility problems for applications
Oh, yes. I need a v2.
What you need here is a<disk type='volume'/> for your new schema.
But before I make up the v2, do you see other design problem on the set? Thanks.
I'm wondering if it is really requires to allow so many different options for specifyin the pool& volume. For<interface type='network'> we were fine simply using the 'name' and ignoring UUID. I cna't help thinking that for storage we can similarly just use the pool name and volume name
This was my hesitating too when on the half road. But to post the RFC earlier, and considering it's at least not a bad thing, as we provide all the interfaces, so I went on with it.
I think it makes no big difference if we simply use pool name and volume name, but what I'm not sure is if the users will want the uuid for pool, and path/key for volume (using path/key is convenient as the pool is not even neccessary).
(Keep in mind this is coming from a non-storage guy, so there may be some flaws in my logic :-) Too many ways of describing the same thing is bad, as it leads to confusion. Also, since the point of making this abstraction is to isolate the host-specific details of the device from the domain's configuration, I think allowing the file path to be specified is counter-productive (since it could be different from host to host, depending on where an NFS share is mounted, for example). Of course this is a bit of a different situation than network devices, since the pool/volume must end up pointing to the same bits from all hosts (either the exact same bits via a different access path, or a new copy of the bits migrated over to a different type of storage), but in the end it should be possible for the disk image to be in a local directory on one host, accessed by NFS on another, and maybe even via iscsi or lvm on another - those details should all be in the pool/volume definitions on each host, and the guest config should just say "this disk's image is in pool x, volume y".

On 2013年01月25日 01:42, Laine Stump wrote:
On 01/23/2013 10:24 AM, Osier Yang wrote:
On 2013年01月23日 22:58, Daniel P. Berrange wrote:
On Wed, Jan 23, 2013 at 10:55:25PM +0800, Osier Yang wrote:
On 2013年01月23日 22:18, Daniel P. Berrange wrote:
On Wed, Jan 23, 2013 at 07:04:35PM +0800, Osier Yang wrote:
With this patch, one can specify the disk source using storage pool/volume like:
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume key='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <volume path='/var/lib/libvirt/images/foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
<disk type='file' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source type='pool'> <pool uuid|name="$var"/> <volume name='foo.img'/> <seclabel relabel='no'/> </source> <target dev='vdb' bus='virtio'/> </disk>
If you're going to introduce new schema for<source>, then you must introduce a new disk type value.ie a <disk type='file'> must always use the<source file='...'/> XML syntax, otherwise you cause backwards compatibility problems for applications
Oh, yes. I need a v2.
What you need here is a<disk type='volume'/> for your new schema.
But before I make up the v2, do you see other design problem on the set? Thanks.
I'm wondering if it is really requires to allow so many different options for specifyin the pool& volume. For<interface type='network'> we were fine simply using the 'name' and ignoring UUID. I cna't help thinking that for storage we can similarly just use the pool name and volume name
This was my hesitating too when on the half road. But to post the RFC earlier, and considering it's at least not a bad thing, as we provide all the interfaces, so I went on with it.
I think it makes no big difference if we simply use pool name and volume name, but what I'm not sure is if the users will want the uuid for pool, and path/key for volume (using path/key is convenient as the pool is not even neccessary).
(Keep in mind this is coming from a non-storage guy, so there may be some flaws in my logic :-)
Rather clear actually. :-)
Too many ways of describing the same thing is bad, as it leads to
confusion.
Also, since the point of making this abstraction is to isolate the host-specific details of the device from the domain's configuration, I think allowing the file path to be specified is counter-productive (since it could be different from host to host, depending on where an NFS share is mounted, for example).
Right, I see several benefits of gluing the domain and storage: 1) the disk source (or source for other devices) can be not stable after a system rebooting, such as the path for LUN which behind a vHBA, and the scsi_host number. We can make them stabe via storage pool. Such as "persistent vHBA support in storage pool". 2) As what you mentioned above: the same underlying disk source (for all of disk of type 'file', 'block', 'dir') can have different path on different host. Using the storage pool and volume can avoid this. 3) Simpler config for disk of 'network' type (in storage, it's pool of RBD, sheepdog, glusterfs type, well, we have to support the glusterfs pool in future), those network configurations can be taken from the pool/volume configuration. On the other hand, this avoid the duplication. It could implies more benifit I have not realizied yet, and thus more work to do though.
Of course this is a bit of a different situation than network devices, since the pool/volume must end up pointing to the same bits from all hosts (either the exact same bits via a different access path, or a new copy of the bits migrated over to a different type of storage), but in the end it should be possible for the disk image to be in a local directory on one host, accessed by NFS on another, and maybe even via iscsi or lvm on another - those details should all be in the pool/volume definitions on each host, and the guest config should just say "this disk's image is in pool x, volume y".
So you agreed with just using the "pool name and volume name"?
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 01/24/2013 10:44 PM, Osier Yang wrote:
So you agreed with just using the "pool name and volume name"?
I think so. Unless you can think of a situation where the pool or volume name legitimately wouldn't be known, or would be required to be different from one machine to another in spite of the path/key/etc being the same.

On 2013年01月26日 03:12, Laine Stump wrote:
On 01/24/2013 10:44 PM, Osier Yang wrote:
So you agreed with just using the "pool name and volume name"?
I think so. Unless you can think of a situation where the pool or volume name legitimately wouldn't be known, or would be required to be different from one machine to another in spite of the path/key/etc being the same.
Hum, this inspires me thinking about the migration. The source and dest host could have pool && vol with the same name. However, the vol's content could be different. I.E, in this case, we will need the globally unique $IDs (pool UUID, and/or vol key).
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On Mon, Jan 28, 2013 at 12:27:15PM +0800, Osier Yang wrote:
On 2013年01月26日 03:12, Laine Stump wrote:
On 01/24/2013 10:44 PM, Osier Yang wrote:
So you agreed with just using the "pool name and volume name"?
I think so. Unless you can think of a situation where the pool or volume name legitimately wouldn't be known, or would be required to be different from one machine to another in spite of the path/key/etc being the same.
Hum, this inspires me thinking about the migration. The source and dest host could have pool && vol with the same name. However, the vol's content could be different. I.E, in this case, we will need the globally unique $IDs (pool UUID, and/or vol key).
IMHO you are describing an administrative mis-configuration. You could just as easily provide a storage pool with the same UUID on two hosts, which has different storage. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This creates src/conf/domain_storage.h and src/conf/storage_conf.c, which defines a driver contains internal APIs to glue the domain and storage. Currently there is only one API, translateDiskSourcePool, which is to translate the specified pool/volume into the the real underlying disk source. Later patch will implement API. --- src/Makefile.am | 3 ++- src/conf/domain_storage.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_storage.h | 37 +++++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 4 ++++ 4 files changed, 88 insertions(+), 1 deletions(-) create mode 100644 src/conf/domain_storage.c create mode 100644 src/conf/domain_storage.h diff --git a/src/Makefile.am b/src/Makefile.am index 070a089..d664c48 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -203,7 +203,8 @@ DOMAIN_CONF_SOURCES = \ conf/domain_conf.c conf/domain_conf.h \ conf/domain_audit.c conf/domain_audit.h \ conf/domain_nwfilter.c conf/domain_nwfilter.h \ - conf/snapshot_conf.c conf/snapshot_conf.h + conf/snapshot_conf.c conf/snapshot_conf.h \ + conf/domain_storage.c conf/domain_storage.h DOMAIN_EVENT_SOURCES = \ conf/domain_event.c conf/domain_event.h diff --git a/src/conf/domain_storage.c b/src/conf/domain_storage.c new file mode 100644 index 0000000..b5f6ee4 --- /dev/null +++ b/src/conf/domain_storage.c @@ -0,0 +1,45 @@ +/* + * domain_storage.c: Internal APIs to glue domain and storage + * + * Copyright (C) 2013 Red Hat, Inc. + * + * 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/>. + * + * Author: Osier Yang <jyang@redhat.com> + */ + +#include <config.h> + +#include "internal.h" + +#include "datatypes.h" +#include "domain_conf.h" +#include "domain_storage.h" + +static virDomainStorageDriverPtr domainStorageDriver; + +void +virRegisterDomainStorageDriver(virDomainStorageDriverPtr driver) { + domainStorageDriver = driver; +} + +int +virDomainStorageTranslateDiskSourcePool(virConnectPtr conn, + virDomainDefPtr def) { + if (domainStorageDriver != NULL) + return domainStorageDriver->translateDiskSourcePool(conn, def); + return 0; +} + diff --git a/src/conf/domain_storage.h b/src/conf/domain_storage.h new file mode 100644 index 0000000..143b476 --- /dev/null +++ b/src/conf/domain_storage.h @@ -0,0 +1,37 @@ +/* + * domain_storage.h: Internal APIs to glue domain and storage + * + * Copyright (C) 2013 Red Hat, Inc. + * + * 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/>. + * + * Author: Osier Yang <jyang@redhat.com> + */ +#ifndef DOMAIN_STORAGE_H +# define DOMAIN_STORAGE_H + +typedef int (*virDomainStorageDiskSourcePoolTranslate)(virConnectPtr conn, + virDomainDefPtr def); +struct _virDomainStorageDriver { + virDomainStorageDiskSourcePoolTranslate translateDiskSourcePool; +}; +typedef struct _virDomainStorageDriver virDomainStorageDriver; +typedef virDomainStorageDriver *virDomainStorageDriverPtr; + +void virRegisterDomainStorageDriver(virDomainStorageDriverPtr driver); +int virDomainStorageTranslateDiskSourcePool(virConnectPtr conn, + virDomainDefPtr def); + +#endif /* DOMAIN_STORAGE_H */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d14be41..04aa24c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -616,6 +616,10 @@ virDomainConfNWFilterTeardown; virDomainConfVMNWFilterTeardown; +# domain_storage.h +virDomainStorageTranslateDiskSourcePool; +virRegisterDomainStorageDriver; + # ebtables.h ebtablesAddForwardAllowIn; ebtablesAddForwardPolicyReject; -- 1.7.7.6

It iterates over all the domain disks, and translate the source for all the disks of 'pool' source type. Disks of type 'file', 'block', and 'dir' are supported now. Network type is not supported yet, it will be another patch. src/storage/storage_driver.c: * New helper storagePoolObjFindByDiskSource to find the specified pool by either name or uuid. * Implement translateDiskSourcePool as storageTranslateDomainDiskSourcePool --- src/storage/storage_driver.c | 207 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 207 insertions(+), 0 deletions(-) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index e98c18c..1faa3cb 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -47,6 +47,8 @@ #include "virlog.h" #include "virfile.h" #include "fdstream.h" +#include "domain_conf.h" +#include "domain_storage.h" #include "configmake.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -2367,6 +2369,205 @@ storageListAllPools(virConnectPtr conn, return ret; } +static virStoragePoolObjPtr +storagePoolObjFindByDiskSource(virConnectPtr conn, + virDomainDiskSourcePoolDefPtr def) +{ + virStorageDriverStatePtr driver = conn->storagePrivateData; + virStoragePoolObjPtr pool = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + bool isActive; + + storageDriverLock(driver); + if (def->poolType == + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_UUID) { + pool = virStoragePoolObjFindByUUID(&driver->pools, + def->pool.uuid); + } else if (def->poolType == + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NAME) { + pool = virStoragePoolObjFindByName(&driver->pools, + def->pool.name); + } + storageDriverUnlock(driver); + + if (!pool || !(isActive = virStoragePoolObjIsActive(pool))) { + if (def->poolType == + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_UUID) { + virUUIDFormat(def->pool.uuid, uuidstr); + } + } + + if (!pool) { + if (def->poolType == + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_UUID) { + virReportError(VIR_ERR_NO_STORAGE_POOL, + ("no storage pool with matching uuid '%s'"), + uuidstr); + goto error; + } else if (def->poolType == + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NAME) { + virReportError(VIR_ERR_NO_STORAGE_POOL, + _("no storage pool with matching name '%s'"), + def->pool.name); + goto error; + } + } + + if (!isActive) { + if (def->poolType == + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_UUID) + virReportError(VIR_ERR_OPERATION_INVALID, + _("The specified pool '%s' is not active"), + uuidstr); + else if (def->poolType == + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NAME) + virReportError(VIR_ERR_OPERATION_INVALID, + _("The specified pool '%s' is not active"), + def->pool.name); + goto error; + } + + return pool; +error: + if (pool) + virStoragePoolObjUnlock(pool); + return NULL; +} + +static int +storageTranslateDomainDiskSourcePool(virConnectPtr conn, + virDomainDefPtr def) +{ + virStorageDriverStatePtr driver = conn->storagePrivateData; + virStoragePoolObjPtr pool = NULL; + virStorageVolDefPtr vol = NULL; + int i; + int ret = -1; + + for (i = 0; i < def->ndisks; i++) { + virDomainDiskDefPtr disk = def->disks[i]; + + if (disk->source_type != VIR_DOMAIN_DISK_SOURCE_TYPE_POOL) + continue; + + /* FIXME: Network disk should also be supported */ + if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) + continue; + + if (disk->srcpool->poolType != + VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NONE && + !(pool = storagePoolObjFindByDiskSource(conn, disk->srcpool))) + goto cleanup; + + switch (disk->srcpool->volType) { + case VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_PATH: + if (pool) { + vol = virStorageVolDefFindByPath(pool, + disk->srcpool->vol.path); + } else { + storageDriverLock(driver); + for (i = 0 ; i < driver->pools.count && !vol; i++) { + virStoragePoolObjLock(driver->pools.objs[i]); + if (virStoragePoolObjIsActive(driver->pools.objs[i])) { + vol = virStorageVolDefFindByPath(driver->pools.objs[i], + disk->srcpool->vol.path); + } + + /* Reflect the found pool to domain's XML */ + if (vol) { + disk->srcpool->poolType = VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NAME; + disk->srcpool->pool.name = strdup(driver->pools.objs[i]->def->name); + } + virStoragePoolObjUnlock(driver->pools.objs[i]); + } + storageDriverUnlock(driver); + } + + if (!vol) { + if (pool) + virReportError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol of specified pool with " + "matching path '%s'"), + disk->srcpool->vol.path); + else + virReportError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching path '%s'"), + disk->srcpool->vol.path); + goto cleanup; + } + + disk->src = strdup(vol->target.path); + vol = NULL; + break; + case VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_NAME: + if (!(vol = virStorageVolDefFindByName(pool, disk->srcpool->vol.name))) { + virReportError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol of specified pool with " + "matching name '%s'"), + disk->srcpool->vol.name); + goto cleanup; + } + + disk->src = strdup(vol->target.path); + vol = NULL; + break; + case VIR_DOMAIN_DISK_SOURCE_VOL_TYPE_KEY: + if (pool) { + vol = virStorageVolDefFindByKey(pool, + disk->srcpool->vol.key); + } else { + storageDriverLock(driver); + for (i = 0 ; i < driver->pools.count && !vol; i++) { + virStoragePoolObjLock(driver->pools.objs[i]); + if (virStoragePoolObjIsActive(driver->pools.objs[i])) { + vol = virStorageVolDefFindByKey(driver->pools.objs[i], + disk->srcpool->vol.key); + } + + /* Reflect the found pool to domain's XML */ + if (vol) { + disk->srcpool->poolType = VIR_DOMAIN_DISK_SOURCE_POOL_TYPE_NAME; + disk->srcpool->pool.name = strdup(driver->pools.objs[i]->def->name); + } + virStoragePoolObjUnlock(driver->pools.objs[i]); + } + storageDriverUnlock(driver); + } + + if (!vol) { + if (pool) + virReportError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol of specified pool with " + "matching key '%s'"), + disk->srcpool->vol.key); + else + virReportError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching key '%s'"), + disk->srcpool->vol.key); + goto cleanup; + } + + disk->src = strdup(vol->target.path); + vol = NULL; + break; + default: + /* Tell the compiler to shutup */ + break; + } + if (pool) { + virStoragePoolObjUnlock(pool); + pool = NULL; + } + } + + ret = 0; + +cleanup: + if (pool) + virStoragePoolObjUnlock(pool); + return ret; +} + static virStorageDriver storageDriver = { .name = "storage", .open = storageOpen, /* 0.4.0 */ @@ -2423,8 +2624,14 @@ static virStateDriver stateDriver = { .reload = storageDriverReload, }; +static virDomainStorageDriver domainStorageDriver = { + .translateDiskSourcePool = storageTranslateDomainDiskSourcePool, +}; + + int storageRegister(void) { virRegisterStorageDriver(&storageDriver); virRegisterStateDriver(&stateDriver); + virRegisterDomainStorageDriver(&domainStorageDriver); return 0; } -- 1.7.7.6

* src/qemu/qemu_driver.c (Use virDomainStorageTranslateDiskSourcePool to translate the source to the real underlying source in qemuDomainCreate and qemuDomainObjStart) --- src/qemu/qemu_driver.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c28c223..d07bde5 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -70,6 +70,7 @@ #include "viruuid.h" #include "domain_conf.h" #include "domain_audit.h" +#include "domain_storage.h" #include "node_device_conf.h" #include "virpci.h" #include "virusb.h" @@ -1653,6 +1654,9 @@ static virDomainPtr qemuDomainCreate(virConnectPtr conn, const char *xml, VIR_DOMAIN_XML_INACTIVE))) goto cleanup; + if (virDomainStorageTranslateDiskSourcePool(conn, vm->def) < 0) + goto cleanup; + if (virSecurityManagerVerify(driver->securityManager, def) < 0) goto cleanup; @@ -5524,6 +5528,9 @@ qemuDomainObjStart(virConnectPtr conn, } } + if (virDomainStorageTranslateDiskSourcePool(conn, vm->def) < 0) + goto cleanup; + ret = qemuProcessStart(conn, driver, vm, NULL, -1, NULL, NULL, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags); virDomainAuditStart(vm, "booted", ret >= 0); -- 1.7.7.6
participants (4)
-
Daniel P. Berrange
-
Eric Blake
-
Laine Stump
-
Osier Yang