Fixup handling of HVM boot preference, and include HVM cdrom/floppy in main device list

This commit is contained in:
Daniel P. Berrange 2006-08-11 14:40:04 +00:00
parent f87c6d4734
commit e1ec9651a2
4 changed files with 182 additions and 57 deletions

View File

@ -1,3 +1,20 @@
Fri Aug 11 09:37:02 EDT 2006 Daniel Berrange <berrange@redhat.com>
* src/libvirt.c: Avoid duplicated attempts to shutdown or
pause a domain if the first attempt succeeded.
* src/xend_internal.c, src/xml.c: When parsing UUID from
SEXPR also allow for format without any embedded '-'. The
ioemu: prefix is no longer required for HVM domains. It is
added when generating SEXPR, and removing when parsing SEXPR
never appearing in XML. CDROM & floppy devices for HVM domains
are now included in XML under <devices><disk> tag. The <disk>
tag now has a 'device' attribute allowing one of 'floppy',
'cdrom', 'disk' to be specified. If the <console> tag is present
in XML, HVM domains get a serial console activated. <boot>
tag now expects one of 'fd' 'hd' or 'cdrom' when specifying
boot device preference. Increased size of XML doc buffer from
1k to 4k to deal with large numbers of devices
Fri Aug 11 13:08:01 CEST 2006 Daniel Veillard <veillard@redhat.com> Fri Aug 11 13:08:01 CEST 2006 Daniel Veillard <veillard@redhat.com>
* configure.in: updated python detection code from latest libxml2 one * configure.in: updated python detection code from latest libxml2 one

View File

