This is a definition that holds information on SCSI persistent
reservation settings. The XML part looks like this:
<reservations enabled='yes' managed='no'>
<source type='unix' path='/path/to/qemu-pr-helper.sock'
mode='client'/>
</reservations>
If @managed is set to 'yes' then the <source/> is not parsed.
This design was agreed on here:
https://www.redhat.com/archives/libvir-list/2017-November/msg01005.html
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
Reviewed-by: John Ferlan <jferlan(a)redhat.com>
---
docs/formatdomain.html.in | 23 +++-
docs/schemas/domaincommon.rng | 34 +-----
docs/schemas/storagecommon.rng | 50 ++++++++
src/conf/domain_conf.c | 38 ++++++
src/libvirt_private.syms | 3 +
src/util/virstoragefile.c | 131 +++++++++++++++++++++
src/util/virstoragefile.h | 14 +++
.../disk-virtio-scsi-reservations.xml | 49 ++++++++
.../disk-virtio-scsi-reservations.xml | 1 +
tests/qemuxml2xmltest.c | 2 +
10 files changed, 314 insertions(+), 31 deletions(-)
create mode 100644 tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml
create mode 120000 tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index ada0df227f..6ed2fd349d 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -2565,7 +2565,10 @@
</disk>
<disk type='block' device='lun'>
<driver name='qemu' type='raw'/>
- <source dev='/dev/sda'/>
+ <source dev='/dev/sda'>
+ <reservations enabled='yes' managed='no'>
+ <source type='unix' path='/path/to/qemu-pr-helper'
mode='client'/>
+ </reservations>
<target dev='sda' bus='scsi'/>
<address type='drive' controller='0' bus='0'
target='3' unit='0'/>
</disk>
@@ -2928,6 +2931,24 @@
<a href="formatstorageencryption.html">Storage
Encryption</a>
page for more information.
</dd>
+ <dt><code>reservations</code></dt>
+ <dd><span class="since">Since libvirt 4.3.0</span>,
the
+ <code>reservations</code> can be a sub-element of the
+ <code>source</code> element for storage sources (QEMU driver
only).
+ If present (and enabled) it enables persistent reservations for SCSI
+ based disks. The element has one mandatory attribute
+ <code>enabled</code> with accepted values
<code>yes</code> and
+ <code>no</code>. If the feature is enabled, then there's
another
+ mandatory attribute <code>managed</code> (accepted values are
the
+ same as for <code>enabled</code>) that enables or disables
libvirt
+ spawning a helper process. When the PR is unmanaged, then hypervisor
+ acts as a client and path to server socket must be provided in child
+ element <code>source</code>, which currently accepts only the
+ following attributes: <code>type</code> with one value
+ <code>unix</code>, <code>path</code> with path the
socket, and
+ finally <code>mode</code> which accepts either
<code>server</code>
+ or <code>client</code> and specifies the role of hypervisor.
+ </dd>
</dl>
<p>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 4cab55f05d..93084887fb 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -1530,6 +1530,9 @@
<optional>
<ref name="encryption"/>
</optional>
+ <optional>
+ <ref name="reservations"/>
+ </optional>
<zeroOrMore>
<ref name='devSeclabel'/>
</zeroOrMore>
@@ -2434,18 +2437,6 @@
</attribute>
</optional>
</define>
- <define name="reconnect">
- <element name="reconnect">
- <attribute name="enabled">
- <ref name="virYesNo"/>
- </attribute>
- <optional>
- <attribute name="timeout">
- <ref name="unsignedInt"/>
- </attribute>
- </optional>
- </element>
- </define>
<!--
An interface description can either be of type bridge in which case
@@ -2494,24 +2485,7 @@
<value>vhostuser</value>
</attribute>
<interleave>
- <element name="source">
- <attribute name="type">
- <value>unix</value>
- </attribute>
- <attribute name="path">
- <ref name="absFilePath"/>
- </attribute>
- <attribute name="mode">
- <choice>
- <value>server</value>
- <value>client</value>
- </choice>
- </attribute>
- <optional>
- <ref name="reconnect"/>
- </optional>
- <empty/>
- </element>
+ <ref name="unixSocketSource"/>
<ref name="interface-options"/>
</interleave>
</group>
diff --git a/docs/schemas/storagecommon.rng b/docs/schemas/storagecommon.rng
index edee1b0845..eed0b33347 100644
--- a/docs/schemas/storagecommon.rng
+++ b/docs/schemas/storagecommon.rng
@@ -39,6 +39,56 @@
</element>
</define>
+ <define name="reconnect">
+ <element name="reconnect">
+ <attribute name="enabled">
+ <ref name="virYesNo"/>
+ </attribute>
+ <optional>
+ <attribute name="timeout">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name='unixSocketSource'>
+ <element name="source">
+ <attribute name="type">
+ <value>unix</value>
+ </attribute>
+ <attribute name="path">
+ <ref name="absFilePath"/>
+ </attribute>
+ <attribute name="mode">
+ <choice>
+ <value>server</value>
+ <value>client</value>
+ </choice>
+ </attribute>
+ <optional>
+ <ref name="reconnect"/>
+ </optional>
+ <empty/>
+ </element>
+ </define>
+
+ <define name='reservations'>
+ <element name='reservations'>
+ <attribute name='enabled'>
+ <ref name='virYesNo'/>
+ </attribute>
+ <optional>
+ <attribute name='managed'>
+ <ref name='virYesNo'/>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name='unixSocketSource'/>
+ </optional>
+ </element>
+ </define>
+
<define name='secret'>
<element name='secret'>
<attribute name='type'>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 65b429460a..c454fb1b9b 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -5205,6 +5205,13 @@ virDomainDiskDefValidate(const virDomainDiskDef *disk)
}
}
+ if (disk->src->pr &&
+ disk->device != VIR_DOMAIN_DISK_DEVICE_LUN) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("<reservations/> allowed only for lun
devices"));
+ return -1;
+ }
+
/* Reject disks with a bus type that is not compatible with the
* given address type. The function considers only buses that are
* handled in common code. For other bus types it's not possible
@@ -8608,6 +8615,31 @@ virDomainDiskSourcePrivateDataParse(xmlNodePtr node,
}
+static int
+virDomainDiskSourcePRParse(xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
+ virStoragePRDefPtr *pr)
+{
+ xmlNodePtr saveNode = ctxt->node;
+ int ret = -1;
+
+ ctxt->node = node;
+
+ if (!(ctxt->node = virXPathNode("./reservations", ctxt))) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (!(*pr = virStoragePRDefParseXML(ctxt)))
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ ctxt->node = saveNode;
+ return ret;
+}
+
+
static int
virDomainStorageSourceParse(xmlNodePtr node,
xmlXPathContextPtr ctxt,
@@ -8654,6 +8686,9 @@ virDomainStorageSourceParse(xmlNodePtr node,
!(src->encryption = virStorageEncryptionParseNode(tmp, ctxt)))
goto cleanup;
+ if (virDomainDiskSourcePRParse(node, ctxt, &src->pr) < 0)
+ goto cleanup;
+
if (virSecurityDeviceLabelDefParseXML(&src->seclabels,
&src->nseclabels,
ctxt, flags) < 0)
goto cleanup;
@@ -22928,6 +22963,9 @@ virDomainStorageSourceFormat(virBufferPtr attrBuf,
virStorageEncryptionFormat(childBuf, src->encryption) < 0)
return -1;
+ if (src->pr)
+ virStoragePRDefFormat(childBuf, src->pr);
+
return 0;
}
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index d2728749fb..f81cb5df93 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2797,6 +2797,9 @@ virStorageNetHostDefFree;
virStorageNetHostTransportTypeFromString;
virStorageNetHostTransportTypeToString;
virStorageNetProtocolTypeToString;
+virStoragePRDefFormat;
+virStoragePRDefFree;
+virStoragePRDefParseXML;
virStorageSourceBackingStoreClear;
virStorageSourceClear;
virStorageSourceCopy;
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
index 531540ac91..42ef24e9d5 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -1892,6 +1892,136 @@ virStorageAuthDefFormat(virBufferPtr buf,
}
+void
+virStoragePRDefFree(virStoragePRDefPtr prd)
+{
+ if (!prd)
+ return;
+
+ VIR_FREE(prd->path);
+ VIR_FREE(prd);
+}
+
+
+virStoragePRDefPtr
+virStoragePRDefParseXML(xmlXPathContextPtr ctxt)
+{
+ virStoragePRDefPtr prd, ret = NULL;
+ char *enabled = NULL;
+ char *managed = NULL;
+ char *type = NULL;
+ char *path = NULL;
+ char *mode = NULL;
+
+ if (VIR_ALLOC(prd) < 0)
+ return NULL;
+
+ if (!(enabled = virXPathString("string(./@enabled)", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing @enabled attribute for
<reservations/>"));
+ goto cleanup;
+ }
+
+ if ((prd->enabled = virTristateBoolTypeFromString(enabled)) <= 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("invalid value for 'enabled': %s"), enabled);
+ goto cleanup;
+ }
+
+ if (prd->enabled == VIR_TRISTATE_BOOL_YES) {
+ if (!(managed = virXPathString("string(./@managed)", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing @managed attribute for
<reservations/>"));
+ goto cleanup;
+ }
+
+ if ((prd->managed = virTristateBoolTypeFromString(managed)) <= 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("invalid value for 'managed': %s"),
managed);
+ goto cleanup;
+ }
+
+ if (prd->managed == VIR_TRISTATE_BOOL_NO) {
+ type = virXPathString("string(./source[1]/@type)", ctxt);
+ path = virXPathString("string(./source[1]/@path)", ctxt);
+ mode = virXPathString("string(./source[1]/@mode)", ctxt);
+
+ if (!type) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing connection type for
<reservations/>"));
+ goto cleanup;
+ }
+
+ if (!path) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing path for <reservations/>"));
+ goto cleanup;
+ }
+
+ if (!mode) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing connection mode for
<reservations/>"));
+ goto cleanup;
+ }
+
+ if (STRNEQ(type, "unix")) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unsupported connection type for
<reservations/>: %s"),
+ type);
+ goto cleanup;
+ }
+
+ if (STRNEQ(mode, "client")) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unsupported connection mode for
<reservations/>: %s"),
+ mode);
+ goto cleanup;
+ }
+
+ VIR_STEAL_PTR(prd->path, path);
+ }
+ }
+
+ ret = prd;
+ prd = NULL;
+
+ cleanup:
+ VIR_FREE(mode);
+ VIR_FREE(path);
+ VIR_FREE(type);
+ VIR_FREE(managed);
+ VIR_FREE(enabled);
+ virStoragePRDefFree(prd);
+ return ret;
+}
+
+
+void
+virStoragePRDefFormat(virBufferPtr buf,
+ virStoragePRDefPtr prd)
+{
+ virBufferAsprintf(buf, "<reservations enabled='%s'",
+ virTristateBoolTypeToString(prd->enabled));
+ if (prd->enabled == VIR_TRISTATE_BOOL_YES) {
+ virBufferAsprintf(buf, " managed='%s'",
+ virTristateBoolTypeToString(prd->managed));
+ if (prd->managed == VIR_TRISTATE_BOOL_NO) {
+ virBufferAddLit(buf, ">\n");
+ virBufferAdjustIndent(buf, 2);
+ virBufferAddLit(buf, "<source type='unix'");
+ virBufferEscapeString(buf, " path='%s'", prd->path);
+ virBufferAddLit(buf, " mode='client'/>\n");
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</reservations>\n");
+ } else {
+ virBufferAddLit(buf, "/>\n");
+ }
+ } else {
+ virBufferAddLit(buf, "/>\n");
+ }
+}
+
+
virSecurityDeviceLabelDefPtr
virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src,
const char *model)
@@ -2264,6 +2394,7 @@ virStorageSourceClear(virStorageSourcePtr def)
virBitmapFree(def->features);
VIR_FREE(def->compat);
virStorageEncryptionFree(def->encryption);
+ virStoragePRDefFree(def->pr);
virStorageSourceSeclabelsClear(def);
virStoragePermsFree(def->perms);
VIR_FREE(def->timestamps);
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
index d129e81978..69e7e20e17 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -216,6 +216,14 @@ struct _virStorageAuthDef {
virSecretLookupTypeDef seclookupdef;
};
+typedef struct _virStoragePRDef virStoragePRDef;
+typedef virStoragePRDef *virStoragePRDefPtr;
+struct _virStoragePRDef {
+ int enabled; /* enum virTristateBool */
+ int managed; /* enum virTristateBool */
+ char *path;
+};
+
typedef struct _virStorageDriverData virStorageDriverData;
typedef virStorageDriverData *virStorageDriverDataPtr;
@@ -243,6 +251,7 @@ struct _virStorageSource {
bool authInherited;
virStorageEncryptionPtr encryption;
bool encryptionInherited;
+ virStoragePRDefPtr pr;
virObjectPtr privateData;
@@ -369,6 +378,11 @@ virStorageAuthDefPtr virStorageAuthDefParse(xmlNodePtr node,
xmlXPathContextPtr ctxt);
void virStorageAuthDefFormat(virBufferPtr buf, virStorageAuthDefPtr authdef);
+void virStoragePRDefFree(virStoragePRDefPtr prd);
+virStoragePRDefPtr virStoragePRDefParseXML(xmlXPathContextPtr ctxt);
+void virStoragePRDefFormat(virBufferPtr buf,
+ virStoragePRDefPtr prd);
+
virSecurityDeviceLabelDefPtr
virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src,
const char *model);
diff --git a/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml
b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml
new file mode 100644
index 0000000000..036c6e3c25
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml
@@ -0,0 +1,49 @@
+<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'>8</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-system-i686</emulator>
+ <disk type='block' device='lun'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'>
+ <reservations enabled='yes' managed='yes'/>
+ </source>
+ <target dev='sda' bus='scsi'/>
+ <address type='drive' controller='0' bus='0'
target='0' unit='0'/>
+ </disk>
+ <disk type='block' device='lun'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest2'>
+ <reservations enabled='yes' managed='no'>
+ <source type='unix' path='/path/to/qemu-pr-helper.sock'
mode='client'/>
+ </reservations>
+ </source>
+ <target dev='sdb' bus='scsi'/>
+ <address type='drive' controller='0' bus='0'
target='0' unit='1'/>
+ </disk>
+ <controller type='usb' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='scsi' index='0' model='virtio-scsi'>
+ <driver queues='8'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x04' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml
b/tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml
new file mode 120000
index 0000000000..dde52fd1db
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml
@@ -0,0 +1 @@
+../qemuxml2argvdata/disk-virtio-scsi-reservations.xml
\ No newline at end of file
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 9e77b9fb13..871f01edd7 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -387,6 +387,8 @@ mymain(void)
QEMU_CAPS_VIRTIO_SCSI);
DO_TEST("disk-virtio-scsi-num_queues",
QEMU_CAPS_VIRTIO_SCSI);
+ DO_TEST("disk-virtio-scsi-reservations",
+ QEMU_CAPS_VIRTIO_SCSI);
DO_TEST("disk-virtio-scsi-cmd_per_lun",
QEMU_CAPS_VIRTIO_SCSI);
DO_TEST("disk-virtio-scsi-max_sectors",
--
2.16.1