[libvirt PATCH v2 06/16] qemu: implement persistent file cache for nbdkit caps
Peter Krempa
pkrempa at redhat.com
Tue Sep 27 11:49:58 UTC 2022
On Wed, Aug 31, 2022 at 13:40:51 -0500, Jonathon Jongsma wrote:
> Implement the loadFile and saveFile virFileCacheHandlers callbacks so
> that nbdkit capabilities are cached perstistently across daemon
> restarts. The format and implementation is modeled on the qemu
> capabilities, but simplified slightly.
>
> Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
> ---
> src/qemu/qemu_nbdkit.c | 253 ++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 251 insertions(+), 2 deletions(-)
>
> diff --git a/src/qemu/qemu_nbdkit.c b/src/qemu/qemu_nbdkit.c
> index 3faa16674a..ac498b948c 100644
> --- a/src/qemu/qemu_nbdkit.c
> +++ b/src/qemu/qemu_nbdkit.c
> @@ -339,11 +339,260 @@ virNbdkitCapsNewData(const char *binary,
> }
>
>
> +static int
> +qemuNbdkitCapsValidateBinary(qemuNbdkitCaps *nbdkitCaps,
> + xmlXPathContextPtr ctxt)
> +{
> + g_autofree char *str = NULL;
> +
> + if (!(str = virXPathString("string(./path)", ctxt))) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("missing path in nbdkit capabilities cache"));
> + return -1;
> + }
> +
> + if (STRNEQ(str, nbdkitCaps->path)) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Expected caps for '%s' but saw '%s'"),
> + nbdkitCaps->path, str);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +
> +static int
> +qemuNbdkitCapsParseFlags(qemuNbdkitCaps *nbdkitCaps,
> + xmlXPathContextPtr ctxt)
> +{
> + g_autofree xmlNodePtr *nodes = NULL;
> + size_t i;
> + int n;
> +
> + if ((n = virXPathNodeSet("./flag", ctxt, &nodes)) < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("failed to parse qemu capabilities flags"));
> + return -1;
> + }
> +
> + VIR_DEBUG("Got flags %d", n);
> + for (i = 0; i < n; i++) {
> + g_autofree char *str = NULL;
> + int flag;
> +
> + if (!(str = virXMLPropString(nodes[i], "name"))) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("missing flag name in QEMU capabilities cache"));
> + return -1;
> + }
> +
> + flag = qemuNbdkitCapsTypeFromString(str);
> + if (flag < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Unknown nbdkit capabilities flag %s"), str);
> + return -1;
> + }
You can use virXMLPropEnum instead of having two clauses.
> +
> + qemuNbdkitCapsSet(nbdkitCaps, flag);
> + }
> +
> + return 0;
> +}
> +
> +
> +/*
> + * Parsing a doc that looks like
> + *
> + * <nbdkitCaps>
> + * <path>/some/path</path>
> + * <nbdkitctime>234235253</nbdkitctime>
> + * <plugindirmtime>234235253</plugindirmtime>
> + * <filterdirmtime>234235253</filterdirmtime>
> + * <selfctime>234235253</selfctime>
> + * <selfvers>1002016</selfvers>
> + * <flag name='foo'/>
> + * <flag name='bar'/>
> + * ...
> + * </nbdkitCaps>
> + *
> + * Returns 0 on success, 1 if outdated, -1 on error
> + */
> +static int
> +qemuNbdkitCapsLoadCache(qemuNbdkitCaps *nbdkitCaps,
> + const char *filename)
> +{
> + g_autoptr(xmlDoc) doc = NULL;
> + g_autoptr(xmlXPathContext) ctxt = NULL;
> + long long int l;
> + unsigned long lu;
> +
> + if (!(doc = virXMLParseFile(filename)))
I'll be posting soon patches that make 'virXMLParse' usable here without
the need to ...
> + return -1;
> +
> + if (!(ctxt = virXMLXPathContextNew(doc)))
> + return -1;
... fetch the context ...
> +
> + ctxt->node = xmlDocGetRootElement(doc);
... get the root node...
> +
> + if (STRNEQ((const char*)ctxt->node->name, "nbdkitCaps")) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("unexpected root element <%s>, "
> + "expecting <nbdkitCaps>"),
> + ctxt->node->name);
> + return -1;
> + }
... and also do this validation by doing all of that in the virXMLParse
function.
> +
> + if (virXPathLongLong("string(./selfctime)", ctxt, &l) < 0) {
> + virReportError(VIR_ERR_XML_ERROR, "%s",
> + _("missing selfctime in nbdkit capabilities XML"));
> + return -1;
> + }
> + nbdkitCaps->libvirtCtime = (time_t)l;
> +
> + nbdkitCaps->libvirtVersion = 0;
> + if (virXPathULong("string(./selfvers)", ctxt, &lu) == 0)
> + nbdkitCaps->libvirtVersion = lu;
> +
> + if (nbdkitCaps->libvirtCtime != virGetSelfLastChanged() ||
> + nbdkitCaps->libvirtVersion != LIBVIR_VERSION_NUMBER) {
> + VIR_DEBUG("Outdated capabilities in %s: libvirt changed "
> + "(%lld vs %lld, %lu vs %lu), stopping load",
> + nbdkitCaps->path,
> + (long long)nbdkitCaps->libvirtCtime,
> + (long long)virGetSelfLastChanged(),
> + (unsigned long)nbdkitCaps->libvirtVersion,
> + (unsigned long)LIBVIR_VERSION_NUMBER);
> + return 1;
> + }
> +
> + if (qemuNbdkitCapsValidateBinary(nbdkitCaps, ctxt) < 0)
> + return -1;
> +
> + if (virXPathLongLong("string(./nbdkitctime)", ctxt, &l) < 0) {
> + virReportError(VIR_ERR_XML_ERROR, "%s",
> + _("missing nbdkitctime in nbdkit capabilities XML"));
> + return -1;
> + }
> + nbdkitCaps->ctime = (time_t)l;
> +
> + if (virXPathLongLong("string(./plugindirmtime)", ctxt, &l) == 0)
> + nbdkitCaps->pluginDirMtime = (time_t)l;
> +
> + if (virXPathLongLong("string(./filterdirmtime)", ctxt, &l) == 0)
> + nbdkitCaps->filterDirMtime = (time_t)l;
Any reason why these are optional but the ctime of nbdkit is not?
> +
> + if (qemuNbdkitCapsParseFlags(nbdkitCaps, ctxt) < 0)
> + return -1;
> +
> + if ((nbdkitCaps->version = virXPathString("string(./version)", ctxt)) == NULL) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("missing version in nbdkit capabilities cache"));
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +
> +static void*
> +virNbdkitCapsLoadFile(const char *filename,
> + const char *binary,
> + void *privData G_GNUC_UNUSED,
> + bool *outdated)
> +{
> + g_autoptr(qemuNbdkitCaps) nbdkitCaps = qemuNbdkitCapsNew(binary);
> + int ret;
> +
> + if (!nbdkitCaps)
> + return NULL;
> +
> + ret = qemuNbdkitCapsLoadCache(nbdkitCaps, filename);
> + if (ret < 0)
> + return NULL;
> + if (ret == 1) {
> + *outdated = true;
> + return NULL;
> + }
> +
> + return g_steal_pointer(&nbdkitCaps);
> +}
> +
> +
> +static char*
> +qemuNbdkitCapsFormatCache(qemuNbdkitCaps *nbdkitCaps)
> +{
> + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
> + size_t i;
> +
> + virBufferAddLit(&buf, "<nbdkitCaps>\n");
> + virBufferAdjustIndent(&buf, 2);
> +
> + virBufferEscapeString(&buf, "<path>%s</path>\n",
> + nbdkitCaps->path);
> + virBufferAsprintf(&buf, "<nbdkitctime>%llu</nbdkitctime>\n",
> + (long long)nbdkitCaps->ctime);
> + if (nbdkitCaps->pluginDirMtime > 0) {
> + virBufferAsprintf(&buf, "<plugindirmtime>%llu</plugindirmtime>\n",
The format substitution is specifying an unsigned long long ...
> + (long long)nbdkitCaps->pluginDirMtime);
while you typecast to long long. Similarly above in the parser you parse
as long long instead of unsigned long long.
> + }
> + if (nbdkitCaps->filterDirMtime > 0) {
> + virBufferAsprintf(&buf, "<filterdirmtime>%llu</filterdirmtime>\n",
> + (long long)nbdkitCaps->filterDirMtime);
> + }
> + virBufferAsprintf(&buf, "<selfctime>%llu</selfctime>\n",
> + (long long)nbdkitCaps->libvirtCtime);
> + virBufferAsprintf(&buf, "<selfvers>%lu</selfvers>\n",
> + (unsigned long)nbdkitCaps->libvirtVersion);
> +
> + for (i = 0; i < QEMU_NBDKIT_CAPS_LAST; i++) {
> + if (qemuNbdkitCapsGet(nbdkitCaps, i)) {
> + virBufferAsprintf(&buf, "<flag name='%s'/>\n",
> + qemuNbdkitCapsTypeToString(i));
> + }
> + }
> +
> + virBufferAsprintf(&buf, "<version>%s</version>\n",
> + nbdkitCaps->version);
> +
> + virBufferAdjustIndent(&buf, -2);
> + virBufferAddLit(&buf, "</nbdkitCaps>\n");
> +
> + return virBufferContentAndReset(&buf);
> +}
> +
> +
> +static int
> +virNbdkitCapsSaveFile(void *data,
> + const char *filename,
> + void *privData G_GNUC_UNUSED)
> +{
> + qemuNbdkitCaps *nbdkitCaps = data;
> + g_autofree char *xml = NULL;
> +
> + xml = qemuNbdkitCapsFormatCache(nbdkitCaps);
> +
> + if (virFileWriteStr(filename, xml, 0600) < 0) {
> + virReportSystemError(errno,
> + _("Failed to save '%s' for '%s'"),
> + filename, nbdkitCaps->path);
> + return -1;
> + }
> +
> + VIR_DEBUG("Saved caps '%s' for '%s' with (%lld, %lld)",
> + filename, nbdkitCaps->path,
> + (long long)nbdkitCaps->ctime,
> + (long long)nbdkitCaps->libvirtCtime);
> +
> + return 0;
> +}
> +
> +
> virFileCacheHandlers nbdkitCapsCacheHandlers = {
> .isValid = virNbdkitCapsIsValid,
> .newData = virNbdkitCapsNewData,
> - .loadFile = NULL,
> - .saveFile = NULL,
> + .loadFile = virNbdkitCapsLoadFile,
> + .saveFile = virNbdkitCapsSaveFile,
> .privFree = NULL,
> };
>
> --
> 2.37.1
>
More information about the libvir-list
mailing list