@ -720,7 +720,7 @@ virDomainLookupByName(virConnectPtr conn, const char *name)
int int
virDomainDestroy(virDomainPtr domain) virDomainDestroy(virDomainPtr domain)
{ {
int ret = -1, i; int i;
virConnectPtr conn; virConnectPtr conn;
if (!VIR_IS_CONNECTED_DOMAIN(domain)) { if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
@ -743,7 +743,7 @@ virDomainDestroy(virDomainPtr domain)
(conn->drivers[i]->no != VIR_DRV_XEN_HYPERVISOR) && (conn->drivers[i]->no != VIR_DRV_XEN_HYPERVISOR) &&
(conn->drivers[i]->domainDestroy != NULL)) { (conn->drivers[i]->domainDestroy != NULL)) {
if (conn->drivers[i]->domainDestroy(domain) == 0) if (conn->drivers[i]->domainDestroy(domain) == 0)
ret = 0; return (0);
} }
} }
for (i = 0;i < conn->nb_drivers;i++) { for (i = 0;i < conn->nb_drivers;i++) {
@ -751,16 +751,12 @@ virDomainDestroy(virDomainPtr domain)
(conn->drivers[i]->no == VIR_DRV_XEN_HYPERVISOR) && (conn->drivers[i]->no == VIR_DRV_XEN_HYPERVISOR) &&
(conn->drivers[i]->domainDestroy != NULL)) { (conn->drivers[i]->domainDestroy != NULL)) {
if (conn->drivers[i]->domainDestroy(domain) == 0) if (conn->drivers[i]->domainDestroy(domain) == 0)
ret = 0; return (0);
} }
} }
if (ret != 0) {
virLibConnError(conn, VIR_ERR_CALL_FAILED, __FUNCTION__); virLibConnError(conn, VIR_ERR_CALL_FAILED, __FUNCTION__);
return (ret); return (-1);
}
return (ret);
} }
/** /**
@ -799,7 +795,7 @@ virDomainFree(virDomainPtr domain)
int int
virDomainSuspend(virDomainPtr domain) virDomainSuspend(virDomainPtr domain)
{ {
int ret = -1, i; int i;
virConnectPtr conn; virConnectPtr conn;
if (!VIR_IS_CONNECTED_DOMAIN(domain)) { if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
@ -822,7 +818,7 @@ virDomainSuspend(virDomainPtr domain)
(conn->drivers[i]->no != VIR_DRV_XEN_HYPERVISOR) && (conn->drivers[i]->no != VIR_DRV_XEN_HYPERVISOR) &&
(conn->drivers[i]->domainSuspend != NULL)) { (conn->drivers[i]->domainSuspend != NULL)) {
if (conn->drivers[i]->domainSuspend(domain) == 0) if (conn->drivers[i]->domainSuspend(domain) == 0)
ret = 0; return (0);
} }
} }
for (i = 0;i < conn->nb_drivers;i++) { for (i = 0;i < conn->nb_drivers;i++) {
@ -830,16 +826,12 @@ virDomainSuspend(virDomainPtr domain)
(conn->drivers[i]->no == VIR_DRV_XEN_HYPERVISOR) && (conn->drivers[i]->no == VIR_DRV_XEN_HYPERVISOR) &&
(conn->drivers[i]->domainSuspend != NULL)) { (conn->drivers[i]->domainSuspend != NULL)) {
if (conn->drivers[i]->domainSuspend(domain) == 0) if (conn->drivers[i]->domainSuspend(domain) == 0)
ret = 0; return (0);
} }
} }
if (ret != 0) {
virLibConnError(conn, VIR_ERR_CALL_FAILED, __FUNCTION__); virLibConnError(conn, VIR_ERR_CALL_FAILED, __FUNCTION__);
return (ret); return (-1);
}
return (ret);
} }
/** /**

View File

@ -790,6 +790,18 @@ sexpr_uuid(char **ptr, struct sexpr *node, const char *path)
if (r == NULL) if (r == NULL)
goto error; goto error;
ret = sscanf(r,
"%02x%02x%02x%02x"
"%02x%02x%02x%02x"
"%02x%02x%02x%02x"
"%02x%02x%02x%02x",
uuid + 0, uuid + 1, uuid + 2, uuid + 3,
uuid + 4, uuid + 5, uuid + 6, uuid + 7,
uuid + 8, uuid + 9, uuid + 10, uuid + 11,
uuid + 12, uuid + 13, uuid + 14, uuid + 15);
if (ret == 16)
goto done;
ret = sscanf(r, ret = sscanf(r,
"%02x%02x%02x%02x-" "%02x%02x%02x%02x-"
"%02x%02x-" "%02x%02x-"
@ -1416,21 +1428,19 @@ xend_parse_sexp_desc_os(struct sexpr *node, virBufferPtr buf, int hvm)
virBufferVSprintf(buf, " <loader>%s</loader>\n", tmp); virBufferVSprintf(buf, " <loader>%s</loader>\n", tmp);
tmp = sexpr_node(node, "domain/image/hvm/boot"); tmp = sexpr_node(node, "domain/image/hvm/boot");
if ((tmp != NULL) && (tmp[0] != 0)) { if ((tmp != NULL) && (tmp[0] != 0)) {
/*
* FIXME:
* Figure out how to map the 'a', 'b', 'c' nonsense to a
* device.
*/
if (tmp[0] == 'a') if (tmp[0] == 'a')
virBufferAdd(buf, " <boot dev='/dev/fd0'/>\n", 25 ); /* XXX no way to deal with boot from 2nd floppy */
virBufferAdd(buf, " <boot dev='fd'/>\n", 21 );
else if (tmp[0] == 'c') else if (tmp[0] == 'c')
/* /*
* Don't know what to put here. Say the vm has been given 3 * Don't know what to put here. Say the vm has been given 3
* disks - hda, hdb, hdc. How does one identify the boot disk? * disks - hda, hdb, hdc. How does one identify the boot disk?
* We're going to assume that first disk is the boot disk since
* this is most common practice
*/ */
virBufferAdd(buf, " <boot dev='hda'/>\n", 22 ); virBufferAdd(buf, " <boot dev='hd'/>\n", 21 );
else if (strcmp(tmp, "d") == 0) else if (strcmp(tmp, "d") == 0)
virBufferAdd(buf, " <boot dev='/dev/cdrom'/>\n", 29 ); virBufferAdd(buf, " <boot dev='cdrom'/>\n", 24 );
} }
} else { } else {
virBufferVSprintf(buf, " <type>linux</type>\n"); virBufferVSprintf(buf, " <type>linux</type>\n");
@ -1482,11 +1492,11 @@ xend_parse_sexp_desc(virConnectPtr conn, struct sexpr *root)
/* ERROR */ /* ERROR */
return (NULL); return (NULL);
} }
ret = malloc(1000); ret = malloc(4000);
if (ret == NULL) if (ret == NULL)
return (NULL); return (NULL);
buf.content = ret; buf.content = ret;
buf.size = 1000; buf.size = 4000;
buf.use = 0; buf.use = 0;
domid = sexpr_int(root, "domain/domid"); domid = sexpr_int(root, "domain/domid");
@ -1552,7 +1562,7 @@ xend_parse_sexp_desc(virConnectPtr conn, struct sexpr *root)
continue; continue;
if (!memcmp(tmp, "file:", 5)) { if (!memcmp(tmp, "file:", 5)) {
tmp += 5; tmp += 5;
virBufferVSprintf(&buf, " <disk type='file'>\n"); virBufferVSprintf(&buf, " <disk type='file' device='disk'>\n");
virBufferVSprintf(&buf, " <source file='%s'/>\n", virBufferVSprintf(&buf, " <source file='%s'/>\n",
tmp); tmp);
tmp = sexpr_node(node, "device/vbd/dev"); tmp = sexpr_node(node, "device/vbd/dev");
@ -1561,6 +1571,8 @@ xend_parse_sexp_desc(virConnectPtr conn, struct sexpr *root)
"domain information incomplete, vbd has no dev"); "domain information incomplete, vbd has no dev");
goto error; goto error;
} }
if (!strncmp(tmp, "ioemu:", 6))
tmp += 6;
virBufferVSprintf(&buf, " <target dev='%s'/>\n", tmp); virBufferVSprintf(&buf, " <target dev='%s'/>\n", tmp);
tmp = sexpr_node(node, "device/vbd/mode"); tmp = sexpr_node(node, "device/vbd/mode");
if ((tmp != NULL) && (!strcmp(tmp, "r"))) if ((tmp != NULL) && (!strcmp(tmp, "r")))
@ -1568,7 +1580,7 @@ xend_parse_sexp_desc(virConnectPtr conn, struct sexpr *root)
virBufferAdd(&buf, " </disk>\n", 12); virBufferAdd(&buf, " </disk>\n", 12);
} else if (!memcmp(tmp, "phy:", 4)) { } else if (!memcmp(tmp, "phy:", 4)) {
tmp += 4; tmp += 4;
virBufferVSprintf(&buf, " <disk type='block'>\n"); virBufferVSprintf(&buf, " <disk type='block' device='disk'>\n");
virBufferVSprintf(&buf, " <source dev='%s'/>\n", tmp); virBufferVSprintf(&buf, " <source dev='%s'/>\n", tmp);
tmp = sexpr_node(node, "device/vbd/dev"); tmp = sexpr_node(node, "device/vbd/dev");
if (tmp == NULL) { if (tmp == NULL) {
@ -1576,6 +1588,8 @@ xend_parse_sexp_desc(virConnectPtr conn, struct sexpr *root)
"domain information incomplete, vbd has no dev"); "domain information incomplete, vbd has no dev");
goto error; goto error;
} }
if (!strncmp(tmp, "ioemu:", 6))
tmp += 6;
virBufferVSprintf(&buf, " <target dev='%s'/>\n", tmp); virBufferVSprintf(&buf, " <target dev='%s'/>\n", tmp);
tmp = sexpr_node(node, "device/vbd/mode"); tmp = sexpr_node(node, "device/vbd/mode");
if ((tmp != NULL) && (!strcmp(tmp, "r"))) if ((tmp != NULL) && (!strcmp(tmp, "r")))
@ -1625,6 +1639,30 @@ xend_parse_sexp_desc(virConnectPtr conn, struct sexpr *root)
} }
if (hvm) { if (hvm) {
tmp = sexpr_node(root, "domain/image/hvm/fda");
if ((tmp != NULL) && (tmp[0] != 0)) {
virBufferAdd(&buf, " <disk type='file' device='floppy'>\n", 39);
virBufferVSprintf(&buf, " <source file='%s'/>\n", tmp);
virBufferAdd(&buf, " <target dev='fda'/>\n", 26);
virBufferAdd(&buf, " </disk>\n", 12);
}
tmp = sexpr_node(root, "domain/image/hvm/fdb");
if ((tmp != NULL) && (tmp[0] != 0)) {
virBufferAdd(&buf, " <disk type='file' device='floppy'>\n", 39);
virBufferVSprintf(&buf, " <source file='%s'/>\n", tmp);
virBufferAdd(&buf, " <target dev='fdb'/>\n", 26);
virBufferAdd(&buf, " </disk>\n", 12);
}
/* XXX new (3.0.3) Xend puts cdrom devs in usual (devices) block */
tmp = sexpr_node(root, "domain/image/hvm/cdrom");
if ((tmp != NULL) && (tmp[0] != 0)) {
virBufferAdd(&buf, " <disk type='file' device='cdrom'>\n", 38);
virBufferVSprintf(&buf, " <source file='%s'/>\n", tmp);
virBufferAdd(&buf, " <target dev='hdc'/>\n", 26);
virBufferAdd(&buf, " <readonly/>\n", 18);
virBufferAdd(&buf, " </disk>\n", 12);
}
/* Graphics device */ /* Graphics device */
tmp = sexpr_node(root, "domain/image/hvm/vnc"); tmp = sexpr_node(root, "domain/image/hvm/vnc");
if (tmp != NULL) { if (tmp != NULL) {
@ -1641,11 +1679,6 @@ xend_parse_sexp_desc(virConnectPtr conn, struct sexpr *root)
if (tmp[0] == '1') if (tmp[0] == '1')
virBufferAdd(&buf, " <graphics type='sdl'/>\n", 27 ); virBufferAdd(&buf, " <graphics type='sdl'/>\n", 27 );
} }
/*
* TODO:
* Device for cdrom
*/
} }
tty = xenStoreDomainGetConsolePath(conn, domid); tty = xenStoreDomainGetConsolePath(conn, domid);

