virstoragefile: Introduce virStoragePRDef

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@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
This commit is contained in:
Michal Privoznik 2017-11-27 11:54:33 +01:00
parent 4a3d6ed5ee
commit 687730540e
10 changed files with 316 additions and 31 deletions

View File

@ -2566,7 +2566,10 @@
&lt;/disk&gt;
&lt;disk type='block' device='lun'&gt;
&lt;driver name='qemu' type='raw'/&gt;
&lt;source dev='/dev/sda'/&gt;
&lt;source dev='/dev/sda'&gt;
&lt;reservations enabled='yes' managed='no'&gt;
&lt;source type='unix' path='/path/to/qemu-pr-helper' mode='client'/&gt;
&lt;/reservations&gt;
&lt;target dev='sda' bus='scsi'/&gt;
&lt;address type='drive' controller='0' bus='0' target='3' unit='0'/&gt;
&lt;/disk&gt;
@ -2929,6 +2932,26 @@
<a href="formatstorageencryption.html">Storage Encryption</a>
page for more information.
</dd>
<dt><code>reservations</code></dt>
<dd><span class="since">Since libvirt 4.4.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 one value
<code>client</code> and specifies the role of hypervisor.
It's recommended to allow libvirt manage the persistent
reservations.
</dd>
</dl>
<p>

View File

@ -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>

View File

@ -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'>

View File

@ -5206,6 +5206,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
@ -8632,6 +8639,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,
@ -8678,6 +8710,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;
@ -22953,6 +22988,9 @@ virDomainStorageSourceFormat(virBufferPtr attrBuf,
virStorageEncryptionFormat(childBuf, src->encryption) < 0)
return -1;
if (src->pr)
virStoragePRDefFormat(childBuf, src->pr);
return 0;
}

View File

@ -2801,6 +2801,9 @@ virStorageNetHostDefFree;
virStorageNetHostTransportTypeFromString;
virStorageNetHostTransportTypeToString;
virStorageNetProtocolTypeToString;
virStoragePRDefFormat;
virStoragePRDefFree;
virStoragePRDefParseXML;
virStorageSourceBackingStoreClear;
virStorageSourceClear;
virStorageSourceCopy;

View File

@ -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)
@ -2268,6 +2398,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);

View File

@ -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;
@ -382,6 +391,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);

View File

@ -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>

View File

@ -0,0 +1 @@
../qemuxml2argvdata/disk-virtio-scsi-reservations.xml

View File

@ -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",