mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 17:34:18 +03:00
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:
parent
4a3d6ed5ee
commit
687730540e
@ -2566,7 +2566,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>
|
||||
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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'>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -2801,6 +2801,9 @@ virStorageNetHostDefFree;
|
||||
virStorageNetHostTransportTypeFromString;
|
||||
virStorageNetHostTransportTypeToString;
|
||||
virStorageNetProtocolTypeToString;
|
||||
virStoragePRDefFormat;
|
||||
virStoragePRDefFree;
|
||||
virStoragePRDefParseXML;
|
||||
virStorageSourceBackingStoreClear;
|
||||
virStorageSourceClear;
|
||||
virStorageSourceCopy;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
49
tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml
Normal file
49
tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml
Normal 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>
|
1
tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml
Symbolic link
1
tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml
Symbolic link
@ -0,0 +1 @@
|
||||
../qemuxml2argvdata/disk-virtio-scsi-reservations.xml
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user