117
src/xml.c
View File

@ -268,7 +268,10 @@ virDomainGetXMLDevice(virDomainPtr domain, virBufferPtr buf, long dev)
} }
val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "dev"); val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "dev");
if (val != NULL) { if (val != NULL) {
virBufferVSprintf(buf, " <target dev='%s'/>\n", val); char *tmp = val;
if (!strncmp(tmp, "ioemu:", 6))
tmp += 6;
virBufferVSprintf(buf, " <target dev='%s'/>\n", tmp);
free(val); free(val);
} }
val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "read-only"); val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "read-only");
@ -286,7 +289,10 @@ virDomainGetXMLDevice(virDomainPtr domain, virBufferPtr buf, long dev)
} }
val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "dev"); val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "dev");
if (val != NULL) { if (val != NULL) {
virBufferVSprintf(buf, " <target dev='%s'/>\n", val); char *tmp = val;
if (!strncmp(tmp, "ioemu:", 6))
tmp += 6;
virBufferVSprintf(buf, " <target dev='%s'/>\n", tmp);
free(val); free(val);
} }
val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "read-only"); val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "read-only");
@ -635,22 +641,72 @@ virDomainParseXMLOSDescHVM(xmlNodePtr node, virBufferPtr buf, xmlXPathContextPtr
obj = NULL; obj = NULL;
if (boot_dev) { if (boot_dev) {
/* TODO: if (xmlStrEqual(boot_dev, BAD_CAST "fd")) {
* Have to figure out the naming used here.
*/
if (xmlStrEqual(type, BAD_CAST "hda")) {
virBufferVSprintf(buf, "(boot a)", (const char *) boot_dev); virBufferVSprintf(buf, "(boot a)", (const char *) boot_dev);
} else if (xmlStrEqual(type, BAD_CAST "hdd")) { } else if (xmlStrEqual(boot_dev, BAD_CAST "cdrom")) {
virBufferVSprintf(buf, "(boot d)", (const char *) boot_dev); virBufferVSprintf(buf, "(boot d)", (const char *) boot_dev);
} else { } else if (xmlStrEqual(boot_dev, BAD_CAST "hd")) {
/* Force hd[b|c] if boot_dev specified but not floppy or cdrom? */
virBufferVSprintf(buf, "(boot c)", (const char *) boot_dev); virBufferVSprintf(buf, "(boot c)", (const char *) boot_dev);
} else {
/* Any other type of boot dev is unsupported right now */
virXMLError(VIR_ERR_XML_ERROR, NULL, 0);
}
/* get the 1st floppy device file */
obj = xmlXPathEval(BAD_CAST "/domain/devices/disk[@device='floppy' and target/@dev='fda']/source", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr == 1)) {
cur = obj->nodesetval->nodeTab[0];
virBufferVSprintf(buf, "(fda '%s')",
(const char *) xmlGetProp(cur, BAD_CAST "file"));
cur = NULL;
}
if (obj) {
xmlXPathFreeObject(obj);
obj = NULL;
}
/* get the 2nd floppy device file */
obj = xmlXPathEval(BAD_CAST "/domain/devices/disk[@device='floppy' and target/@dev='fdb']/source", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr == 1)) {
cur = obj->nodesetval->nodeTab[0];
virBufferVSprintf(buf, "(fdb '%s')",
(const char *) xmlGetProp(cur, BAD_CAST "file"));
cur = NULL;
}
if (obj) {
xmlXPathFreeObject(obj);
obj = NULL;
}
/* get the cdrom device file */
/* XXX new (3.0.3) Xend puts cdrom devs in usual (devices) block */
obj = xmlXPathEval(BAD_CAST "/domain/devices/disk[@device='cdrom' and target/@dev='hdc']/source", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr == 1)) {
cur = obj->nodesetval->nodeTab[0];
virBufferVSprintf(buf, "(cdrom '%s')",
(const char *) xmlGetProp(cur, BAD_CAST "file"));
cur = NULL;
}
if (obj) {
xmlXPathFreeObject(obj);
obj = NULL;
} }
} }
/* TODO:
* Is a cdrom disk device specified? obj = xmlXPathEval(BAD_CAST "count(domain/devices/console) > 0", ctxt);
* Kind of ugly since it is buried in the devices/diskk node. if ((obj == NULL) || (obj->type != XPATH_BOOLEAN)) {
*/ virXMLError(VIR_ERR_XML_ERROR, NULL, 0);
goto error;
}
if (obj->boolval) {
virBufferAdd(buf, "(serial pty)", 12);
}
xmlXPathFreeObject(obj);
obj = NULL;
/* Is a graphics device specified? */ /* Is a graphics device specified? */
obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics[1]", ctxt); obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics[1]", ctxt);
@ -779,10 +835,11 @@ virDomainParseXMLOSDescPV(xmlNodePtr node, virBufferPtr buf)
* Returns 0 in case of success, -1 in case of error. * Returns 0 in case of success, -1 in case of error.
*/ */
static int static int
virDomainParseXMLDiskDesc(xmlNodePtr node, virBufferPtr buf) virDomainParseXMLDiskDesc(xmlNodePtr node, virBufferPtr buf, int hvm)
{ {
xmlNodePtr cur; xmlNodePtr cur;
xmlChar *type = NULL; xmlChar *type = NULL;
xmlChar *device = NULL;
xmlChar *source = NULL; xmlChar *source = NULL;
xmlChar *target = NULL; xmlChar *target = NULL;
int ro = 0; int ro = 0;
@ -796,6 +853,8 @@ virDomainParseXMLDiskDesc(xmlNodePtr node, virBufferPtr buf)
typ = 1; typ = 1;
xmlFree(type); xmlFree(type);
} }
device = xmlGetProp(node, BAD_CAST "device");
cur = node->children; cur = node->children;
while (cur != NULL) { while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) { if (cur->type == XML_ELEMENT_NODE) {
@ -829,7 +888,29 @@ virDomainParseXMLDiskDesc(xmlNodePtr node, virBufferPtr buf)
xmlFree(source); xmlFree(source);
return (-1); return (-1);
} }
/* Skip floppy/cdrom disk used as the boot device
* since that's incorporated into the HVM kernel
* (image (hvm..)) part of the sexpr, rather than
* the (devices...) bit. Odd Xend HVM config :-(
* XXX This will have to change in Xen 3.0.3
*/
if (hvm && device &&
(!strcmp((const char *)device, "floppy") ||
!strcmp((const char *)device, "cdrom"))) {
return 0;
}
virBufferAdd(buf, "(device ", 8);
virBufferAdd(buf, "(vbd ", 5); virBufferAdd(buf, "(vbd ", 5);
/* XXX ioemu prefix is going away in Xen 3.0.3 */
if (hvm) {
char *tmp = (char *)target;
if (!strncmp((const char *) tmp, "ioemu:", 6))
tmp += 6;
virBufferVSprintf(buf, "(dev 'ioemu:%s')", (const char *) tmp);
} else
virBufferVSprintf(buf, "(dev '%s')", (const char *) target); virBufferVSprintf(buf, "(dev '%s')", (const char *) target);
if (typ == 0) if (typ == 0)
virBufferVSprintf(buf, "(uname 'file:%s')", source); virBufferVSprintf(buf, "(uname 'file:%s')", source);
@ -844,6 +925,7 @@ virDomainParseXMLDiskDesc(xmlNodePtr node, virBufferPtr buf)
else if (ro == 1) else if (ro == 1)
virBufferVSprintf(buf, "(mode 'r')"); virBufferVSprintf(buf, "(mode 'r')");
virBufferAdd(buf, ")", 1);
virBufferAdd(buf, ")", 1); virBufferAdd(buf, ")", 1);
xmlFree(target); xmlFree(target);
xmlFree(source); xmlFree(source);
@ -912,6 +994,7 @@ virDomainParseXMLIfDesc(xmlNodePtr node, virBufferPtr buf)
} }
if (script != NULL) if (script != NULL)
virBufferVSprintf(buf, "(script '%s')", script); virBufferVSprintf(buf, "(script '%s')", script);
virBufferAdd(buf, "(type ioemu)", 12);
virBufferAdd(buf, ")", 1); virBufferAdd(buf, ")", 1);
if (mac != NULL) if (mac != NULL)
@ -948,6 +1031,7 @@ virDomainParseXMLDesc(const char *xmldesc, char **name)
xmlXPathContextPtr ctxt = NULL; xmlXPathContextPtr ctxt = NULL;
int i, res; int i, res;
int bootloader = 0; int bootloader = 0;
int hvm = 0;
if (name != NULL) if (name != NULL)
*name = NULL; *name = NULL;
@ -1072,6 +1156,7 @@ virDomainParseXMLDesc(const char *xmldesc, char **name)
if ((tmpobj == NULL) || !xmlStrEqual(tmpobj->stringval, BAD_CAST "hvm")) { if ((tmpobj == NULL) || !xmlStrEqual(tmpobj->stringval, BAD_CAST "hvm")) {
res = virDomainParseXMLOSDescPV(obj->nodesetval->nodeTab[0], &buf); res = virDomainParseXMLOSDescPV(obj->nodesetval->nodeTab[0], &buf);
} else { } else {
hvm = 1;
res = virDomainParseXMLOSDescHVM(obj->nodesetval->nodeTab[0], &buf, ctxt); res = virDomainParseXMLOSDescHVM(obj->nodesetval->nodeTab[0], &buf, ctxt);
} }
@ -1090,12 +1175,10 @@ virDomainParseXMLDesc(const char *xmldesc, char **name)
if ((obj != NULL) && (obj->type == XPATH_NODESET) && if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
for (i = 0; i < obj->nodesetval->nodeNr; i++) { for (i = 0; i < obj->nodesetval->nodeNr; i++) {
virBufferAdd(&buf, "(device ", 8); res = virDomainParseXMLDiskDesc(obj->nodesetval->nodeTab[i], &buf, hvm);
res = virDomainParseXMLDiskDesc(obj->nodesetval->nodeTab[i], &buf);
if (res != 0) { if (res != 0) {
goto error; goto error;
} }
virBufferAdd(&buf, ")", 1);
} }
} }
xmlXPathFreeObject(obj); xmlXPathFreeObject(obj);