virNodeDeviceDefParseXML(xmlXPathContextPtr ctxt,
                         int create,
                         const char *virt_type)
{
    virNodeDeviceDefPtr def;
    virNodeDevCapsDefPtr *next_cap;
    xmlNodePtr *nodes = NULL;
    int n, m;
    size_t i;

    def = g_new0(virNodeDeviceDef, 1);

    /* Extract device name */
    if (create == EXISTING_DEVICE) {
        def->name = virXPathString("string(./name[1])", ctxt);

        if (!def->name) {
            virReportError(VIR_ERR_NO_NAME, NULL);
            goto error;
        }
    } else {
        def->name = g_strdup("new device");
    }

    def->sysfs_path = virXPathString("string(./path[1])", ctxt);

    /* Parse devnodes */
    if ((n = virXPathNodeSet("./devnode", ctxt, &nodes)) < 0)
        goto error;

    def->devlinks = g_new0(char *, n + 1);

    for (i = 0, m = 0; i < n; i++) {
        xmlNodePtr node = nodes[i];
        char *tmp = virXMLPropString(node, "type");
        int val;

        if (!tmp) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing devnode type"));
            goto error;
        }

        val = virNodeDevDevnodeTypeFromString(tmp);

        if (val < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown devnode type '%s'"), tmp);
            VIR_FREE(tmp);
            goto error;
        }
        VIR_FREE(tmp);

        switch ((virNodeDevDevnodeType)val) {
        case VIR_NODE_DEV_DEVNODE_DEV:
            if (!(def->devnode = virXMLNodeContentString(node)))
                goto error;
            break;
        case VIR_NODE_DEV_DEVNODE_LINK:
            if (!(def->devlinks[m++] = virXMLNodeContentString(node)))
                goto error;
            break;
        case VIR_NODE_DEV_DEVNODE_LAST:
            break;
        }
    }

    /* Extract device parent, if any */
    def->parent = virXPathString("string(./parent[1])", ctxt);
    def->parent_wwnn = virXPathString("string(./parent[1]/@wwnn)", ctxt);
    def->parent_wwpn = virXPathString("string(./parent[1]/@wwpn)", ctxt);
    if (def->parent_wwnn && !def->parent_wwpn) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("when providing parent wwnn='%s', the "
                         "wwpn must also be provided"),
                       def->parent_wwnn);
        goto error;
    }

    if (!def->parent_wwnn && def->parent_wwpn) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("when providing parent wwpn='%s', the "
                         "wwnn must also be provided"),
                       def->parent_wwpn);
        goto error;
    }
    def->parent_fabric_wwn = virXPathString("string(./parent[1]/@fabric_wwn)",
                                            ctxt);

    /* Parse device capabilities */
    VIR_FREE(nodes);
    if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0)
        goto error;

    if (n == 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("no device capabilities for '%s'"),
                       def->name);
        goto error;
    }

    next_cap = &def->caps;
    for (i = 0; i < n; i++) {
        *next_cap = virNodeDevCapsDefParseXML(ctxt, def,
                                              nodes[i],
                                              create,
                                              virt_type);
        if (!*next_cap)
            goto error;

        next_cap = &(*next_cap)->next;
    }
    VIR_FREE(nodes);

    return def;

 error:
    virNodeDeviceDefFree(def);
    VIR_FREE(nodes);
    return NULL;
}