virDomainResctrlMonDefParse(virDomainDefPtr def,
                            xmlXPathContextPtr ctxt,
                            xmlNodePtr node,
                            virResctrlMonitorType tag,
                            virDomainResctrlDefPtr resctrl)
{
    virDomainResctrlMonDefPtr domresmon = NULL;
    VIR_XPATH_NODE_AUTORESTORE(ctxt);
    unsigned int level = 0;
    size_t i = 0;
    int n = 0;
    int rv = -1;
    int ret = -1;
    g_autofree xmlNodePtr *nodes = NULL;
    g_autofree char *tmp = NULL;
    g_autofree char *id = NULL;

    ctxt->node = node;

    if ((n = virXPathNodeSet("./monitor", ctxt, &nodes)) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Cannot extract monitor nodes"));
        goto cleanup;
    }

    for (i = 0; i < n; i++) {
        if (VIR_ALLOC(domresmon) < 0)
            goto cleanup;

        domresmon->tag = tag;

        domresmon->instance = virResctrlMonitorNew();
        if (!domresmon->instance) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Could not create monitor"));
            goto cleanup;
        }

        if (tag == VIR_RESCTRL_MONITOR_TYPE_CACHE) {
            tmp = virXMLPropString(nodes[i], "level");
            if (!tmp) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("Missing monitor attribute 'level'"));
                goto cleanup;
            }

            if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) {
                virReportError(VIR_ERR_XML_ERROR,
                               _("Invalid monitor attribute 'level' value '%s'"),
                               tmp);
                goto cleanup;
            }

            if (level != VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL) {
                virReportError(VIR_ERR_XML_ERROR,
                               _("Invalid monitor cache level '%d'"),
                               level);
                goto cleanup;
            }

            VIR_FREE(tmp);
        }

        if (virDomainResctrlParseVcpus(def, nodes[i], &domresmon->vcpus) < 0)
            goto cleanup;

        rv = virDomainResctrlValidateMonitor(resctrl, domresmon);
        if (rv < 0)
            goto cleanup;

        /* If monitor's vcpu list is identical to the vcpu list of the
         * associated allocation, set monitor's id to the same value
         * as the allocation. */
        if (rv == 1) {
            const char *alloc_id = virResctrlAllocGetID(resctrl->alloc);

            id = g_strdup(alloc_id);
        } else {
            if (!(tmp = virBitmapFormat(domresmon->vcpus)))
                goto cleanup;

            id = g_strdup_printf("vcpus_%s", tmp);
        }

        virResctrlMonitorSetAlloc(domresmon->instance, resctrl->alloc);

        if (virResctrlMonitorSetID(domresmon->instance, id) < 0)
            goto cleanup;

        if (VIR_APPEND_ELEMENT(resctrl->monitors,
                               resctrl->nmonitors,
                               domresmon) < 0)
            goto cleanup;

        VIR_FREE(id);
        VIR_FREE(tmp);
    }

    ret = 0;
 cleanup:
    virDomainResctrlMonDefFree(domresmon);
    return ret;
}