Daniel P. Berrange wrote:
Unfortunately I think we'll need to get an implementation done that works with SEXPR. Even if everything progresses smoothly with the new XML-RPC protocol for XenD, realistically it is going to be 2-3 months into the future before its anywhere near ready for use by libvirt, and even then there'll be a non-trivial existing userbase still on the SEXPR based XenD for quite a while after that.
Here is a first cut of the patch for review. I have minimally tested the "get" side of the patch, e.g. virsh dumpxml <some_hvm_domain>. Have not tried the "put" side, e.g. virsh create. I hope to test/fix the create side tomorrow as well as test the get side with more interesting configurations.
I haven't quite decided on how to handle the cdrom yet. I'm hardly and expert on libxml so perhaps someone can suggest how to elegantly extract the cdrom info from the incoming xml :-). See the TODO related to cdrom in xml.c.
Regards, Jim
Index: xend_internal.c =================================================================== RCS file: /data/cvs/libvirt/src/xend_internal.c,v retrieving revision 1.38 diff -u -r1.38 xend_internal.c --- xend_internal.c 29 Jun 2006 22:12:47 -0000 1.38 +++ xend_internal.c 30 Jun 2006 00:47:53 -0000 @@ -1323,10 +1323,77 @@ ****** *****************************************************************/ #ifndef XEN_RO + +/** + * xend_parse_sexp_desc_os: + * @node: the root of the parsed S-Expression + * @buf: output buffer object + * @hvm: true or 1 if no contains HVM S-Expression + * + * Parse the xend sexp for description of os and append it to buf. + * + * Returns nothing + */ +static void +xend_parse_sexp_desc_os(struct sexpr *node, virBufferPtr buf, int hvm) +{ + const char *tmp; + + if (node == NULL || buf == NULL) { + return; + } + + tmp = sexpr_node(node, hvm ? "domain/image/hvm/kernel" : + "domain/image/linux/kernel"); + if (tmp == NULL) { + /* + * TODO: we will need some fallback here for other guest OSes + */ + virXendError(NULL, VIR_ERR_INTERNAL_ERROR, + "domain informations incomplete, missing kernel"); + return; + } + + virBufferAdd(buf, " <os>\n", 7); + virBufferVSprintf(buf, hvm ? " <type>hvm</type>\n" : + " <type>linux</type>\n"); + virBufferVSprintf(buf, " <kernel>%s</kernel>\n", tmp); + if (hvm) { + tmp = sexpr_node(node, "domain/image/hvm/device_model"); + if ((tmp != NULL) && (tmp[0] != 0)) + virBufferVSprintf(buf, " <device_model>%s</device_model>\n", tmp); + tmp = sexpr_node(node, "domain/image/hvm/boot"); + if ((tmp != NULL) && (tmp[0] != 0)) { + // FIXME: + // Figure out how to map the 'a', 'b', 'c' nonsense to a + // device. + if (tmp[0] == 'a') + virBufferAdd(buf, " <boot dev='/dev/fd0'/>\n", 25 ); + else if (tmp[0] == 'c') + // 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? + virBufferAdd(buf, " <boot dev='hda'/>\n", 22 ); + else if (strcmp(tmp, "d") == 0) + virBufferAdd(buf, " <boot dev='/dev/cdrom'/>\n", 24 ); + } + } + else { + tmp = sexpr_node(node, "domain/image/linux/ramdisk"); + if ((tmp != NULL) && (tmp[0] != 0)) + virBufferVSprintf(buf, " <initrd>%s</initrd>\n", tmp); + tmp = sexpr_node(node, "domain/image/linux/root"); + if ((tmp != NULL) && (tmp[0] != 0)) + virBufferVSprintf(buf, " <root>%s</root>\n", tmp); + tmp = sexpr_node(node, "domain/image/linux/args"); + if ((tmp != NULL) && (tmp[0] != 0)) + virBufferVSprintf(buf, " <cmdline>%s</cmdline>\n", tmp); + } + virBufferAdd(buf, " </os>\n", 8); +} + /** * xend_parse_sexp_desc: * @root: the root of the parsed S-Expression - * @name: output name of the domain * * Parse the xend sexp description and turn it into the XML format similar * to the one unsed for creation. @@ -1341,6 +1408,7 @@ struct sexpr *cur, *node; const char *tmp; virBuffer buf; + int hvm; if (root == NULL) { /* ERROR */ @@ -1380,30 +1448,12 @@ tmp = sexpr_node(root, "domain/bootloader"); if (tmp != NULL) virBufferVSprintf(&buf, " <bootloader>%s</bootloader>\n", tmp); + if (sexpr_lookup(root, "domain/image")) { - tmp = sexpr_node(root, "domain/image/linux/kernel"); - if (tmp == NULL) { - /* - * TODO: we will need some fallback here for other guest OSes - */ - virXendError(NULL, VIR_ERR_INTERNAL_ERROR, - "domain informations incomplete, missing kernel"); - goto error; - } - virBufferAdd(&buf, " <os>\n", 7); - virBufferVSprintf(&buf, " <type>linux</type>\n"); - virBufferVSprintf(&buf, " <kernel>%s</kernel>\n", tmp); - tmp = sexpr_node(root, "domain/image/linux/ramdisk"); - if ((tmp != NULL) && (tmp[0] != 0)) - virBufferVSprintf(&buf, " <initrd>%s</initrd>\n", tmp); - tmp = sexpr_node(root, "domain/image/linux/root"); - if ((tmp != NULL) && (tmp[0] != 0)) - virBufferVSprintf(&buf, " <root>%s</root>\n", tmp); - tmp = sexpr_node(root, "domain/image/linux/args"); - if ((tmp != NULL) && (tmp[0] != 0)) - virBufferVSprintf(&buf, " <cmdline>%s</cmdline>\n", tmp); - virBufferAdd(&buf, " </os>\n", 8); + hvm = sexpr_lookup(root, "domain/image/hvm") ? 1 : 0; + xend_parse_sexp_desc_os(root, &buf, hvm); } + virBufferVSprintf(&buf, " <memory>%d</memory>\n", (int) (sexpr_u64(root, "domain/maxmem") << 10)); virBufferVSprintf(&buf, " <vcpu>%d</vcpu>\n", @@ -1496,9 +1546,32 @@ virBufferVSprintf(&buf, "<!-- Failed to parse vif: %s -->\n", serial); } + } + } + if (hvm) { + /* Graphics device */ + /* TODO: + * Support for some additional attributes for graphics device? + */ + tmp = sexpr_node(root, "domain/image/hvm/vnc"); + if (tmp != NULL) { + if (tmp[0] == '1') + virBufferAdd(&buf, " <graphics type='vnc'/>\n", 27 ); + } + + tmp = sexpr_node(root, "domain/image/hvm/sdl"); + if (tmp != NULL) { + if (tmp[0] == '1') + virBufferAdd(&buf, " <graphics type='sdl'/>\n", 27 ); } + + /* + * TODO: + * Device for cdrom + */ } + virBufferAdd(&buf, " </devices>\n", 13); virBufferAdd(&buf, "</domain>\n", 10); Index: xml.c =================================================================== RCS file: /data/cvs/libvirt/src/xml.c,v retrieving revision 1.21 diff -u -r1.21 xml.c --- xml.c 26 Jun 2006 15:02:19 -0000 1.21 +++ xml.c 30 Jun 2006 00:47:53 -0000 @@ -563,21 +563,140 @@ #endif /** - * virDomainParseXMLOSDesc: - * @xmldesc: string with the XML description + * virDomainParseXMLDescHVM: + * @ctxt: a path context representing the XML description * @buf: a buffer for the result S-Expr - * @bootloader: indocate if a bootloader script was provided * - * Parse the OS part of the XML description and add it to the S-Expr in buf - * This is a temporary interface as the S-Expr interface + * Parse the OS part of the XML description for an HVM domain and add it to + * the S-Expr in buf. This is a temporary interface as the S-Expr interface * will be replaced by XML-RPC in the future. However the XML format should * stay valid over time. * * Returns 0 in case of success, -1 in case of error. */ static int -virDomainParseXMLOSDesc(xmlNodePtr node, virBufferPtr buf, int bootloader) +virDomainParseXMLDescHVM(xmlXPathContextPtr ctxt, virBufferPtr buf) { + xmlXPathObjectPtr obj = NULL; + xmlNodePtr cur, txt; + const xmlChar *type = NULL; + const xmlChar *kernel = NULL; + const xmlChar *dev_model = NULL; + const xmlChar *boot_dev = NULL; + xmlChar *graphics_type = NULL; + + virBufferAdd(buf, "(image ", 7); + obj = xmlXPathEval(BAD_CAST "/domain/os[1]", ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr != 1)) { + virXMLError(VIR_ERR_NO_OS, "", 0); + return (-1); + } + + cur = obj->nodesetval->nodeTab[0]->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if ((type == NULL) + && (xmlStrEqual(cur->name, BAD_CAST "type"))) { + txt = cur->children; + if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL)) + type = txt->content; + } else if ((kernel == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "kernel"))) { + txt = cur->children; + if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL)) + kernel = txt->content; + } else if ((dev_model == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "device_model"))) { + txt = cur->children; + if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL)) + dev_model = txt->content; + } else if ((boot_dev == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "boot"))) { + boot_dev = xmlGetProp(cur, BAD_CAST "dev"); + } + } + cur = cur->next; + } + if ((type == NULL) || (!xmlStrEqual(type, BAD_CAST "hvm"))) { + /* VIR_ERR_OS_TYPE */ + virXMLError(VIR_ERR_OS_TYPE, (const char *) type, 0); + return (-1); + } + virBufferAdd(buf, "(hvm ", 5); + if (kernel == NULL) { + virXMLError(VIR_ERR_NO_KERNEL, NULL, 0); + return (-1); + } else { + virBufferVSprintf(buf, "(kernel '%s')", (const char *) kernel); + } + if (dev_model == NULL) { + virXMLError(VIR_ERR_NO_KERNEL, NULL, 0); // need error + return (-1); + } else { + virBufferVSprintf(buf, "(device_model '%s')", (const char *) dev_model); + } + if (boot_dev) { + /* TODO: + * Have to figure out the naming used here. + */ + if (xmlStrEqual(type, BAD_CAST "hda")) { + virBufferVSprintf(buf, "(boot a)", (const char *) boot_dev); + } else if (xmlStrEqual(type, BAD_CAST "hdd")) { + virBufferVSprintf(buf, "(boot d)", (const char *) boot_dev); + } else { + /* Force hd[b|c] if boot_dev specified but not floppy or cdrom? */ + virBufferVSprintf(buf, "(boot c)", (const char *) boot_dev); + } + } + xmlXPathFreeObject(obj); + + /* TODO: + * Is a cdrom disk device specified? + * Kind of ugly since it is buried in the devices/diskk node. + */ + + /* Is a graphics device specified? */ + obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics[1]", ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr != 1)) { + virXMLError(VIR_ERR_NO_OS, "", 0); // need error + return (-1); + } + + graphics_type = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "type"); + if (graphics_type != NULL) { + if (xmlStrEqual(graphics_type, BAD_CAST "sdl")) + virBufferAdd(buf, "(sdl 1)", 7); + else if (xmlStrEqual(graphics_type, BAD_CAST "vnc")) + virBufferAdd(buf, "(vnc 1)", 7); + xmlFree(graphics_type); + } + xmlXPathFreeObject(obj); + + virBufferAdd(buf, ")", 1); /* terminates (hvm */ + virBufferAdd(buf, ")", 1); /* terminates (image */ + + return (0); +} + +/** + * virDomainParseXMLDescPV: + * @ctxt: a path context representing the XML description + * @buf: a buffer for the result S-Expr + * @bootloader: indocate if a bootloader script was provided + * + * Parse the OS part of the XML description for a paravirtualized domain + * and add it to the S-Expr in buf. This is a temporary interface as the + * S-Expr interface will be replaced by XML-RPC in the future. However + * the XML format should stay valid over time. + * + * Returns 0 in case of success, -1 in case of error. + */ +static int +virDomainParseXMLDescPV(xmlXPathContextPtr ctxt, virBufferPtr buf, int bootloader) +{ + xmlXPathObjectPtr obj = NULL; xmlNodePtr cur, txt; const xmlChar *type = NULL; const xmlChar *root = NULL; @@ -585,7 +704,15 @@ const xmlChar *initrd = NULL; const xmlChar *cmdline = NULL; - cur = node->children; + virBufferAdd(buf, "(image ", 7); + obj = xmlXPathEval(BAD_CAST "/domain/os[1]", ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr != 1)) { + virXMLError(VIR_ERR_NO_OS, "", 0); + return (-1); + } + + cur = obj->nodesetval->nodeTab[0]->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if ((type == NULL) @@ -617,19 +744,19 @@ } cur = cur->next; } - if ((type != NULL) && (!xmlStrEqual(type, BAD_CAST "linux"))) { + if ((type == NULL) || (!xmlStrEqual(type, BAD_CAST "linux"))) { /* VIR_ERR_OS_TYPE */ virXMLError(VIR_ERR_OS_TYPE, (const char *) type, 0); return (-1); } virBufferAdd(buf, "(linux ", 7); if (kernel == NULL) { - if (bootloader == 0) { - virXMLError(VIR_ERR_NO_KERNEL, NULL, 0); - return (-1); - } + if (bootloader == 0) { + virXMLError(VIR_ERR_NO_KERNEL, NULL, 0); + return (-1); + } } else { - virBufferVSprintf(buf, "(kernel '%s')", (const char *) kernel); + virBufferVSprintf(buf, "(kernel '%s')", (const char *) kernel); } if (initrd != NULL) virBufferVSprintf(buf, "(ramdisk '%s')", (const char *) initrd); @@ -637,13 +764,21 @@ virBufferVSprintf(buf, "(root '%s')", (const char *) root); if (cmdline != NULL) virBufferVSprintf(buf, "(args '%s')", (const char *) cmdline); - virBufferAdd(buf, ")", 1); + + /* TODO: + * Can this be freed earlier? + */ + xmlXPathFreeObject(obj); + + virBufferAdd(buf, ")", 1); /* terminates (linux */ + virBufferAdd(buf, ")", 1); /* terminates (image */ + return (0); } - + /** * virDomainParseXMLDiskDesc: - * @xmldesc: string with the XML description + * @node: node containing disk description * @buf: a buffer for the result S-Expr * * Parse the one disk in the XML description and add it to the S-Expr in buf @@ -705,10 +840,7 @@ return (-1); } virBufferAdd(buf, "(vbd ", 5); - if (target[0] == '/') virBufferVSprintf(buf, "(dev '%s')", (const char *) target); - else - virBufferVSprintf(buf, "(dev '/dev/%s')", (const char *) target); if (typ == 0) virBufferVSprintf(buf, "(uname 'file:%s')", source); else if (typ == 1) { @@ -730,7 +862,7 @@ /** * virDomainParseXMLIfDesc: - * @xmldesc: string with the XML description + * @node: node containing the interface description * @buf: a buffer for the result S-Expr * * Parse the one interface the XML description and add it to the S-Expr in buf @@ -818,7 +950,7 @@ { xmlDocPtr xml = NULL; xmlNodePtr node; - char *ret = NULL, *nam = NULL; + char *ret = NULL, *nam = NULL, *type = NULL; virBuffer buf; xmlChar *prop; xmlXPathObjectPtr obj = NULL; @@ -859,7 +991,7 @@ goto error; } /* - * extract soem of the basics, name, memory, cpus ... + * extract some of the basics, name, memory, cpus ... */ obj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", ctxt); if ((obj == NULL) || (obj->type != XPATH_STRING) || @@ -909,7 +1041,6 @@ if ((obj != NULL) && (obj->type == XPATH_STRING) && (obj->stringval != NULL) && (obj->stringval[0] != 0)) { virBufferVSprintf(&buf, "(on_poweroff '%s')", obj->stringval); - bootloader = 1; } xmlXPathFreeObject(obj); @@ -917,7 +1048,6 @@ if ((obj != NULL) && (obj->type == XPATH_STRING) && (obj->stringval != NULL) && (obj->stringval[0] != 0)) { virBufferVSprintf(&buf, "(on_reboot '%s')", obj->stringval); - bootloader = 1; } xmlXPathFreeObject(obj); @@ -925,26 +1055,28 @@ if ((obj != NULL) && (obj->type == XPATH_STRING) && (obj->stringval != NULL) && (obj->stringval[0] != 0)) { virBufferVSprintf(&buf, "(on_crash '%s')", obj->stringval); - bootloader = 1; } xmlXPathFreeObject(obj); - /* analyze of the os description */ - virBufferAdd(&buf, "(image ", 7); - obj = xmlXPathEval(BAD_CAST "/domain/os[1]", ctxt); - if ((obj == NULL) || (obj->type != XPATH_NODESET) || - (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr != 1)) { - virXMLError(VIR_ERR_NO_OS, nam, 0); + /* Analyze of the os description, based on HVM or PV. */ + obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + virXMLError(VIR_ERR_OS_TYPE, (const char *) type, 0); goto error; } - res = virDomainParseXMLOSDesc(obj->nodesetval->nodeTab[0], &buf, - bootloader); + if (xmlStrEqual(obj->stringval, BAD_CAST "hvm")) { + res = virDomainParseXMLDescHVM(ctxt, &buf); + } + else { + res = virDomainParseXMLDescPV(ctxt, &buf, bootloader); + } + if (res != 0) { goto error; } xmlXPathFreeObject(obj); - virBufferAdd(&buf, ")", 1); - + /* analyze of the devices */ obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt); if ((obj == NULL) || (obj->type != XPATH_NODESET) || @@ -976,7 +1108,6 @@ } xmlXPathFreeObject(obj); - virBufferAdd(&buf, ")", 1); /* closes (vm */ buf.content[buf.use] = 0;