virDomainVcpuParse(virDomainDefPtr def,
                   xmlXPathContextPtr ctxt,
                   virDomainXMLOptionPtr xmlopt)
{
    int n;
    xmlNodePtr vcpuNode;
    size_t i;
    unsigned int maxvcpus;
    unsigned int vcpus;
    g_autofree char *tmp = NULL;
    g_autofree xmlNodePtr *nodes = NULL;

    vcpus = maxvcpus = 1;

    if ((vcpuNode = virXPathNode("./vcpu[1]", ctxt))) {
        if ((tmp = virXMLNodeContentString(vcpuNode))) {
            if (virStrToLong_ui(tmp, NULL, 10, &maxvcpus) < 0) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("maximum vcpus count must be an integer"));
                return -1;
            }
            VIR_FREE(tmp);
        }

        if ((tmp = virXMLPropString(vcpuNode, "current"))) {
            if (virStrToLong_ui(tmp, NULL, 10, &vcpus) < 0) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("current vcpus count must be an integer"));
                return -1;
            }
            VIR_FREE(tmp);
        } else {
            vcpus = maxvcpus;
        }

        tmp = virXMLPropString(vcpuNode, "placement");
        if (tmp) {
            if ((def->placement_mode =
                 virDomainCpuPlacementModeTypeFromString(tmp)) < 0) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("Unsupported CPU placement mode '%s'"),
                               tmp);
                return -1;
            }
            VIR_FREE(tmp);
        } else {
            def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC;
        }

        if (def->placement_mode != VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) {
            tmp = virXMLPropString(vcpuNode, "cpuset");
            if (tmp) {
                if (virBitmapParse(tmp, &def->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0)
                    return -1;

                if (virBitmapIsAllClear(def->cpumask)) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("Invalid value of 'cpuset': %s"), tmp);
                    return -1;
                }

                VIR_FREE(tmp);
            }
        }
    }

    if (virDomainDefSetVcpusMax(def, maxvcpus, xmlopt) < 0)
        return -1;

    if ((n = virXPathNodeSet("./vcpus/vcpu", ctxt, &nodes)) < 0)
        return -1;

    if (n) {
        /* if individual vcpu states are provided take them as master */
        def->individualvcpus = true;

        for (i = 0; i < n; i++) {
            virDomainVcpuDefPtr vcpu;
            int state;
            unsigned int id;
            unsigned int order;

            if (!(tmp = virXMLPropString(nodes[i], "id")) ||
                virStrToLong_uip(tmp, NULL, 10, &id) < 0) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("missing or invalid vcpu id"));
                return -1;
            }

            VIR_FREE(tmp);

            if (id >= def->maxvcpus) {
                virReportError(VIR_ERR_XML_ERROR,
                               _("vcpu id '%u' is out of range of maximum "
                                 "vcpu count"), id);
                return -1;
            }

            vcpu = virDomainDefGetVcpu(def, id);

            if (!(tmp = virXMLPropString(nodes[i], "enabled"))) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("missing vcpu enabled state"));
                return -1;
            }

            if ((state = virTristateBoolTypeFromString(tmp)) < 0) {
                virReportError(VIR_ERR_XML_ERROR,
                               _("invalid vcpu 'enabled' value '%s'"), tmp);
                return -1;
            }
            VIR_FREE(tmp);

            vcpu->online = state == VIR_TRISTATE_BOOL_YES;

            if ((tmp = virXMLPropString(nodes[i], "hotpluggable"))) {
                int hotpluggable;
                if ((hotpluggable = virTristateBoolTypeFromString(tmp)) < 0) {
                    virReportError(VIR_ERR_XML_ERROR,
                                   _("invalid vcpu 'hotpluggable' value '%s'"), tmp);
                    return -1;
                }
                vcpu->hotpluggable = hotpluggable;
                VIR_FREE(tmp);
            }

            if ((tmp = virXMLPropString(nodes[i], "order"))) {
                if (virStrToLong_uip(tmp, NULL, 10, &order) < 0) {
                    virReportError(VIR_ERR_XML_ERROR, "%s",
                                   _("invalid vcpu order"));
                    return -1;
                }
                vcpu->order = order;
                VIR_FREE(tmp);
            }
        }
    } else {
        if (virDomainDefSetVcpus(def, vcpus) < 0)
            return -1;
    }

    return 0;
}