[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] PATCH: Generic internal API for domain XML parser/formatter



We currently have five drivers which handle the domain XML containing
duplicated parsers and formatters for the XML with varying degrees of
buginess, and often very similar structs. This patch introduces a new
general purpose internal API for parsing and formatting network XML, 
and representing it as a series of structs.

This code is derived from the current equivalent code in the QEMU driver
for domains, but with alot of extra config parsing added for stuff needed
by the Xen drivers. I have not yet added the extra bits needed by the
container based drivers, LXC and OpenVZ, but don't anticipate any problems
in this regard.

Again the naming conventions I'm adopting in this patch follow those  in
the storage and network drivers:

 - virDomainPtr - the public opaque object in libvirt.h
 - virDomainObjPtr - the primary internal object for domain state
 - virDomainDefPtr - the configuration data for a domain

A virDomainObjPtr contains a reference to one or two virDomainDefPtr
objects - the current live config, and potentially a secondary inactive
config which will become live at the next restart.

The structs are defined in domain_conf.h, along with a bunch of APIs for
dealing with them. These APIs are the same as similarly named ones from
the current qemu driver, but I'll go over them again for a reminder:

  virDomainObjPtr virDomainFindByUUID(const virDomainObjPtr doms,
                                      const unsigned char *uuid);
  virDomainObjPtr virDomainFindByName(const virDomainObjPtr doms,
                                      const char *name);
  virDomainObjPtr virDomainFindByID(const virDomainObjPtr doms,
                                    const char *id);

Allow lookup of a virDomainObjPtr object based on its name, ID or UUID, 
as typically obtained from the public virDomainPtr object.
The 'doms' parameter to both of thse is a linked list of domains which
are currently known to the driver using this API. Not all drivers will
use these APIs - the Xen driver will just query XenD - these are only
for drivers which maintain state internally with the libvirt daemon.

  void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def);
  void virDomainInputDefFree(virDomainInputDefPtr def);
  void virDomainDiskDefFree(virDomainDiskDefPtr def);
  void virDomainNetDefFree(virDomainNetDefPtr def);
  void virDomainChrDefFree(virDomainChrDefPtr def);
  void virDomainSoundDefFree(virDomainSoundDefPtr def);
  void virDomainDefFree(virDomainDefPtr def);
  void virDomainObjFree(virDomainObjPtr net);

Convenience APIs to totally free the memory associated with these
objects.

  virDomainObjPtr virDomainAssignDef(virConnectPtr conn,
                                     virDomainObjPtr *doms,
                                     const virDomainDefPtr def);

Given a virDomainDefPtr object, it'll search for a pre-existing
virDomainObjPtr object with matching config. If one is found, its
config will be updated, otherwise a new object will be allocated.

  void virDomainRemoveInactive(virDomainObjPtr *doms,
                               const virDomainObjPtr dom);

Convenience for removing and free'ing a virDomainObjPtr object in
the current list of active domains.

  virDomainDefPtr virDomainDefParse(virConnectPtr conn,
                                    virCapsPtr caps,
                                    const char *xmlStr,
                                    const char *displayName);

Given an XML document describing a network, parses the doc and generates
a virDomainDefPtr to represent it in memory. This is a little more 
advanced than the network parser because the various hypervisor drivers
have slightly varying capabilities. So we pass a virCapsPtr object in
so the parser can validate against the actual driver's capabilities.
I'd like to extend the capabilities format to cover things like the
boot parameters supported - eg PXE vs kernel+initrd vs bootloader vs
BIOS, so we can further validate at time of parsing, instead of time
of use.

  char *virDomainDefFormat(virConnectPtr conn,
                           const virDomainDefPtr def,
                           int secure);

Given a virDomainDefPtr object, generate a XML document describing the
domain. The secure flag controls whether passwords are included in the
XML output.

  int virDomainCpuSetParse(virConnectPtr conn,
                           const char **str,
                           char sep,
                           char *cpuset,
                           int maxcpu);
  char *virDomainCpuSetFormat(virConnectPtr conn,
                              char *cpuset,
                              int maxcpu);

These are just the APIs for dealing with CPU ranges moved from the xml.c
file so they are in the expected place. A future patch will remove them
from xml.c when the Xen driver is ported to this API.

  VIR_ENUM_DECL(virDomainVirt)
  VIR_ENUM_DECL(virDomainBoot)
  VIR_ENUM_DECL(virDomainFeature)
  VIR_ENUM_DECL(virDomainLifecycle)
  VIR_ENUM_DECL(virDomainDisk)
  VIR_ENUM_DECL(virDomainDiskDevice)
  VIR_ENUM_DECL(virDomainDiskBus)
  VIR_ENUM_DECL(virDomainNet)
  VIR_ENUM_DECL(virDomainChr)
  VIR_ENUM_DECL(virDomainSoundModel)
  VIR_ENUM_DECL(virDomainInput)
  VIR_ENUM_DECL(virDomainInputBus)
  VIR_ENUM_DECL(virDomainGraphics)

This provides prototypes for all the enumerations useful to drivers using
this API and struct definitions.

As a mentioned earlier, the impl of these APIs is just copied from the QEMU
driver, but instead of using pre-declared char[PATH_MAX] fields, we allocate
memory for strings as required. This dramatically reduces the memory needs
of the QEMU driver (when ported to this API). It also switches to use the
xml.c convenience routines instead of the regular libxml2 APIs, and in doing
so I added a couple more convenience routines.

There is no test suite explicitly against these parsers because when the
QEMU and  Xen drivers are ported to this API their existing test suites
exercise nearly all the code.

Regards,
Daniel.

diff -r f47fdc49c62f src/Makefile.am
--- a/src/Makefile.am	Sun Jun 22 17:58:48 2008 -0400
+++ b/src/Makefile.am	Sun Jun 22 18:31:20 2008 -0400
@@ -39,6 +39,7 @@
 		test.c test.h                                   \
                 buf.c buf.h					\
 		qparams.c qparams.h				\
+                domain_conf.c domain_conf.h                     \
                 capabilities.c capabilities.h			\
 		xml.c xml.h					\
 		event.c event.h					\
diff -r f47fdc49c62f src/domain_conf.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/domain_conf.c	Sun Jun 22 18:31:20 2008 -0400
@@ -0,0 +1,2621 @@
+/*
+ * domain_conf.c: domain XML processing
+ *
+ * Copyright (C) 2006-2008 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#include <config.h>
+
+#include "internal.h"
+
+#include "domain_conf.h"
+#include "memory.h"
+#include "verify.h"
+#include "xml.h"
+#include "uuid.h"
+#include "util.h"
+#include "buf.h"
+#include "c-ctype.h"
+
+VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST,
+              "qemu",
+              "kqemu",
+              "kvm",
+              "xen",
+              "lxc",
+              "uml",
+              "openvz",
+              "vserver",
+              "ldom",
+              "test",
+              "vmware",
+              "hyperv")
+
+VIR_ENUM_IMPL(virDomainBoot, VIR_DOMAIN_BOOT_LAST,
+              "fd",
+              "cdrom",
+              "hd",
+              "network")
+
+VIR_ENUM_IMPL(virDomainFeature, VIR_DOMAIN_FEATURE_LAST,
+              "acpi",
+              "apic",
+              "pae")
+
+VIR_ENUM_IMPL(virDomainLifecycle, VIR_DOMAIN_LIFECYCLE_LAST,
+              "destroy",
+              "restart",
+              "rename-restart",
+              "preserve")
+
+VIR_ENUM_IMPL(virDomainDisk, VIR_DOMAIN_DISK_TYPE_LAST,
+              "block",
+              "file")
+
+VIR_ENUM_IMPL(virDomainDiskDevice, VIR_DOMAIN_DISK_DEVICE_LAST,
+              "disk",
+              "cdrom",
+              "floppy")
+
+VIR_ENUM_IMPL(virDomainDiskBus, VIR_DOMAIN_DISK_BUS_LAST,
+              "ide",
+              "fdc",
+              "scsi",
+              "virtio",
+              "xen")
+
+VIR_ENUM_IMPL(virDomainNet, VIR_DOMAIN_NET_TYPE_LAST,
+              "user",
+              "ethernet",
+              "server",
+              "client",
+              "mcast",
+              "network",
+              "bridge")
+
+VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_CHR_TYPE_LAST,
+              "null",
+              "vc",
+              "pty",
+              "dev",
+              "file",
+              "pipe",
+              "stdio",
+              "udp",
+              "tcp",
+              "unix")
+
+VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST,
+              "sb16",
+              "es1370",
+              "pcspk");
+
+VIR_ENUM_IMPL(virDomainInput, VIR_DOMAIN_INPUT_TYPE_LAST,
+              "mouse",
+              "tablet")
+
+VIR_ENUM_IMPL(virDomainInputBus, VIR_DOMAIN_INPUT_BUS_LAST,
+              "ps2",
+              "usb",
+              "xen")
+
+VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST,
+              "sdl",
+              "vnc")
+
+
+static void virDomainReportError(virConnectPtr conn,
+                                 int code, const char *fmt, ...)
+{
+    va_list args;
+    char errorMessage[1024];
+    const char *virerr;
+
+    if (fmt) {
+        va_start(args, fmt);
+        vsnprintf(errorMessage, sizeof(errorMessage)-1, fmt, args);
+        va_end(args);
+    } else {
+        errorMessage[0] = '\0';
+    }
+
+    virerr = __virErrorMsg(code, (errorMessage[0] ? errorMessage : NULL));
+    __virRaiseError(conn, NULL, NULL, VIR_FROM_QEMU, code, VIR_ERR_ERROR,
+                    virerr, errorMessage, NULL, -1, -1, virerr, errorMessage);
+}
+
+
+virDomainObjPtr virDomainFindByID(const virDomainObjPtr doms,
+                                  int id)
+{
+    virDomainObjPtr dom = doms;
+    while (dom) {
+        if (virDomainIsActive(dom) && dom->def->id == id)
+            return dom;
+        dom = dom->next;
+    }
+
+    return NULL;
+}
+
+
+virDomainObjPtr virDomainFindByUUID(const virDomainObjPtr doms,
+                                    const unsigned char *uuid)
+{
+    virDomainObjPtr dom = doms;
+
+    while (dom) {
+        if (!memcmp(dom->def->uuid, uuid, VIR_UUID_BUFLEN))
+            return dom;
+        dom = dom->next;
+    }
+
+    return NULL;
+}
+
+virDomainObjPtr virDomainFindByName(const virDomainObjPtr doms,
+                                    const char *name)
+{
+    virDomainObjPtr dom = doms;
+
+    while (dom) {
+        if (STREQ(dom->def->name, name))
+            return dom;
+        dom = dom->next;
+    }
+
+    return NULL;
+}
+
+void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def)
+{
+    if (!def)
+        return;
+
+    switch (def->type) {
+    case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
+        VIR_FREE(def->data.vnc.listenAddr);
+        VIR_FREE(def->data.vnc.keymap);
+        VIR_FREE(def->data.vnc.passwd);
+        break;
+
+    case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
+        VIR_FREE(def->data.sdl.display);
+        VIR_FREE(def->data.sdl.xauth);
+        break;
+    }
+
+    VIR_FREE(def);
+}
+
+void virDomainInputDefFree(virDomainInputDefPtr def)
+{
+    if (!def)
+        return;
+
+    virDomainInputDefFree(def->next);
+    VIR_FREE(def);
+}
+
+void virDomainDiskDefFree(virDomainDiskDefPtr def)
+{
+    if (!def)
+        return;
+
+    VIR_FREE(def->src);
+    VIR_FREE(def->dst);
+    VIR_FREE(def->driverName);
+    VIR_FREE(def->driverType);
+
+    virDomainDiskDefFree(def->next);
+    VIR_FREE(def);
+}
+
+void virDomainNetDefFree(virDomainNetDefPtr def)
+{
+    if (!def)
+        return;
+
+    VIR_FREE(def->model);
+
+    switch (def->type) {
+    case VIR_DOMAIN_NET_TYPE_ETHERNET:
+        VIR_FREE(def->dst.ethernet.ifname);
+        VIR_FREE(def->dst.ethernet.script);
+        VIR_FREE(def->dst.ethernet.ipaddr);
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_SERVER:
+    case VIR_DOMAIN_NET_TYPE_CLIENT:
+    case VIR_DOMAIN_NET_TYPE_MCAST:
+        VIR_FREE(def->dst.socket.address);
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_NETWORK:
+        VIR_FREE(def->dst.network.name);
+        VIR_FREE(def->dst.network.ifname);
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_BRIDGE:
+        VIR_FREE(def->dst.bridge.brname);
+        VIR_FREE(def->dst.bridge.ifname);
+        break;
+    }
+
+    virDomainNetDefFree(def->next);
+    VIR_FREE(def);
+}
+
+void virDomainChrDefFree(virDomainChrDefPtr def)
+{
+    if (!def)
+        return;
+
+    switch (def->type) {
+    case VIR_DOMAIN_CHR_TYPE_PTY:
+    case VIR_DOMAIN_CHR_TYPE_DEV:
+    case VIR_DOMAIN_CHR_TYPE_FILE:
+    case VIR_DOMAIN_CHR_TYPE_PIPE:
+        VIR_FREE(def->data.file.path);
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_UDP:
+        VIR_FREE(def->data.udp.bindHost);
+        VIR_FREE(def->data.udp.bindService);
+        VIR_FREE(def->data.udp.connectHost);
+        VIR_FREE(def->data.udp.connectService);
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_TCP:
+        VIR_FREE(def->data.tcp.host);
+        VIR_FREE(def->data.tcp.service);
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_UNIX:
+        VIR_FREE(def->data.nix.path);
+        break;
+    }
+
+    virDomainChrDefFree(def->next);
+    VIR_FREE(def);
+}
+
+void virDomainSoundDefFree(virDomainSoundDefPtr def)
+{
+    if (!def)
+        return;
+
+    virDomainSoundDefFree(def->next);
+    VIR_FREE(def);
+}
+
+void virDomainDefFree(virDomainDefPtr def)
+{
+    if (!def)
+        return;
+
+    virDomainGraphicsDefFree(def->graphics);
+    virDomainInputDefFree(def->inputs);
+    virDomainDiskDefFree(def->disks);
+    virDomainNetDefFree(def->nets);
+    virDomainChrDefFree(def->serials);
+    virDomainChrDefFree(def->parallels);
+    virDomainChrDefFree(def->console);
+    virDomainSoundDefFree(def->sounds);
+
+
+    VIR_FREE(def->os.type);
+    VIR_FREE(def->os.arch);
+    VIR_FREE(def->os.machine);
+    VIR_FREE(def->os.kernel);
+    VIR_FREE(def->os.initrd);
+    VIR_FREE(def->os.cmdline);
+    VIR_FREE(def->os.root);
+    VIR_FREE(def->os.loader);
+    VIR_FREE(def->os.bootloader);
+    VIR_FREE(def->os.bootloaderArgs);
+
+    VIR_FREE(def->name);
+    VIR_FREE(def->cpumask);
+    VIR_FREE(def->emulator);
+
+    VIR_FREE(def);
+}
+
+void virDomainObjFree(virDomainObjPtr dom)
+{
+    if (!dom)
+        return;
+
+    virDomainDefFree(dom->def);
+    virDomainDefFree(dom->newDef);
+
+    VIR_FREE(dom->vcpupids);
+    VIR_FREE(dom->configFile);
+    VIR_FREE(dom->autostartLink);
+
+    VIR_FREE(dom);
+}
+
+virDomainObjPtr virDomainAssignDef(virConnectPtr conn,
+                                   virDomainObjPtr *doms,
+                                   const virDomainDefPtr def)
+{
+    virDomainObjPtr domain;
+
+    if ((domain = virDomainFindByName(*doms, def->name))) {
+        if (!virDomainIsActive(domain)) {
+            virDomainDefFree(domain->def);
+            domain->def = def;
+        } else {
+            if (domain->newDef)
+                virDomainDefFree(domain->newDef);
+            domain->newDef = def;
+        }
+
+        return domain;
+    }
+
+    if (VIR_ALLOC(domain) < 0) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    domain->def = def;
+    domain->next = *doms;
+
+    *doms = domain;
+
+    return domain;
+}
+
+void virDomainRemoveInactive(virDomainObjPtr *doms,
+                             virDomainObjPtr dom)
+{
+    virDomainObjPtr prev = NULL;
+    virDomainObjPtr curr = *doms;
+
+    while (curr &&
+           curr != dom) {
+        prev = curr;
+        curr = curr->next;
+    }
+
+    if (curr) {
+        if (prev)
+            prev->next = curr->next;
+        else
+            *doms = curr->next;
+    }
+
+    virDomainObjFree(dom);
+}
+
+static int virDomainDiskCompare(virDomainDiskDefPtr a,
+                                virDomainDiskDefPtr b) {
+    if (a->bus == b->bus)
+        return virDiskNameToIndex(a->dst) - virDiskNameToIndex(b->dst);
+    else
+        return a->bus - b->bus;
+}
+
+
+/* Parse the XML definition for a disk
+ * @param node XML nodeset to parse for disk definition
+ */
+static virDomainDiskDefPtr
+virDomainDiskDefParseXML(virConnectPtr conn,
+                         xmlNodePtr node) {
+    virDomainDiskDefPtr def;
+    xmlNodePtr cur;
+    char *type = NULL;
+    char *device = NULL;
+    char *driverName = NULL;
+    char *driverType = NULL;
+    char *source = NULL;
+    char *target = NULL;
+    char *bus = NULL;
+
+    if (VIR_ALLOC(def) < 0) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    type = virXMLPropString(node, "type");
+    if (type) {
+        if ((def->type = virDomainDiskTypeFromString(type)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown disk type '%s'"), type);
+            goto error;
+        }
+    } else {
+        def->type = VIR_DOMAIN_DISK_TYPE_FILE;
+    }
+
+    cur = node->children;
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            if ((source == NULL) &&
+                (xmlStrEqual(cur->name, BAD_CAST "source"))) {
+
+                if (def->type == VIR_DOMAIN_DISK_TYPE_FILE)
+                    source = virXMLPropString(cur, "file");
+                else
+                    source = virXMLPropString(cur, "dev");
+            } else if ((target == NULL) &&
+                       (xmlStrEqual(cur->name, BAD_CAST "target"))) {
+                target = virXMLPropString(cur, "dev");
+                bus = virXMLPropString(cur, "bus");
+            } else if ((driverName == NULL) &&
+                       (xmlStrEqual(cur->name, BAD_CAST "driver"))) {
+                driverName = virXMLPropString(cur, "name");
+                driverType = virXMLPropString(cur, "type");
+            } else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) {
+                def->readonly = 1;
+            } else if (xmlStrEqual(cur->name, BAD_CAST "shareable")) {
+                def->shared = 1;
+            }
+        }
+        cur = cur->next;
+    }
+
+    device = virXMLPropString(node, "device");
+    if (device) {
+        if ((def->device = virDomainDiskDeviceTypeFromString(device)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown disk device '%s'"), device);
+            goto error;
+        }
+    } else {
+        def->device = VIR_DOMAIN_DISK_DEVICE_DISK;
+    }
+
+    /* Only CDROM and Floppy devices are allowed missing source path
+     * to indicate no media present */
+    if (source == NULL &&
+        def->device != VIR_DOMAIN_DISK_DEVICE_CDROM &&
+        def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
+        virDomainReportError(conn, VIR_ERR_NO_SOURCE,
+                             target ? "%s" : NULL, target);
+        goto error;
+    }
+
+    if (target == NULL) {
+        virDomainReportError(conn, VIR_ERR_NO_TARGET,
+                             source ? "%s" : NULL, source);
+        goto error;
+    }
+
+    if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
+        !STRPREFIX(target, "fd")) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("Invalid floppy device name: %s"), target);
+        goto error;
+    }
+
+    /* Force CDROM to be listed as read only */
+    if (def->device == VIR_DOMAIN_DISK_DEVICE_CDROM)
+        def->readonly = 1;
+
+    if (def->device == VIR_DOMAIN_DISK_DEVICE_DISK &&
+        !STRPREFIX((const char *)target, "hd") &&
+        !STRPREFIX((const char *)target, "sd") &&
+        !STRPREFIX((const char *)target, "vd") &&
+        !STRPREFIX((const char *)target, "xvd")) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("Invalid harddisk device name: %s"), target);
+        goto error;
+    }
+
+    if (bus) {
+        if ((def->bus = virDomainDiskBusTypeFromString(bus)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown disk bus type '%s'"), bus);
+            goto error;
+        }
+    } else {
+        if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
+            def->bus = VIR_DOMAIN_DISK_BUS_FDC;
+        } else {
+            if (STRPREFIX(target, "hd"))
+                def->bus = VIR_DOMAIN_DISK_BUS_IDE;
+            else if (STRPREFIX(target, "sd"))
+                def->bus = VIR_DOMAIN_DISK_BUS_SCSI;
+            else if (STRPREFIX(target, "vd"))
+                def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO;
+            else if (STRPREFIX(target, "xvd"))
+                def->bus = VIR_DOMAIN_DISK_BUS_XEN;
+            else
+                def->bus = VIR_DOMAIN_DISK_BUS_IDE;
+        }
+    }
+
+    if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
+        def->bus != VIR_DOMAIN_DISK_BUS_FDC) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("Invalid bus type '%s' for floppy disk"), bus);
+        goto error;
+    }
+    if (def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
+        def->bus == VIR_DOMAIN_DISK_BUS_FDC) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("Invalid bus type '%s' for disk"), bus);
+        goto error;
+    }
+
+    def->src = source;
+    source = NULL;
+    def->dst = target;
+    target = NULL;
+    def->driverName = driverName;
+    driverName = NULL;
+    def->driverType = driverType;
+    driverType = NULL;
+
+cleanup:
+    VIR_FREE(bus);
+    VIR_FREE(type);
+    VIR_FREE(target);
+    VIR_FREE(source);
+    VIR_FREE(device);
+    VIR_FREE(driverType);
+    VIR_FREE(driverName);
+
+    return def;
+
+ error:
+    virDomainDiskDefFree(def);
+    def = NULL;
+    goto cleanup;
+}
+
+
+static void virDomainNetRandomMAC(virDomainNetDefPtr def) {
+    /* XXX there different vendor prefixes per hypervisor */
+    def->mac[0] = 0x52;
+    def->mac[1] = 0x54;
+    def->mac[2] = 0x00;
+    def->mac[3] = 1 + (int)(256*(rand()/(RAND_MAX+1.0)));
+    def->mac[4] = 1 + (int)(256*(rand()/(RAND_MAX+1.0)));
+    def->mac[5] = 1 + (int)(256*(rand()/(RAND_MAX+1.0)));
+}
+
+
+/* Parse the XML definition for a network interface
+ * @param node XML nodeset to parse for net definition
+ * @return 0 on success, -1 on failure
+ */
+static virDomainNetDefPtr
+virDomainNetDefParseXML(virConnectPtr conn,
+                        xmlNodePtr node) {
+    virDomainNetDefPtr def;
+    xmlNodePtr cur;
+    char *macaddr = NULL;
+    char *type = NULL;
+    char *network = NULL;
+    char *bridge = NULL;
+    char *ifname = NULL;
+    char *script = NULL;
+    char *address = NULL;
+    char *port = NULL;
+    char *model = NULL;
+
+    if (VIR_ALLOC(def) < 0) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    type = virXMLPropString(node, "type");
+    if (type != NULL) {
+        if ((def->type = virDomainNetTypeFromString(type)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown interface type '%s'"), type);
+            goto error;
+        }
+    } else {
+        def->type = VIR_DOMAIN_NET_TYPE_USER;
+    }
+
+    cur = node->children;
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            if ((macaddr == NULL) &&
+                (xmlStrEqual(cur->name, BAD_CAST "mac"))) {
+                macaddr = virXMLPropString(cur, "address");
+            } else if ((network == NULL) &&
+                       (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) &&
+                       (xmlStrEqual(cur->name, BAD_CAST "source"))) {
+                network = virXMLPropString(cur, "network");
+            } else if ((network == NULL) &&
+                       (def->type == VIR_DOMAIN_NET_TYPE_BRIDGE) &&
+                       (xmlStrEqual(cur->name, BAD_CAST "source"))) {
+                bridge = virXMLPropString(cur, "bridge");
+            } else if ((network == NULL) &&
+                       ((def->type == VIR_DOMAIN_NET_TYPE_SERVER) ||
+                        (def->type == VIR_DOMAIN_NET_TYPE_CLIENT) ||
+                        (def->type == VIR_DOMAIN_NET_TYPE_MCAST)) &&
+                       (xmlStrEqual(cur->name, BAD_CAST "source"))) {
+                address = virXMLPropString(cur, "address");
+                port = virXMLPropString(cur, "port");
+            } else if ((address == NULL) &&
+                       (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET) &&
+                       (xmlStrEqual(cur->name, BAD_CAST "ip"))) {
+                address = virXMLPropString(cur, "address");
+            } else if ((ifname == NULL) &&
+                       ((def->type == VIR_DOMAIN_NET_TYPE_NETWORK) ||
+                        (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET) ||
+                        (def->type == VIR_DOMAIN_NET_TYPE_BRIDGE)) &&
+                       xmlStrEqual(cur->name, BAD_CAST "target")) {
+                ifname = virXMLPropString(cur, "dev");
+                if (STRPREFIX((const char*)ifname, "vnet")) {
+                    /* An auto-generated target name, blank it out */
+                    VIR_FREE(ifname);
+                    ifname = NULL;
+                }
+            } else if ((script == NULL) &&
+                       (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET) &&
+                       xmlStrEqual(cur->name, BAD_CAST "script")) {
+                script = virXMLPropString(cur, "path");
+            } else if (xmlStrEqual (cur->name, BAD_CAST "model")) {
+                model = virXMLPropString(cur, "type");
+            }
+        }
+        cur = cur->next;
+    }
+
+    if (macaddr) {
+        unsigned int mac[6];
+        sscanf((const char *)macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
+               (unsigned int*)&mac[0],
+               (unsigned int*)&mac[1],
+               (unsigned int*)&mac[2],
+               (unsigned int*)&mac[3],
+               (unsigned int*)&mac[4],
+               (unsigned int*)&mac[5]);
+        def->mac[0] = mac[0];
+        def->mac[1] = mac[1];
+        def->mac[2] = mac[2];
+        def->mac[3] = mac[3];
+        def->mac[4] = mac[4];
+        def->mac[5] = mac[5];
+    } else {
+        virDomainNetRandomMAC(def);
+    }
+
+    switch (def->type) {
+    case VIR_DOMAIN_NET_TYPE_NETWORK:
+        if (network == NULL) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("No <source> 'network' attribute specified with <interface type='network'/>"));
+            goto error;
+        }
+        def->dst.network.name = network;
+        network = NULL;
+
+        if (ifname != NULL) {
+            def->dst.network.ifname = ifname;
+            ifname = NULL;
+        }
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_ETHERNET:
+
+        if (script != NULL) {
+            def->dst.ethernet.script = script;
+            script = NULL;
+        }
+        if (ifname != NULL) {
+            def->dst.ethernet.ifname = ifname;
+            ifname = NULL;
+        }
+        if (address != NULL) {
+            def->dst.ethernet.ipaddr = address;
+            address = NULL;
+        }
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_BRIDGE:
+        if (bridge == NULL) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("No <source> 'dev' attribute specified with <interface type='bridge'/>"));
+            goto error;
+        }
+        def->dst.bridge.brname = bridge;
+        bridge = NULL;
+
+        if (ifname != NULL) {
+            def->dst.bridge.ifname = ifname;
+            ifname = NULL;
+        }
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_CLIENT:
+    case VIR_DOMAIN_NET_TYPE_SERVER:
+    case VIR_DOMAIN_NET_TYPE_MCAST:
+        if (port == NULL) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("No <source> 'port' attribute specified with socket interface"));
+            goto error;
+        }
+        if (virStrToLong_i(port, NULL, 10, &def->dst.socket.port) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("Cannot parse <source> 'port' attribute with socket interface"));
+            goto error;
+        }
+
+        if (address == NULL) {
+            if (def->type == VIR_DOMAIN_NET_TYPE_CLIENT ||
+                def->type == VIR_DOMAIN_NET_TYPE_MCAST) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("No <source> 'address' attribute specified with socket interface"));
+                goto error;
+            }
+        } else {
+            def->dst.socket.address = address;
+            address = NULL;
+        }
+    }
+
+    /* NIC model (see -net nic,model=?).  We only check that it looks
+     * reasonable, not that it is a supported NIC type.  FWIW kvm
+     * supports these types as of April 2008:
+     * i82551 i82557b i82559er ne2k_pci pcnet rtl8139 e1000 virtio
+     */
+    if (model != NULL) {
+        int i;
+        for (i = 0 ; i < strlen(model) ; i++) {
+            int char_ok = c_isalnum(model[i]) || model[i] == '_';
+            if (!char_ok) {
+                virDomainReportError (conn, VIR_ERR_INVALID_ARG, "%s",
+                                      _("Model name contains invalid characters"));
+                goto error;
+            }
+        }
+        def->model = model;
+        model = NULL;
+    }
+
+cleanup:
+    VIR_FREE(macaddr);
+    VIR_FREE(network);
+    VIR_FREE(address);
+    VIR_FREE(port);
+    VIR_FREE(ifname);
+    VIR_FREE(script);
+    VIR_FREE(bridge);
+    VIR_FREE(model);
+    VIR_FREE(type);
+
+    return def;
+
+error:
+    virDomainNetDefFree(def);
+    def = NULL;
+    goto cleanup;
+}
+
+
+/* Parse the XML definition for a character device
+ * @param node XML nodeset to parse for net definition
+ *
+ * The XML we're dealing with looks like
+ *
+ * <serial type="pty">
+ *   <source path="/dev/pts/3"/>
+ *   <target port="1"/>
+ * </serial>
+ *
+ * <serial type="dev">
+ *   <source path="/dev/ttyS0"/>
+ *   <target port="1"/>
+ * </serial>
+ *
+ * <serial type="tcp">
+ *   <source mode="connect" host="0.0.0.0" service="2445"/>
+ *   <target port="1"/>
+ * </serial>
+ *
+ * <serial type="tcp">
+ *   <source mode="bind" host="0.0.0.0" service="2445"/>
+ *   <target port="1"/>
+ * </serial>
+ *
+ * <serial type="udp">
+ *   <source mode="bind" host="0.0.0.0" service="2445"/>
+ *   <source mode="connect" host="0.0.0.0" service="2445"/>
+ *   <target port="1"/>
+ * </serial>
+ *
+ * <serial type="unix">
+ *   <source mode="bind" path="/tmp/foo"/>
+ *   <target port="1"/>
+ * </serial>
+ *
+ */
+static virDomainChrDefPtr
+virDomainChrDefParseXML(virConnectPtr conn,
+                        xmlNodePtr node) {
+    xmlNodePtr cur;
+    char *type = NULL;
+    char *bindHost = NULL;
+    char *bindService = NULL;
+    char *connectHost = NULL;
+    char *connectService = NULL;
+    char *path = NULL;
+    char *mode = NULL;
+    char *protocol = NULL;
+    virDomainChrDefPtr def;
+
+    if (VIR_ALLOC(def) < 0) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    def->type = VIR_DOMAIN_CHR_TYPE_PTY;
+    type = virXMLPropString(node, "type");
+    if (type != NULL) {
+        if (STREQ(type, "null"))
+            def->type = VIR_DOMAIN_CHR_TYPE_NULL;
+        else if (STREQ(type, "vc"))
+            def->type = VIR_DOMAIN_CHR_TYPE_VC;
+        else if (STREQ(type, "pty"))
+            def->type = VIR_DOMAIN_CHR_TYPE_PTY;
+        else if (STREQ(type, "dev"))
+            def->type = VIR_DOMAIN_CHR_TYPE_DEV;
+        else if (STREQ(type, "file"))
+            def->type = VIR_DOMAIN_CHR_TYPE_FILE;
+        else if (STREQ(type, "pipe"))
+            def->type = VIR_DOMAIN_CHR_TYPE_PIPE;
+        else if (STREQ(type, "stdio"))
+            def->type = VIR_DOMAIN_CHR_TYPE_STDIO;
+        else if (STREQ(type, "udp"))
+            def->type = VIR_DOMAIN_CHR_TYPE_UDP;
+        else if (STREQ(type, "tcp"))
+            def->type = VIR_DOMAIN_CHR_TYPE_TCP;
+        else if (STREQ(type, "unix"))
+            def->type = VIR_DOMAIN_CHR_TYPE_UNIX;
+        else
+            def->type = VIR_DOMAIN_CHR_TYPE_NULL;
+    }
+
+    cur = node->children;
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            if (xmlStrEqual(cur->name, BAD_CAST "source")) {
+                if (mode == NULL)
+                    mode = virXMLPropString(cur, "mode");
+
+                switch (def->type) {
+                case VIR_DOMAIN_CHR_TYPE_PTY:
+                case VIR_DOMAIN_CHR_TYPE_DEV:
+                case VIR_DOMAIN_CHR_TYPE_FILE:
+                case VIR_DOMAIN_CHR_TYPE_PIPE:
+                case VIR_DOMAIN_CHR_TYPE_UNIX:
+                    if (path == NULL)
+                        path = virXMLPropString(cur, "path");
+
+                    break;
+
+                case VIR_DOMAIN_CHR_TYPE_UDP:
+                case VIR_DOMAIN_CHR_TYPE_TCP:
+                    if (mode == NULL ||
+                        STREQ((const char *)mode, "connect")) {
+
+                        if (connectHost == NULL)
+                            connectHost = virXMLPropString(cur, "host");
+                        if (connectService == NULL)
+                            connectService = virXMLPropString(cur, "service");
+                    } else {
+                        if (bindHost == NULL)
+                            bindHost = virXMLPropString(cur, "host");
+                        if (bindService == NULL)
+                            bindService = virXMLPropString(cur, "service");
+                    }
+
+                    if (def->type == VIR_DOMAIN_CHR_TYPE_UDP) {
+                        VIR_FREE(mode);
+                        mode = NULL;
+                    }
+                }
+            } else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) {
+                if (protocol == NULL)
+                    protocol = virXMLPropString(cur, "type");
+            }
+        }
+        cur = cur->next;
+    }
+
+
+    switch (def->type) {
+    case VIR_DOMAIN_CHR_TYPE_NULL:
+        /* Nada */
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_VC:
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_PTY:
+        /* @path attribute is an output only property - pty is auto-allocted */
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_DEV:
+    case VIR_DOMAIN_CHR_TYPE_FILE:
+    case VIR_DOMAIN_CHR_TYPE_PIPE:
+        if (path == NULL) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("Missing source path attribute for char device"));
+            goto error;
+        }
+
+        def->data.file.path = path;
+        path = NULL;
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_STDIO:
+        /* Nada */
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_TCP:
+        if (mode == NULL ||
+            STREQ(mode, "connect")) {
+            if (connectHost == NULL) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     "%s", _("Missing source host attribute for char device"));
+                goto error;
+            }
+            if (connectService == NULL) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     "%s", _("Missing source service attribute for char device"));
+                goto error;
+            }
+
+            def->data.tcp.host = connectHost;
+            connectHost = NULL;
+            def->data.tcp.service = connectService;
+            connectService = NULL;
+            def->data.tcp.listen = 0;
+        } else {
+            if (bindHost == NULL) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     "%s", _("Missing source host attribute for char device"));
+                goto error;
+            }
+            if (bindService == NULL) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     "%s", _("Missing source service attribute for char device"));
+                goto error;
+            }
+
+            def->data.tcp.host = bindHost;
+            bindHost = NULL;
+            def->data.tcp.service = bindService;
+            bindService = NULL;
+            def->data.tcp.listen = 1;
+        }
+        if (protocol != NULL &&
+            STREQ(protocol, "telnet"))
+            def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
+        else
+            def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW;
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_UDP:
+        if (connectService == NULL) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("Missing source service attribute for char device"));
+            goto error;
+        }
+
+        def->data.udp.connectHost = connectHost;
+        connectHost = NULL;
+        def->data.udp.connectService = connectService;
+        connectService = NULL;
+
+        def->data.udp.bindHost = bindHost;
+        bindHost = NULL;
+        def->data.udp.bindService = bindService;
+        bindService = NULL;
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_UNIX:
+        if (path == NULL) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("Missing source path attribute for char device"));
+            goto error;
+        }
+
+        if (mode != NULL &&
+            STRNEQ(mode, "connect"))
+            def->data.nix.listen = 1;
+        else
+            def->data.nix.listen = 0;
+
+        def->data.nix.path = path;
+        path = NULL;
+        break;
+    }
+
+cleanup:
+    VIR_FREE(mode);
+    VIR_FREE(protocol);
+    VIR_FREE(type);
+    VIR_FREE(bindHost);
+    VIR_FREE(bindService);
+    VIR_FREE(connectHost);
+    VIR_FREE(connectService);
+    VIR_FREE(path);
+
+    return def;
+
+error:
+    virDomainChrDefFree(def);
+    def = NULL;
+    goto cleanup;
+}
+
+/* Parse the XML definition for a network interface */
+static virDomainInputDefPtr
+virDomainInputDefParseXML(virConnectPtr conn,
+                          const char *ostype,
+                          xmlNodePtr node) {
+    virDomainInputDefPtr def;
+    char *type = NULL;
+    char *bus = NULL;
+
+    if (VIR_ALLOC(def) < 0) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    type = virXMLPropString(node, "type");
+    bus = virXMLPropString(node, "bus");
+
+    if (!type) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("missing input device type"));
+        goto error;
+    }
+
+    if ((def->type = virDomainInputTypeFromString(type)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unknown input device type '%s'"), type);
+        goto error;
+    }
+
+    if (bus) {
+        if ((def->bus = virDomainInputBusTypeFromString(bus)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown input bus type '%s'"), bus);
+            goto error;
+        }
+
+        if (STREQ(ostype, "hvm")) {
+            if (def->bus == VIR_DOMAIN_INPUT_BUS_PS2 && /* Only allow mouse for ps2 */
+                def->type != VIR_DOMAIN_INPUT_TYPE_MOUSE) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     _("ps2 bus does not support %s input device"),
+                                     type);
+                goto error;
+            }
+            if (def->bus == VIR_DOMAIN_INPUT_BUS_XEN) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     _("unsupported input bus %s"),
+                                     bus);
+                goto error;
+            }
+        } else {
+            if (def->bus != VIR_DOMAIN_INPUT_BUS_XEN) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     _("unsupported input bus %s"),
+                                     bus);
+            }
+            if (def->type != VIR_DOMAIN_INPUT_TYPE_MOUSE) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     _("xen bus does not support %s input device"),
+                                     type);
+                goto error;
+            }
+        }
+    } else {
+        if (STREQ(ostype, "hvm")) {
+            if (def->type == VIR_DOMAIN_INPUT_TYPE_MOUSE)
+                def->bus = VIR_DOMAIN_INPUT_BUS_PS2;
+            else
+                def->bus = VIR_DOMAIN_INPUT_BUS_USB;
+        } else {
+            def->bus = VIR_DOMAIN_INPUT_BUS_XEN;
+        }
+    }
+
+cleanup:
+    VIR_FREE(type);
+    VIR_FREE(bus);
+
+    return def;
+
+error:
+    virDomainInputDefFree(def);
+    def = NULL;
+    goto cleanup;
+}
+
+
+/* Parse the XML definition for a graphics device */
+static virDomainGraphicsDefPtr
+virDomainGraphicsDefParseXML(virConnectPtr conn,
+                             xmlNodePtr node) {
+    virDomainGraphicsDefPtr def;
+    char *type = NULL;
+
+    if (VIR_ALLOC(def) < 0) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    type = virXMLPropString(node, "type");
+
+    if (!type) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("missing graphics device type"));
+        goto error;
+    }
+
+    if ((def->type = virDomainGraphicsTypeFromString(type)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unknown graphics device type '%s'"), type);
+        goto error;
+    }
+
+    if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
+        char *port = virXMLPropString(node, "port");
+        char *autoport;
+
+        if (port) {
+            if (virStrToLong_i(port, NULL, 10, &def->data.vnc.port) < 0) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     _("cannot parse vnc port %s"), port);
+                VIR_FREE(port);
+                goto error;
+            }
+            VIR_FREE(port);
+            /* Legacy compat syntax, used -1 for auto-port */
+            if (def->data.vnc.port == -1) {
+                def->data.vnc.port = 0;
+                def->data.vnc.autoport = 1;
+            }
+        } else {
+            def->data.vnc.port = 0;
+            def->data.vnc.autoport = 1;
+        }
+
+        if ((autoport = virXMLPropString(node, "autoport")) != NULL) {
+            if (STREQ(autoport, "yes")) {
+                def->data.vnc.port = 0;
+                def->data.vnc.autoport = 1;
+            }
+            VIR_FREE(autoport);
+        }
+
+        def->data.vnc.listenAddr = virXMLPropString(node, "listen");
+        def->data.vnc.passwd = virXMLPropString(node, "passwd");
+        def->data.vnc.keymap = virXMLPropString(node, "keymap");
+    } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) {
+        def->data.sdl.xauth = virXMLPropString(node, "xauth");
+        def->data.sdl.display = virXMLPropString(node, "display");
+    }
+
+cleanup:
+    VIR_FREE(type);
+
+    return def;
+
+error:
+    virDomainGraphicsDefFree(def);
+    def = NULL;
+    goto cleanup;
+}
+
+
+static virDomainSoundDefPtr
+virDomainSoundDefParseXML(virConnectPtr conn,
+                          const xmlNodePtr node) {
+
+    char *model;
+    virDomainSoundDefPtr def;
+
+    if (VIR_ALLOC(def) < 0) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    model = virXMLPropString(node, "model");
+    if ((def->model = virDomainSoundModelTypeFromString(model)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unknown sound model '%s'"), model);
+        goto error;
+    }
+
+cleanup:
+    VIR_FREE(model);
+
+    return def;
+
+error:
+    virDomainSoundDefFree(def);
+    def = NULL;
+    goto cleanup;
+}
+
+
+static int virDomainLifecycleParseXML(virConnectPtr conn,
+                                      xmlXPathContextPtr ctxt,
+                                      const char *xpath,
+                                      int *val,
+                                      int defaultVal)
+{
+    char *tmp = virXPathString(xpath, ctxt);
+    if (tmp == NULL) {
+        *val = defaultVal;
+    } else {
+        *val = virDomainLifecycleTypeFromString(tmp);
+        if (*val < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown lifecycle action %s"), tmp);
+            VIR_FREE(tmp);
+            return -1;
+        }
+        VIR_FREE(tmp);
+    }
+    return 0;
+}
+
+
+virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn,
+                                              const virDomainDefPtr def,
+                                              const char *xmlStr)
+{
+    xmlDocPtr xml;
+    xmlNodePtr node;
+    virDomainDeviceDefPtr dev = NULL;
+
+    if (!(xml = xmlReadDoc(BAD_CAST xmlStr, "device.xml", NULL,
+                           XML_PARSE_NOENT | XML_PARSE_NONET |
+                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
+        virDomainReportError(conn, VIR_ERR_XML_ERROR, NULL);
+        goto error;
+    }
+
+    node = xmlDocGetRootElement(xml);
+    if (node == NULL) {
+        virDomainReportError(conn, VIR_ERR_XML_ERROR,
+                             "%s", _("missing root element"));
+        goto error;
+    }
+
+    if (VIR_ALLOC(dev) < 0) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        goto error;
+    }
+
+    if (xmlStrEqual(node->name, BAD_CAST "disk")) {
+        dev->type = VIR_DOMAIN_DEVICE_DISK;
+        if (!(dev->data.disk = virDomainDiskDefParseXML(conn, node)))
+            goto error;
+    } else if (xmlStrEqual(node->name, BAD_CAST "interface")) {
+        dev->type = VIR_DOMAIN_DEVICE_NET;
+        if (!(dev->data.net = virDomainNetDefParseXML(conn, node)))
+            goto error;
+    } else if (xmlStrEqual(node->name, BAD_CAST "input")) {
+        dev->type = VIR_DOMAIN_DEVICE_DISK;
+        if (!(dev->data.input = virDomainInputDefParseXML(conn, def->os.type, node)))
+            goto error;
+    } else if (xmlStrEqual(node->name, BAD_CAST "sound")) {
+        dev->type = VIR_DOMAIN_DEVICE_SOUND;
+        if (!(dev->data.sound = virDomainSoundDefParseXML(conn, node)))
+            goto error;
+    } else {
+        virDomainReportError(conn, VIR_ERR_XML_ERROR,
+                             "%s", _("unknown device type"));
+        goto error;
+    }
+
+    xmlFreeDoc(xml);
+
+    return dev;
+
+  error:
+    xmlFreeDoc(xml);
+    VIR_FREE(dev);
+    return NULL;
+}
+
+
+static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn,
+                                            virCapsPtr caps,
+                                            xmlDocPtr xml)
+{
+    xmlXPathContextPtr ctxt = NULL;
+    xmlNodePtr *nodes = NULL, node = NULL;
+    char *tmp = NULL;
+    int i, n;
+    virDomainDefPtr def;
+
+    if (VIR_ALLOC(def) < 0) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY,
+                         "%s", _("failed to allocate space for xmlXPathContext"));
+        return NULL;
+    }
+    def->id = -1;
+
+    ctxt = xmlXPathNewContext(xml);
+    if (ctxt == NULL) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY,
+                         "%s", _("failed to allocate space for xmlXPathContext"));
+        goto error;
+    }
+
+
+    /* Find out what type of QEMU virtualization to use */
+    if (!(tmp = virXPathString("string(/domain/@type)", ctxt))) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("missing domain type attribute"));
+        goto error;
+    }
+
+    if ((def->virtType = virDomainVirtTypeFromString(tmp)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("invalid domain type %s"), tmp);
+        goto error;
+    }
+    VIR_FREE(tmp);
+
+    /* Extract domain name */
+    if (!(def->name = virXPathString("string(/domain/name[1])", ctxt))) {
+        virDomainReportError(conn, VIR_ERR_NO_NAME, NULL);
+        goto error;
+    }
+
+    /* Extract domain uuid */
+    tmp = virXPathString("string(/domain/uuid[1])", ctxt);
+    if (!tmp) {
+        int err;
+        if ((err = virUUIDGenerate(def->uuid))) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("Failed to generate UUID: %s"),
+                                 strerror(err));
+            goto error;
+        }
+    } else {
+        if (virUUIDParse(tmp, def->uuid) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("malformed uuid element"));
+            goto error;
+        }
+        VIR_FREE(tmp);
+    }
+
+    /* Extract domain memory */
+    if (virXPathULong("string(/domain/memory[1])", ctxt, &def->maxmem) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("missing memory element"));
+        goto error;
+    }
+
+    if (virXPathULong("string(/domain/currentMemory[1])", ctxt, &def->memory) < 0)
+        def->memory = def->maxmem;
+
+    if (virXPathULong("string(/domain/vcpu[1])", ctxt, &def->vcpus) < 0)
+        def->vcpus = 1;
+
+    tmp = virXPathString("string(/domain/vcpu[1]/@cpuset)", ctxt);
+    if (tmp) {
+        char *set = tmp;
+        def->cpumasklen = VIR_DOMAIN_CPUMASK_LEN;
+        if (VIR_ALLOC_N(def->cpumask, def->cpumasklen) < 0) {
+            virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+            goto error;
+        }
+        if (virDomainCpuSetParse(conn, (const char **)&set,
+                                 0, def->cpumask,
+                                 def->cpumasklen) < 0)
+            goto error;
+        VIR_FREE(tmp);
+    }
+
+    if ((n = virXPathNodeSet("/domain/features/*", ctxt, &nodes)) > 0) {
+        for (i = 0 ; i < n ; i++) {
+            int val = virDomainFeatureTypeFromString((const char *)nodes[i]->name);
+            if (val < 0) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     _("unexpected feature %s"),
+                                     nodes[i]->name);
+                goto error;
+            }
+            def->features |= (1 << val);
+        }
+    }
+    VIR_FREE(nodes);
+
+    if (virDomainLifecycleParseXML(conn, ctxt, "string(/domain/on_reboot[1])",
+                                   &def->onReboot, VIR_DOMAIN_LIFECYCLE_RESTART) < 0)
+        goto error;
+
+    if (virDomainLifecycleParseXML(conn, ctxt, "string(/domain/on_poweroff[1])",
+                                   &def->onPoweroff, VIR_DOMAIN_LIFECYCLE_DESTROY) < 0)
+        goto error;
+
+    if (virDomainLifecycleParseXML(conn, ctxt, "string(/domain/on_crash[1])",
+                                   &def->onCrash, VIR_DOMAIN_LIFECYCLE_DESTROY) < 0)
+        goto error;
+
+
+    tmp = virXPathString("string(/domain/clock/@offset)", ctxt);
+    if (tmp && STREQ(tmp, "localtime"))
+        def->localtime = 1;
+    VIR_FREE(tmp);
+
+    def->os.bootloader = virXPathString("string(/domain/bootloader)", ctxt);
+    def->os.bootloaderArgs = virXPathString("string(/domain/bootloader_args)", ctxt);
+
+    def->os.type = virXPathString("string(/domain/os/type[1])", ctxt);
+    if (!def->os.type) {
+        if (def->os.bootloader) {
+            def->os.type = strdup("xen");
+            if (!def->os.type) {
+                virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+                goto error;
+            }
+        } else {
+            virDomainReportError(conn, VIR_ERR_OS_TYPE,
+                                 "%s", _("no OS type"));
+            goto error;
+        }
+    }
+    /*
+     * HACK: For xen driver we previously used bogus 'linux' as the
+     * os type for paravirt, whereas capabilities declare it to
+     * be 'xen'. So we accept the former and convert
+     */
+    if (STREQ(def->os.type, "linux") &&
+        def->virtType == VIR_DOMAIN_VIRT_XEN) {
+        VIR_FREE(def->os.type);
+        if (!(def->os.type = strdup("xen"))) {
+            virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+            goto error;
+        }
+    }
+
+    if (!virCapabilitiesSupportsGuestOSType(caps, def->os.type)) {
+        virDomainReportError(conn, VIR_ERR_OS_TYPE,
+                             "%s", def->os.type);
+        goto error;
+    }
+
+    def->os.arch = virXPathString("string(/domain/os/type[1]/@arch)", ctxt);
+    if (!def->os.arch) {
+        const char *defaultArch = virCapabilitiesDefaultGuestArch(caps, def->os.type);
+        if (defaultArch == NULL) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("no supported architecture for os type '%s'"),
+                                 def->os.type);
+            goto error;
+        }
+        if (!(def->os.arch = strdup(defaultArch))) {
+            virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+            goto error;
+        }
+    }
+
+    def->os.machine = virXPathString("string(/domain/os/type[1]/@machine)", ctxt);
+    if (!def->os.machine) {
+        const char *defaultMachine = virCapabilitiesDefaultGuestMachine(caps,
+                                                                        def->os.type,
+                                                                        def->os.arch);
+        if (defaultMachine != NULL) {
+            if (!(def->os.machine = strdup(defaultMachine))) {
+                virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+                goto error;
+            }
+        }
+    }
+
+    if (!def->os.bootloader) {
+        def->os.kernel = virXPathString("string(/domain/os/kernel[1])", ctxt);
+        def->os.initrd = virXPathString("string(/domain/os/initrd[1])", ctxt);
+        def->os.cmdline = virXPathString("string(/domain/os/cmdline[1])", ctxt);
+        def->os.root = virXPathString("string(/domain/os/root[1])", ctxt);
+        def->os.loader = virXPathString("string(/domain/os/loader[1])", ctxt);
+
+        /* analysis of the boot devices */
+        if ((n = virXPathNodeSet("/domain/os/boot", ctxt, &nodes)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("cannot extract boot device"));
+            goto error;
+        }
+        for (i = 0 ; i < n && i < VIR_DOMAIN_BOOT_LAST ; i++) {
+            int val;
+            char *dev = virXMLPropString(nodes[i], "dev");
+            if (!dev) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     "%s", _("missing boot device"));
+                goto error;
+            }
+            if ((val = virDomainBootTypeFromString(dev)) < 0) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     _("unknown boot device '%s'"),
+                                     dev);
+                VIR_FREE(dev);
+                goto error;
+            }
+            VIR_FREE(dev);
+            def->os.bootDevs[def->os.nBootDevs++] = val;
+        }
+        if (def->os.nBootDevs == 0) {
+            def->os.nBootDevs = 1;
+            def->os.bootDevs[0] = VIR_DOMAIN_BOOT_DISK;
+        }
+        VIR_FREE(nodes);
+    }
+
+    def->emulator = virXPathString("string(/domain/devices/emulator[1])", ctxt);
+    if (!def->emulator) {
+        const char *type = virDomainVirtTypeToString(def->virtType);
+        if (!type) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("unknown virt type"));
+            goto error;
+        }
+        const char *emulator = virCapabilitiesDefaultGuestEmulator(caps,
+                                                                   def->os.type,
+                                                                   def->os.arch,
+                                                                   type);
+        if (!emulator) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("unsupported guest type"));
+            goto error;
+        }
+        if (!(def->emulator = strdup(emulator))) {
+            virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+            goto error;
+        }
+    }
+
+    /* analysis of the disk devices */
+    if ((n = virXPathNodeSet("/domain/devices/disk", ctxt, &nodes)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("cannot extract disk devices"));
+        goto error;
+    }
+    for (i = 0 ; i < n ; i++) {
+        virDomainDiskDefPtr disk = virDomainDiskDefParseXML(conn,
+                                                            nodes[i]);
+        if (!disk)
+            goto error;
+
+        /* Maintain list in sorted order according to target device name */
+        if (def->disks == NULL) {
+            disk->next = def->disks;
+            def->disks = disk;
+        } else {
+            virDomainDiskDefPtr ptr = def->disks;
+            while (ptr) {
+                if (!ptr->next || virDomainDiskCompare(disk, ptr->next) < 0) {
+                    disk->next = ptr->next;
+                    ptr->next = disk;
+                    break;
+                }
+                ptr = ptr->next;
+            }
+        }
+    }
+    VIR_FREE(nodes);
+
+    /* analysis of the network devices */
+    if ((n = virXPathNodeSet("/domain/devices/interface", ctxt, &nodes)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("cannot extract network devices"));
+        goto error;
+    }
+    for (i = n - 1 ; i >= 0 ; i--) {
+        virDomainNetDefPtr net = virDomainNetDefParseXML(conn,
+                                                         nodes[i]);
+        if (!net)
+            goto error;
+
+        net->next = def->nets;
+        def->nets = net;
+    }
+    VIR_FREE(nodes);
+
+
+    /* analysis of the character devices */
+    if ((n = virXPathNodeSet("/domain/devices/parallel", ctxt, &nodes)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("cannot extract parallel devices"));
+        goto error;
+    }
+    for (i = n - 1 ; i >= 0 ; i--) {
+        virDomainChrDefPtr chr = virDomainChrDefParseXML(conn,
+                                                         nodes[i]);
+        if (!chr)
+            goto error;
+
+        chr->dstPort = i;
+        chr->next = def->parallels;
+        def->parallels = chr;
+    }
+    VIR_FREE(nodes);
+
+    if ((n = virXPathNodeSet("/domain/devices/serial", ctxt, &nodes)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("cannot extract serial devices"));
+        goto error;
+    }
+    for (i = n - 1 ; i >= 0 ; i--) {
+        virDomainChrDefPtr chr = virDomainChrDefParseXML(conn,
+                                                         nodes[i]);
+        if (!chr)
+            goto error;
+
+        chr->dstPort = i;
+        chr->next = def->serials;
+        def->serials = chr;
+    }
+    VIR_FREE(nodes);
+
+    /*
+     * If no serial devices were listed, then look for console
+     * devices which is the legacy syntax for the same thing
+     */
+    if (def->serials == NULL) {
+        if ((node = virXPathNode("/domain/devices/console[1]", ctxt)) != NULL) {
+            virDomainChrDefPtr chr = virDomainChrDefParseXML(conn,
+                                                             node);
+            if (!chr)
+                goto error;
+
+            chr->dstPort = 0;
+            /*
+             * For HVM console actually created a serial device
+             * while for non-HVM it was a parvirt console
+             */
+            if (STREQ(def->os.type, "hvm")) {
+                chr->next = def->serials;
+                def->serials = chr;
+            } else {
+                def->console = chr;
+            }
+        }
+    }
+
+
+    /* analysis of the input devices */
+    if ((n = virXPathNodeSet("/domain/devices/input", ctxt, &nodes)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("cannot extract input devices"));
+        goto error;
+    }
+    for (i = n - 1 ; i >= 0 ; i--) {
+        virDomainInputDefPtr input = virDomainInputDefParseXML(conn,
+                                                               def->os.type,
+                                                               nodes[i]);
+        if (!input)
+            goto error;
+
+
+        /* With QEMU / KVM / Xen graphics, mouse + PS/2 is implicit
+         * with graphics, so don't store it.
+         * XXX will this be true for other virt types ? */
+        if ((STREQ(def->os.type, "hvm") &&
+             input->bus == VIR_DOMAIN_INPUT_BUS_PS2 &&
+             input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE) ||
+            (STRNEQ(def->os.type, "hvm") &&
+             input->bus == VIR_DOMAIN_INPUT_BUS_XEN &&
+             input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE)) {
+            virDomainInputDefFree(input);
+            continue;
+        }
+
+        input->next = def->inputs;
+        def->inputs = input;
+    }
+    VIR_FREE(nodes);
+
+    /* analysis of the input devices */
+    if ((n = virXPathNodeSet("/domain/devices/graphics", ctxt, &nodes)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("cannot extract graphics devices"));
+        goto error;
+    }
+    if (n > 0) {
+        virDomainGraphicsDefPtr graphics = virDomainGraphicsDefParseXML(conn,
+                                                                        nodes[0]);
+        if (!graphics)
+            goto error;
+
+        def->graphics = graphics;
+    }
+    VIR_FREE(nodes);
+
+    /* If graphics are enabled, there's an implicit PS2 mouse */
+    if (def->graphics != NULL) {
+        virDomainInputDefPtr input;
+
+        if (VIR_ALLOC(input) < 0) {
+            virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+            goto error;
+        }
+        if (STREQ(def->os.type, "hvm")) {
+            input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE;
+            input->bus = VIR_DOMAIN_INPUT_BUS_PS2;
+        } else {
+            input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE;
+            input->bus = VIR_DOMAIN_INPUT_BUS_XEN;
+        }
+        input->next = def->inputs;
+        def->inputs = input;
+    }
+
+
+    /* analysis of the sound devices */
+    if ((n = virXPathNodeSet("/domain/devices/sound", ctxt, &nodes)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("cannot extract sound devices"));
+        goto error;
+    }
+    for (i = n - 1 ; i >= 0 ; i--) {
+        int collision = 0;
+        virDomainSoundDefPtr check;
+        virDomainSoundDefPtr sound = virDomainSoundDefParseXML(conn,
+                                                               nodes[i]);
+        if (!sound)
+            goto error;
+
+        /* Verify there's no duplicated sound card */
+        check = def->sounds;
+        while (check) {
+            if (check->model == sound->model)
+                collision = 1;
+            check = check->next;
+        }
+        if (collision) {
+            virDomainSoundDefFree(sound);
+            continue;
+        }
+
+        sound->next = def->sounds;
+        def->sounds = sound;
+    }
+    VIR_FREE(nodes);
+
+    xmlXPathFreeContext(ctxt);
+
+    return def;
+
+ error:
+    VIR_FREE(tmp);
+    VIR_FREE(nodes);
+    xmlXPathFreeContext(ctxt);
+    virDomainDefFree(def);
+    return NULL;
+}
+
+
+virDomainDefPtr virDomainDefParse(virConnectPtr conn,
+                                  virCapsPtr caps,
+                                  const char *xmlStr,
+                                  const char *displayName)
+{
+    xmlDocPtr xml;
+    virDomainDefPtr def;
+
+    if (!(xml = xmlReadDoc(BAD_CAST xmlStr,
+                           displayName ? displayName : "domain.xml", NULL,
+                           XML_PARSE_NOENT | XML_PARSE_NONET |
+                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
+        virDomainReportError(conn, VIR_ERR_XML_ERROR, NULL);
+        return NULL;
+    }
+
+    def = virDomainDefParseXML(conn, caps, xml);
+
+    xmlFreeDoc(xml);
+
+    return def;
+}
+
+/************************************************************************
+ *									*
+ * Parser and converter for the CPUset strings used in libvirt		*
+ *									*
+ ************************************************************************/
+/**
+ * virDomainCpuNumberParse
+ * @str: pointer to the char pointer used
+ * @maxcpu: maximum CPU number allowed
+ *
+ * Parse a CPU number
+ *
+ * Returns the CPU number or -1 in case of error. @str will be
+ *         updated to skip the number.
+ */
+static int
+virDomainCpuNumberParse(const char **str, int maxcpu)
+{
+    int ret = 0;
+    const char *cur = *str;
+
+    if (!c_isdigit(*cur))
+        return (-1);
+
+    while (c_isdigit(*cur)) {
+        ret = ret * 10 + (*cur - '0');
+        if (ret >= maxcpu)
+            return (-1);
+        cur++;
+    }
+    *str = cur;
+    return (ret);
+}
+
+/**
+ * virDomainCpuSetFormat:
+ * @conn: connection
+ * @cpuset: pointer to a char array for the CPU set
+ * @maxcpu: number of elements available in @cpuset
+ *
+ * Serialize the cpuset to a string
+ *
+ * Returns the new string NULL in case of error. The string need to be
+ *         freed by the caller.
+ */
+char *
+virDomainCpuSetFormat(virConnectPtr conn, char *cpuset, int maxcpu)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    int start, cur;
+    int first = 1;
+
+    if ((cpuset == NULL) || (maxcpu <= 0) || (maxcpu > 100000))
+        return (NULL);
+
+    cur = 0;
+    start = -1;
+    while (cur < maxcpu) {
+        if (cpuset[cur]) {
+            if (start == -1)
+                start = cur;
+        } else if (start != -1) {
+            if (!first)
+                virBufferAddLit(&buf, ",");
+            else
+                first = 0;
+            if (cur == start + 1)
+                virBufferVSprintf(&buf, "%d", start);
+            else
+                virBufferVSprintf(&buf, "%d-%d", start, cur - 1);
+            start = -1;
+        }
+        cur++;
+    }
+    if (start != -1) {
+        if (!first)
+            virBufferAddLit(&buf, ",");
+        if (maxcpu == start + 1)
+            virBufferVSprintf(&buf, "%d", start);
+        else
+            virBufferVSprintf(&buf, "%d-%d", start, maxcpu - 1);
+    }
+
+    if (virBufferError(&buf)) {
+        virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    return virBufferContentAndReset(&buf);
+}
+
+/**
+ * virDomainCpuSetParse:
+ * @conn: connection
+ * @str: pointer to a CPU set string pointer
+ * @sep: potential character used to mark the end of string if not 0
+ * @cpuset: pointer to a char array for the CPU set
+ * @maxcpu: number of elements available in @cpuset
+ *
+ * Parse the cpu set, it will set the value for enabled CPUs in the @cpuset
+ * to 1, and 0 otherwise. The syntax allows coma separated entries each
+ * can be either a CPU number, ^N to unset that CPU or N-M for ranges.
+ *
+ * Returns the number of CPU found in that set, or -1 in case of error.
+ *         @cpuset is modified accordingly to the value parsed.
+ *         @str is updated to the end of the part parsed
+ */
+int
+virDomainCpuSetParse(virConnectPtr conn, const char **str, char sep,
+                     char *cpuset, int maxcpu)
+{
+    const char *cur;
+    int ret = 0;
+    int i, start, last;
+    int neg = 0;
+
+    if ((str == NULL) || (cpuset == NULL) || (maxcpu <= 0) ||
+        (maxcpu > 100000))
+        return (-1);
+
+    cur = *str;
+    virSkipSpaces(&cur);
+    if (*cur == 0)
+        goto parse_error;
+
+    /* initialize cpumap to all 0s */
+    for (i = 0; i < maxcpu; i++)
+        cpuset[i] = 0;
+    ret = 0;
+
+    while ((*cur != 0) && (*cur != sep)) {
+        /*
+         * 3 constructs are allowed:
+         *     - N   : a single CPU number
+         *     - N-M : a range of CPU numbers with N < M
+         *     - ^N  : remove a single CPU number from the current set
+         */
+        if (*cur == '^') {
+            cur++;
+            neg = 1;
+        }
+
+        if (!c_isdigit(*cur))
+            goto parse_error;
+        start = virDomainCpuNumberParse(&cur, maxcpu);
+        if (start < 0)
+            goto parse_error;
+        virSkipSpaces(&cur);
+        if ((*cur == ',') || (*cur == 0) || (*cur == sep)) {
+            if (neg) {
+                if (cpuset[start] == 1) {
+                    cpuset[start] = 0;
+                    ret--;
+                }
+            } else {
+                if (cpuset[start] == 0) {
+                    cpuset[start] = 1;
+                    ret++;
+                }
+            }
+        } else if (*cur == '-') {
+            if (neg)
+                goto parse_error;
+            cur++;
+            virSkipSpaces(&cur);
+            last = virDomainCpuNumberParse(&cur, maxcpu);
+            if (last < start)
+                goto parse_error;
+            for (i = start; i <= last; i++) {
+                if (cpuset[i] == 0) {
+                    cpuset[i] = 1;
+                    ret++;
+                }
+            }
+            virSkipSpaces(&cur);
+        }
+        if (*cur == ',') {
+            cur++;
+            virSkipSpaces(&cur);
+            neg = 0;
+        } else if ((*cur == 0) || (*cur == sep)) {
+            break;
+        } else
+            goto parse_error;
+    }
+    *str = cur;
+    return (ret);
+
+  parse_error:
+    virDomainReportError(conn, VIR_ERR_XEN_CALL,
+                         "%s", _("topology cpuset syntax error"));
+    return (-1);
+}
+
+
+static int
+virDomainLifecycleDefFormat(virConnectPtr conn,
+                            virBufferPtr buf,
+                            int type,
+                            const char *name)
+{
+    const char *typeStr = virDomainLifecycleTypeToString(type);
+    if (!typeStr) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected lifecycle type %d"), type);
+        return -1;
+    }
+
+    virBufferVSprintf(buf, "  <%s>%s</%s>\n", name, typeStr, name);
+
+    return 0;
+}
+
+
+static int
+virDomainDiskDefFormat(virConnectPtr conn,
+                       virBufferPtr buf,
+                       virDomainDiskDefPtr def)
+{
+    const char *type = virDomainDiskTypeToString(def->type);
+    const char *device = virDomainDiskDeviceTypeToString(def->device);
+    const char *bus = virDomainDiskBusTypeToString(def->bus);
+
+    if (!type) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected disk type %d"), def->type);
+        return -1;
+    }
+    if (!device) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected disk device %d"), def->device);
+        return -1;
+    }
+    if (!bus) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected disk bus %d"), def->bus);
+        return -1;
+    }
+
+    virBufferVSprintf(buf,
+                      "    <disk type='%s' device='%s'>\n",
+                      type, device);
+
+    if (def->driverName) {
+        if (def->driverType)
+            virBufferVSprintf(buf,
+                              "      <driver name='%s' type='%s'/>\n",
+                              def->driverName, def->driverType);
+        else
+            virBufferVSprintf(buf,
+                              "      <driver name='%s'/>\n",
+                              def->driverName);
+    }
+
+    if (def->src) {
+        if (def->type == VIR_DOMAIN_DISK_TYPE_FILE)
+            virBufferVSprintf(buf, "      <source file='%s'/>\n",
+                              def->src);
+        else
+            virBufferVSprintf(buf, "      <source dev='%s'/>\n",
+                              def->src);
+    }
+
+    virBufferVSprintf(buf, "      <target dev='%s' bus='%s'/>\n",
+                      def->dst, bus);
+
+    if (def->readonly)
+        virBufferAddLit(buf, "      <readonly/>\n");
+    if (def->shared)
+        virBufferAddLit(buf, "      <shareable/>\n");
+
+    virBufferAddLit(buf, "    </disk>\n");
+
+    return 0;
+}
+
+static int
+virDomainNetDefFormat(virConnectPtr conn,
+                      virBufferPtr buf,
+                      virDomainNetDefPtr def)
+{
+    const char *type = virDomainNetTypeToString(def->type);
+
+    if (!type) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected net type %d"), def->type);
+        return -1;
+    }
+
+    virBufferVSprintf(buf, "    <interface type='%s'>\n", type);
+
+    virBufferVSprintf(buf,
+                      "      <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n",
+                      def->mac[0], def->mac[1], def->mac[2],
+                      def->mac[3], def->mac[4], def->mac[5]);
+
+    switch (def->type) {
+    case VIR_DOMAIN_NET_TYPE_NETWORK:
+        virBufferVSprintf(buf, "      <source network='%s'/>\n",
+                          def->dst.network.name);
+
+        if (def->dst.network.ifname)
+            virBufferVSprintf(buf, "      <target dev='%s'/>\n",
+                              def->dst.network.ifname);
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_ETHERNET:
+        if (def->dst.ethernet.ifname)
+            virBufferVSprintf(buf, "      <target dev='%s'/>\n",
+                              def->dst.ethernet.ifname);
+        if (def->dst.ethernet.ipaddr)
+            virBufferVSprintf(buf, "      <ip address='%s'/>\n",
+                              def->dst.ethernet.ipaddr);
+        if (def->dst.ethernet.script)
+            virBufferVSprintf(buf, "      <script path='%s'/>\n",
+                              def->dst.ethernet.script);
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_BRIDGE:
+        virBufferVSprintf(buf, "      <source bridge='%s'/>\n",
+                          def->dst.bridge.brname);
+        if (def->dst.bridge.ifname)
+            virBufferVSprintf(buf, "      <target dev='%s'/>\n",
+                              def->dst.bridge.ifname);
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_SERVER:
+    case VIR_DOMAIN_NET_TYPE_CLIENT:
+    case VIR_DOMAIN_NET_TYPE_MCAST:
+        if (def->dst.socket.address)
+            virBufferVSprintf(buf, "      <source address='%s' port='%d'/>\n",
+                              def->dst.socket.address, def->dst.socket.port);
+        else
+            virBufferVSprintf(buf, "      <source port='%d'/>\n",
+                              def->dst.socket.port);
+    }
+
+    if (def->model)
+        virBufferVSprintf(buf, "      <model type='%s'/>\n",
+                          def->model);
+
+    virBufferAddLit(buf, "    </interface>\n");
+
+    return 0;
+}
+
+
+static int
+virDomainChrDefFormat(virConnectPtr conn,
+                      virBufferPtr buf,
+                      virDomainChrDefPtr def,
+                      const char *name)
+{
+    const char *type = virDomainChrTypeToString(def->type);
+
+    if (!type) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected char type %d"), def->type);
+        return -1;
+    }
+
+    /* Compat with legacy  <console tty='/dev/pts/5'/> syntax */
+    if (STREQ(name, "console") &&
+        def->type == VIR_DOMAIN_CHR_TYPE_PTY &&
+        def->data.file.path) {
+        virBufferVSprintf(buf, "    <%s type='%s' tty='%s'>\n",
+                          name, type,
+                          def->data.file.path);
+    } else {
+        virBufferVSprintf(buf, "    <%s type='%s'>\n",
+                          name, type);
+    }
+
+    switch (def->type) {
+    case VIR_DOMAIN_CHR_TYPE_NULL:
+    case VIR_DOMAIN_CHR_TYPE_VC:
+    case VIR_DOMAIN_CHR_TYPE_STDIO:
+        /* nada */
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_PTY:
+    case VIR_DOMAIN_CHR_TYPE_DEV:
+    case VIR_DOMAIN_CHR_TYPE_FILE:
+    case VIR_DOMAIN_CHR_TYPE_PIPE:
+        if (def->type != VIR_DOMAIN_CHR_TYPE_PTY ||
+            def->data.file.path) {
+            virBufferVSprintf(buf, "      <source path='%s'/>\n",
+                              def->data.file.path);
+        }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_UDP:
+        if (def->data.udp.bindService &&
+            def->data.udp.bindHost) {
+            virBufferVSprintf(buf, "      <source mode='bind' host='%s' service='%s'/>\n",
+                              def->data.udp.bindHost,
+                              def->data.udp.bindService);
+        } else if (def->data.udp.bindHost) {
+            virBufferVSprintf(buf, "      <source mode='bind' host='%s'/>\n",
+                              def->data.udp.bindHost);
+        } else if (def->data.udp.bindService) {
+            virBufferVSprintf(buf, "      <source mode='bind' service='%s'/>\n",
+                              def->data.udp.bindService);
+        }
+
+        if (def->data.udp.connectService &&
+            def->data.udp.connectHost) {
+            virBufferVSprintf(buf, "      <source mode='connect' host='%s' service='%s'/>\n",
+                              def->data.udp.connectHost,
+                              def->data.udp.connectService);
+        } else if (def->data.udp.connectHost) {
+            virBufferVSprintf(buf, "      <source mode='connect' host='%s'/>\n",
+                              def->data.udp.connectHost);
+        } else if (def->data.udp.connectService) {
+            virBufferVSprintf(buf, "      <source mode='connect' service='%s'/>\n",
+                              def->data.udp.connectService);
+        }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_TCP:
+        virBufferVSprintf(buf, "      <source mode='%s' host='%s' service='%s'/>\n",
+                          def->data.tcp.listen ? "bind" : "connect",
+                          def->data.tcp.host,
+                          def->data.tcp.service);
+        virBufferVSprintf(buf, "      <protocol type='%s'/>\n",
+                          def->data.tcp.protocol ==
+                          VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET
+                          ? "telnet" : "raw");
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_UNIX:
+        virBufferVSprintf(buf, "      <source mode='%s' path='%s'/>\n",
+                          def->data.nix.listen ? "bind" : "connect",
+                          def->data.nix.path);
+        break;
+    }
+
+    virBufferVSprintf(buf, "      <target port='%d'/>\n",
+                      def->dstPort);
+
+    virBufferVSprintf(buf, "    </%s>\n",
+                      name);
+
+    return 0;
+}
+
+static int
+virDomainSoundDefFormat(virConnectPtr conn,
+                        virBufferPtr buf,
+                        virDomainSoundDefPtr def)
+{
+    const char *model = virDomainSoundModelTypeToString(def->model);
+
+    if (!model) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected sound model %d"), def->model);
+        return -1;
+    }
+
+    virBufferVSprintf(buf, "    <sound model='%s'/>\n",
+                      model);
+
+    return 0;
+}
+
+static int
+virDomainInputDefFormat(virConnectPtr conn,
+                        virBufferPtr buf,
+                        virDomainInputDefPtr def)
+{
+    const char *type = virDomainInputTypeToString(def->type);
+    const char *bus = virDomainInputBusTypeToString(def->bus);
+
+    if (!type) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected input type %d"), def->type);
+        return -1;
+    }
+    if (!bus) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected input bus type %d"), def->bus);
+        return -1;
+    }
+
+    virBufferVSprintf(buf, "    <input type='%s' bus='%s'/>\n",
+                      type, bus);
+
+    return 0;
+}
+
+
+static int
+virDomainGraphicsDefFormat(virConnectPtr conn,
+                           virBufferPtr buf,
+                           virDomainGraphicsDefPtr def,
+                           int secure)
+{
+    const char *type = virDomainGraphicsTypeToString(def->type);
+
+    if (!type) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected net type %d"), def->type);
+        return -1;
+    }
+
+    virBufferVSprintf(buf, "    <graphics type='%s'", type);
+
+    switch (def->type) {
+    case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
+        if (def->data.vnc.autoport)
+            virBufferAddLit(buf, " port='-1'");
+        else if (def->data.vnc.port)
+            virBufferVSprintf(buf, " port='%d'",
+                              def->data.vnc.port);
+
+        virBufferVSprintf(buf, " autoport='%s'",
+                          def->data.vnc.autoport ? "yes" : "no");
+
+        if (def->data.vnc.listenAddr)
+            virBufferVSprintf(buf, " listen='%s'",
+                              def->data.vnc.listenAddr);
+
+        if (def->data.vnc.keymap)
+            virBufferVSprintf(buf, " keymap='%s'",
+                              def->data.vnc.keymap);
+
+        if (def->data.vnc.passwd && secure)
+            virBufferVSprintf(buf, " passwd='%s'",
+                              def->data.vnc.passwd);
+
+        break;
+
+    case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
+        if (def->data.sdl.display)
+            virBufferVSprintf(buf, " display='%s'",
+                              def->data.sdl.display);
+
+        if (def->data.sdl.xauth)
+            virBufferVSprintf(buf, " xauth='%s'",
+                              def->data.sdl.xauth);
+        break;
+    }
+
+    virBufferAddLit(buf, "/>\n");
+
+    return 0;
+}
+
+char *virDomainDefFormat(virConnectPtr conn,
+                         virDomainDefPtr def,
+                         int secure)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    unsigned char *uuid;
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+    virDomainDiskDefPtr disk;
+    virDomainNetDefPtr net;
+    virDomainSoundDefPtr sound;
+    virDomainInputDefPtr input;
+    virDomainChrDefPtr chr;
+    const char *type = NULL, *tmp;
+    int n, allones = 1;
+
+    if (!(type = virDomainVirtTypeToString(def->virtType))) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                         _("unexpected domain type %d"), def->virtType);
+        goto cleanup;
+    }
+
+    if (def->id >= 0)
+        virBufferVSprintf(&buf, "<domain type='%s' id='%d'>\n", type, def->id);
+    else
+        virBufferVSprintf(&buf, "<domain type='%s'>\n", type);
+
+    virBufferVSprintf(&buf, "  <name>%s</name>\n", def->name);
+
+    uuid = def->uuid;
+    virUUIDFormat(uuid, uuidstr);
+    virBufferVSprintf(&buf, "  <uuid>%s</uuid>\n", uuidstr);
+
+    virBufferVSprintf(&buf, "  <memory>%lu</memory>\n", def->maxmem);
+    virBufferVSprintf(&buf, "  <currentMemory>%lu</currentMemory>\n",
+                      def->memory);
+
+    for (n = 0 ; n < def->cpumasklen ; n++)
+        if (def->cpumask[n] != 1)
+            allones = 0;
+
+    if (allones) {
+        virBufferVSprintf(&buf, "  <vcpu>%lu</vcpu>\n", def->vcpus);
+    } else {
+        char *cpumask = NULL;
+        if ((cpumask =
+             virDomainCpuSetFormat(conn, def->cpumask, def->cpumasklen)) == NULL)
+            goto cleanup;
+        virBufferVSprintf(&buf, "  <vcpu cpuset='%s'>%lu</vcpu>\n",
+                          cpumask, def->vcpus);
+        VIR_FREE(cpumask);
+    }
+
+    if (def->os.bootloader) {
+        virBufferVSprintf(&buf, "  <bootloader>%s</bootloader>\n",
+                          def->os.bootloader);
+        if (def->os.bootloaderArgs)
+            virBufferVSprintf(&buf, "  <bootloader_args>%s</bootloader_args>\n",
+                              def->os.bootloaderArgs);
+    }
+    virBufferAddLit(&buf, "  <os>\n");
+
+    virBufferAddLit(&buf, "    <type");
+    if (def->os.arch)
+        virBufferVSprintf(&buf, " arch='%s'", def->os.arch);
+    if (def->os.machine)
+        virBufferVSprintf(&buf, " machine='%s'", def->os.machine);
+    /*
+     * HACK: For xen driver we previously used bogus 'linux' as the
+     * os type for paravirt, whereas capabilities declare it to
+     * be 'xen'. So we convert to the former for backcompat
+     */
+    if (def->virtType == VIR_DOMAIN_VIRT_XEN &&
+        STREQ(def->os.type, "xen"))
+        virBufferVSprintf(&buf, ">%s</type>\n", "linux");
+    else
+        virBufferVSprintf(&buf, ">%s</type>\n", def->os.type);
+
+    if (def->os.loader)
+        virBufferVSprintf(&buf, "    <loader>%s</loader>\n",
+                          def->os.loader);
+    if (def->os.kernel)
+        virBufferVSprintf(&buf, "    <kernel>%s</kernel>\n",
+                          def->os.kernel);
+    if (def->os.initrd)
+        virBufferVSprintf(&buf, "    <initrd>%s</initrd>\n",
+                          def->os.initrd);
+    if (def->os.cmdline)
+        virBufferVSprintf(&buf, "    <cmdline>%s</cmdline>\n",
+                          def->os.cmdline);
+    if (def->os.root)
+        virBufferVSprintf(&buf, "    <root>%s</root>\n",
+                          def->os.root);
+
+    if (!def->os.bootloader) {
+        for (n = 0 ; n < def->os.nBootDevs ; n++) {
+            const char *boottype =
+                virDomainBootTypeToString(def->os.bootDevs[n]);
+            if (!boottype) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     _("unexpected boot device type %d"),
+                                     def->os.bootDevs[n]);
+                goto cleanup;
+            }
+            virBufferVSprintf(&buf, "    <boot dev='%s'/>\n", boottype);
+        }
+    }
+
+    virBufferAddLit(&buf, "  </os>\n");
+
+    if (def->features) {
+        int i;
+        virBufferAddLit(&buf, "  <features>\n");
+        for (i = 0 ; i < VIR_DOMAIN_FEATURE_LAST ; i++) {
+            if (def->features & (1 << i)) {
+                const char *name = virDomainFeatureTypeToString(i);
+                if (!name) {
+                    virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                         _("unexpected feature %d"), i);
+                    goto cleanup;
+                }
+                virBufferVSprintf(&buf, "    <%s/>\n", name);
+            }
+        }
+        virBufferAddLit(&buf, "  </features>\n");
+    }
+
+    virBufferVSprintf(&buf, "  <clock offset='%s'/>\n",
+                      def->localtime ? "localtime" : "utc");
+
+    if (virDomainLifecycleDefFormat(conn, &buf, def->onPoweroff,
+                                    "on_poweroff") < 0)
+        goto cleanup;
+    if (virDomainLifecycleDefFormat(conn, &buf, def->onReboot,
+                                    "on_reboot") < 0)
+        goto cleanup;
+    if (virDomainLifecycleDefFormat(conn, &buf, def->onCrash,
+                                    "on_crash") < 0)
+        goto cleanup;
+
+    virBufferAddLit(&buf, "  <devices>\n");
+
+    if (def->emulator)
+        virBufferVSprintf(&buf, "    <emulator>%s</emulator>\n",
+                          def->emulator);
+
+    disk = def->disks;
+    while (disk) {
+        if (virDomainDiskDefFormat(conn, &buf, disk) < 0)
+            goto cleanup;
+        disk = disk->next;
+    }
+
+    net = def->nets;
+    while (net) {
+        if (virDomainNetDefFormat(conn, &buf, net) < 0)
+            goto cleanup;
+        net = net->next;
+    }
+
+
+    chr = def->serials;
+    while (chr) {
+        if (virDomainChrDefFormat(conn, &buf, chr, "serial") < 0)
+            goto cleanup;
+        chr = chr->next;
+    }
+
+    chr = def->parallels;
+    while (chr) {
+        if (virDomainChrDefFormat(conn, &buf, chr, "parallel") < 0)
+            goto cleanup;
+        chr = chr->next;
+    }
+
+    /* If there's a PV console that's preferred.. */
+    if (def->console) {
+        if (virDomainChrDefFormat(conn, &buf, def->console, "console") < 0)
+            goto cleanup;
+    } else if (def->serials != NULL) {
+        /* ..else for legacy compat duplicate the serial device as a console */
+        if (virDomainChrDefFormat(conn, &buf, def->serials, "console") < 0)
+            goto cleanup;
+    }
+
+    input = def->inputs;
+    while (input) {
+        if (input->bus == VIR_DOMAIN_INPUT_BUS_USB &&
+            virDomainInputDefFormat(conn, &buf, input) < 0)
+            goto cleanup;
+        input = input->next;
+    }
+
+    if (def->graphics) {
+        /* If graphics is enabled, add the implicit mouse */
+        virDomainInputDef autoInput = {
+            VIR_DOMAIN_INPUT_TYPE_MOUSE,
+            STREQ(def->os.type, "hvm") ?
+            VIR_DOMAIN_INPUT_BUS_PS2 : VIR_DOMAIN_INPUT_BUS_XEN,
+            NULL };
+
+        if (virDomainInputDefFormat(conn, &buf, &autoInput) < 0)
+            goto cleanup;
+
+        if (virDomainGraphicsDefFormat(conn, &buf, def->graphics, secure) < 0)
+            goto cleanup;
+    }
+
+    sound = def->sounds;
+    while(sound) {
+        if (virDomainSoundDefFormat(conn, &buf, sound) < 0)
+            goto cleanup;
+        sound = sound->next;
+    }
+
+    virBufferAddLit(&buf, "  </devices>\n");
+    virBufferAddLit(&buf, "</domain>\n");
+
+    if (virBufferError(&buf))
+        goto no_memory;
+
+    return virBufferContentAndReset(&buf);
+
+ no_memory:
+    virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+ cleanup:
+    tmp = virBufferContentAndReset(&buf);
+    VIR_FREE(tmp);
+    return NULL;
+}
+
diff -r f47fdc49c62f src/domain_conf.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/domain_conf.h	Sun Jun 22 18:31:20 2008 -0400
@@ -0,0 +1,461 @@
+/*
+ * domain_conf.h: domain XML processing
+ *
+ * Copyright (C) 2006-2008 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#ifndef __DOMAIN_CONF_H
+#define __DOMAIN_CONF_H
+
+#include <config.h>
+
+#include "internal.h"
+#include "capabilities.h"
+#include "util.h"
+
+/* Different types of hypervisor */
+/* NB: Keep in sync with virDomainVirtTypeToString impl */
+enum virDomainVirtType {
+    VIR_DOMAIN_VIRT_QEMU,
+    VIR_DOMAIN_VIRT_KQEMU,
+    VIR_DOMAIN_VIRT_KVM,
+    VIR_DOMAIN_VIRT_XEN,
+    VIR_DOMAIN_VIRT_LXC,
+    VIR_DOMAIN_VIRT_UML,
+    VIR_DOMAIN_VIRT_OPENVZ,
+    VIR_DOMAIN_VIRT_VSERVER,
+    VIR_DOMAIN_VIRT_LDOM,
+    VIR_DOMAIN_VIRT_TEST,
+    VIR_DOMAIN_VIRT_VMWARE,
+    VIR_DOMAIN_VIRT_HYPERV,
+
+    VIR_DOMAIN_VIRT_LAST,
+};
+
+/* Two types of disk backends */
+enum virDomainDiskType {
+    VIR_DOMAIN_DISK_TYPE_BLOCK,
+    VIR_DOMAIN_DISK_TYPE_FILE,
+
+    VIR_DOMAIN_DISK_TYPE_LAST
+};
+
+/* Three types of disk frontend */
+enum virDomainDiskDevice {
+    VIR_DOMAIN_DISK_DEVICE_DISK,
+    VIR_DOMAIN_DISK_DEVICE_CDROM,
+    VIR_DOMAIN_DISK_DEVICE_FLOPPY,
+
+    VIR_DOMAIN_DISK_DEVICE_LAST
+};
+
+enum virDomainDiskBus {
+    VIR_DOMAIN_DISK_BUS_IDE,
+    VIR_DOMAIN_DISK_BUS_FDC,
+    VIR_DOMAIN_DISK_BUS_SCSI,
+    VIR_DOMAIN_DISK_BUS_VIRTIO,
+    VIR_DOMAIN_DISK_BUS_XEN,
+
+    VIR_DOMAIN_DISK_BUS_LAST
+};
+
+/* Stores the virtual disk configuration */
+typedef struct _virDomainDiskDef virDomainDiskDef;
+typedef virDomainDiskDef *virDomainDiskDefPtr;
+struct _virDomainDiskDef {
+    int type;
+    int device;
+    int bus;
+    char *src;
+    char *dst;
+    char *driverName;
+    char *driverType;
+    unsigned int readonly : 1;
+    unsigned int shared : 1;
+
+    virDomainDiskDefPtr next;
+};
+
+
+/* 5 different types of networking config */
+enum virDomainNetType {
+    VIR_DOMAIN_NET_TYPE_USER,
+    VIR_DOMAIN_NET_TYPE_ETHERNET,
+    VIR_DOMAIN_NET_TYPE_SERVER,
+    VIR_DOMAIN_NET_TYPE_CLIENT,
+    VIR_DOMAIN_NET_TYPE_MCAST,
+    VIR_DOMAIN_NET_TYPE_NETWORK,
+    VIR_DOMAIN_NET_TYPE_BRIDGE,
+
+    VIR_DOMAIN_NET_TYPE_LAST,
+};
+
+
+#define VIR_DOMAIN_NET_MAC_SIZE 6
+
+/* Stores the virtual network interface configuration */
+typedef struct _virDomainNetDef virDomainNetDef;
+typedef virDomainNetDef *virDomainNetDefPtr;
+struct _virDomainNetDef {
+    int type;
+    unsigned char mac[VIR_DOMAIN_NET_MAC_SIZE];
+    char *model;
+    union {
+        struct {
+            char *ifname;
+            char *script;
+            char *ipaddr;
+        } ethernet;
+        struct {
+            char *address;
+            int port;
+        } socket; /* any of NET_CLIENT or NET_SERVER or NET_MCAST */
+        struct {
+            char *name;
+            char *ifname;
+        } network;
+        struct {
+            char *brname;
+            char *ifname;
+        } bridge;
+    } dst;
+
+    virDomainNetDefPtr next;
+};
+
+enum virDomainChrSrcType {
+    VIR_DOMAIN_CHR_TYPE_NULL,
+    VIR_DOMAIN_CHR_TYPE_VC,
+    VIR_DOMAIN_CHR_TYPE_PTY,
+    VIR_DOMAIN_CHR_TYPE_DEV,
+    VIR_DOMAIN_CHR_TYPE_FILE,
+    VIR_DOMAIN_CHR_TYPE_PIPE,
+    VIR_DOMAIN_CHR_TYPE_STDIO,
+    VIR_DOMAIN_CHR_TYPE_UDP,
+    VIR_DOMAIN_CHR_TYPE_TCP,
+    VIR_DOMAIN_CHR_TYPE_UNIX,
+
+    VIR_DOMAIN_CHR_TYPE_LAST,
+};
+
+enum virDomainChrTcpProtocol {
+    VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW,
+    VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET,
+
+    VIR_DOMAIN_CHR_TCP_PROTOCOL_LAST,
+};
+
+typedef struct _virDomainChrDef virDomainChrDef;
+typedef virDomainChrDef *virDomainChrDefPtr;
+struct _virDomainChrDef {
+    int dstPort;
+
+    int type;
+    union {
+        struct {
+            char *path;
+        } file; /* pty, file, pipe, or device */
+        struct {
+            char *host;
+            char *service;
+            int listen;
+            int protocol;
+        } tcp;
+        struct {
+            char *bindHost;
+            char *bindService;
+            char *connectHost;
+            char *connectService;
+        } udp;
+        struct {
+            char *path;
+            int listen;
+        } nix;
+    } data;
+
+    virDomainChrDefPtr next;
+};
+
+enum virDomainInputType {
+    VIR_DOMAIN_INPUT_TYPE_MOUSE,
+    VIR_DOMAIN_INPUT_TYPE_TABLET,
+
+    VIR_DOMAIN_INPUT_TYPE_LAST,
+};
+
+enum virDomainInputBus {
+    VIR_DOMAIN_INPUT_BUS_PS2,
+    VIR_DOMAIN_INPUT_BUS_USB,
+    VIR_DOMAIN_INPUT_BUS_XEN,
+
+    VIR_DOMAIN_INPUT_BUS_LAST
+};
+
+typedef struct _virDomainInputDef virDomainInputDef;
+typedef virDomainInputDef *virDomainInputDefPtr;
+struct _virDomainInputDef {
+    int type;
+    int bus;
+    virDomainInputDefPtr next;
+};
+
+enum virDomainSoundModel {
+    VIR_DOMAIN_SOUND_MODEL_SB16,
+    VIR_DOMAIN_SOUND_MODEL_ES1370,
+    VIR_DOMAIN_SOUND_MODEL_PCSPK,
+
+    VIR_DOMAIN_SOUND_MODEL_LAST
+};
+
+typedef struct _virDomainSoundDef virDomainSoundDef;
+typedef virDomainSoundDef *virDomainSoundDefPtr;
+struct _virDomainSoundDef {
+    int model;
+    virDomainSoundDefPtr next;
+};
+
+/* 3 possible graphics console modes */
+enum virDomainGraphicsType {
+    VIR_DOMAIN_GRAPHICS_TYPE_SDL,
+    VIR_DOMAIN_GRAPHICS_TYPE_VNC,
+
+    VIR_DOMAIN_GRAPHICS_TYPE_LAST,
+};
+
+typedef struct _virDomainGraphicsDef virDomainGraphicsDef;
+typedef virDomainGraphicsDef *virDomainGraphicsDefPtr;
+struct _virDomainGraphicsDef {
+    int type;
+    union {
+        struct {
+            int port;
+            int autoport : 1;
+            char *listenAddr;
+            char *keymap;
+            char *passwd;
+        } vnc;
+        struct {
+            char *display;
+            char *xauth;
+        } sdl;
+    } data;
+};
+
+
+
+/* Flags for the 'type' field in next struct */
+enum virDomainDeviceType {
+    VIR_DOMAIN_DEVICE_DISK,
+    VIR_DOMAIN_DEVICE_NET,
+    VIR_DOMAIN_DEVICE_INPUT,
+    VIR_DOMAIN_DEVICE_SOUND,
+};
+
+typedef struct _virDomainDeviceDef virDomainDeviceDef;
+typedef virDomainDeviceDef *virDomainDeviceDefPtr;
+struct _virDomainDeviceDef {
+    int type;
+    union {
+        virDomainDiskDefPtr disk;
+        virDomainNetDefPtr net;
+        virDomainInputDefPtr input;
+        virDomainSoundDefPtr sound;
+    } data;
+};
+
+
+#define VIR_DOMAIN_MAX_BOOT_DEVS 4
+
+/* 3 possible boot devices */
+enum virDomainBootOrder {
+    VIR_DOMAIN_BOOT_FLOPPY,
+    VIR_DOMAIN_BOOT_CDROM,
+    VIR_DOMAIN_BOOT_DISK,
+    VIR_DOMAIN_BOOT_NET,
+
+    VIR_DOMAIN_BOOT_LAST,
+};
+
+enum virDomainFeature {
+    VIR_DOMAIN_FEATURE_ACPI,
+    VIR_DOMAIN_FEATURE_APIC,
+    VIR_DOMAIN_FEATURE_PAE,
+
+    VIR_DOMAIN_FEATURE_LAST
+};
+
+enum virDomainLifecycleAction {
+    VIR_DOMAIN_LIFECYCLE_DESTROY,
+    VIR_DOMAIN_LIFECYCLE_RESTART,
+    VIR_DOMAIN_LIFECYCLE_RESTART_RENAME,
+    VIR_DOMAIN_LIFECYCLE_PRESERVE,
+
+    VIR_DOMAIN_LIFECYCLE_LAST
+};
+
+/* Operating system configuration data & machine / arch */
+typedef struct _virDomainOSDef virDomainOSDef;
+typedef virDomainOSDef *virDomainOSDefPtr;
+struct _virDomainOSDef {
+    char *type;
+    char *arch;
+    char *machine;
+    int nBootDevs;
+    int bootDevs[VIR_DOMAIN_BOOT_LAST];
+    char *kernel;
+    char *initrd;
+    char *cmdline;
+    char *root;
+    char *loader;
+    char *bootloader;
+    char *bootloaderArgs;
+};
+
+#define VIR_DOMAIN_CPUMASK_LEN 1024
+
+/* Guest VM main configuration */
+typedef struct _virDomainDef virDomainDef;
+typedef virDomainDef *virDomainDefPtr;
+struct _virDomainDef {
+    int virtType;
+    int id;
+    unsigned char uuid[VIR_UUID_BUFLEN];
+    char *name;
+
+    unsigned long memory;
+    unsigned long maxmem;
+    unsigned long vcpus;
+    int cpumasklen;
+    char *cpumask;
+
+    /* These 3 are based on virDomainLifeCycleAction enum flags */
+    int onReboot;
+    int onPoweroff;
+    int onCrash;
+
+    virDomainOSDef os;
+    char *emulator;
+    int features;
+
+    int localtime;
+
+    virDomainGraphicsDefPtr graphics;
+    virDomainDiskDefPtr disks;
+    virDomainNetDefPtr nets;
+    virDomainInputDefPtr inputs;
+    virDomainSoundDefPtr sounds;
+    virDomainChrDefPtr serials;
+    virDomainChrDefPtr parallels;
+    virDomainChrDefPtr console;
+};
+
+/* Guest VM runtime state */
+typedef struct _virDomainObj virDomainObj;
+typedef virDomainObj *virDomainObjPtr;
+struct _virDomainObj {
+    int stdin;
+    int stdout;
+    int stderr;
+    int monitor;
+    int logfile;
+    int pid;
+    int state;
+
+    int nvcpupids;
+    int *vcpupids;
+
+    unsigned int autostart : 1;
+    unsigned int persistent : 1;
+
+    char *configFile;
+    char *autostartLink;
+
+    virDomainDefPtr def; /* The current definition */
+    virDomainDefPtr newDef; /* New definition to activate at shutdown */
+
+    virDomainObjPtr next;
+};
+
+
+static inline int
+virDomainIsActive(virDomainObjPtr dom)
+{
+    return dom->def->id != -1;
+}
+
+
+virDomainObjPtr virDomainFindByID(const virDomainObjPtr doms,
+                                  int id);
+virDomainObjPtr virDomainFindByUUID(const virDomainObjPtr doms,
+                                    const unsigned char *uuid);
+virDomainObjPtr virDomainFindByName(const virDomainObjPtr doms,
+                                    const char *name);
+
+
+void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def);
+void virDomainInputDefFree(virDomainInputDefPtr def);
+void virDomainDiskDefFree(virDomainDiskDefPtr def);
+void virDomainNetDefFree(virDomainNetDefPtr def);
+void virDomainChrDefFree(virDomainChrDefPtr def);
+void virDomainSoundDefFree(virDomainSoundDefPtr def);
+void virDomainDefFree(virDomainDefPtr vm);
+void virDomainObjFree(virDomainObjPtr vm);
+
+virDomainObjPtr virDomainAssignDef(virConnectPtr conn,
+                                   virDomainObjPtr *doms,
+                                   const virDomainDefPtr def);
+void virDomainRemoveInactive(virDomainObjPtr *doms,
+                             virDomainObjPtr dom);
+
+virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn,
+                                              const virDomainDefPtr def,
+                                              const char *xmlStr);
+virDomainDefPtr virDomainDefParse(virConnectPtr conn,
+                                  virCapsPtr caps,
+                                  const char *xmlStr,
+                                  const char *displayName);
+char *virDomainDefFormat(virConnectPtr conn,
+                         virDomainDefPtr def,
+                         int secure);
+
+int virDomainCpuSetParse(virConnectPtr conn,
+                         const char **str,
+                         char sep,
+                         char *cpuset,
+                         int maxcpu);
+char *virDomainCpuSetFormat(virConnectPtr conn,
+                            char *cpuset,
+                            int maxcpu);
+
+VIR_ENUM_DECL(virDomainVirt)
+VIR_ENUM_DECL(virDomainBoot)
+VIR_ENUM_DECL(virDomainFeature)
+VIR_ENUM_DECL(virDomainLifecycle)
+VIR_ENUM_DECL(virDomainDisk)
+VIR_ENUM_DECL(virDomainDiskDevice)
+VIR_ENUM_DECL(virDomainDiskBus)
+VIR_ENUM_DECL(virDomainNet)
+VIR_ENUM_DECL(virDomainChr)
+VIR_ENUM_DECL(virDomainSoundModel)
+VIR_ENUM_DECL(virDomainInput)
+VIR_ENUM_DECL(virDomainInputBus)
+VIR_ENUM_DECL(virDomainGraphics)
+
+#endif /* __DOMAIN_CONF_H */
diff -r f47fdc49c62f src/util.h
--- a/src/util.h	Sun Jun 22 17:58:48 2008 -0400
+++ b/src/util.h	Sun Jun 22 18:31:20 2008 -0400
@@ -100,7 +100,7 @@
 
 #define VIR_ENUM_IMPL(name, lastVal, ...)                               \
     static const char const *name ## TypeList[] = { __VA_ARGS__ };      \
-    verify(ARRAY_CARDINALITY(name ## TypeList) == lastVal);             \
+    extern int (* name ## Verify (void)) [verify_true (ARRAY_CARDINALITY(name ## TypeList) == lastVal)]; \
     const char *name ## TypeToString(int type) {                        \
         return virEnumToString(name ## TypeList,                        \
                                ARRAY_CARDINALITY(name ## TypeList),     \
diff -r f47fdc49c62f src/xml.c
--- a/src/xml.c	Sun Jun 22 17:58:48 2008 -0400
+++ b/src/xml.c	Sun Jun 22 18:31:20 2008 -0400
@@ -490,6 +490,59 @@
 }
 
 /**
+ * virXPathULong:
+ * @xpath: the XPath string to evaluate
+ * @ctxt: an XPath context
+ * @value: the returned long value
+ *
+ * Convenience function to evaluate an XPath number
+ *
+ * Returns 0 in case of success in which case @value is set,
+ *         or -1 if the XPath evaluation failed or -2 if the
+ *         value doesn't have a long format.
+ */
+int
+virXPathULong(const char *xpath, xmlXPathContextPtr ctxt, unsigned long *value)
+{
+    xmlXPathObjectPtr obj;
+    int ret = 0;
+
+    if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
+        virXMLError(NULL, VIR_ERR_INTERNAL_ERROR,
+                    _("Invalid parameter to virXPathNumber()"), 0);
+        return (-1);
+    }
+    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
+        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
+        char *conv = NULL;
+        long val;
+
+        val = strtoul((const char *) obj->stringval, &conv, 10);
+        if (conv == (const char *) obj->stringval) {
+            ret = -2;
+        } else {
+            *value = val;
+        }
+    } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
+               (!(isnan(obj->floatval)))) {
+        if (obj->floatval < 0) {
+            ret = -2;
+        } else {
+            *value = (unsigned long) obj->floatval;
+            if (*value != obj->floatval) {
+                ret = -2;
+            }
+        }
+    } else {
+        ret = -1;
+    }
+
+    xmlXPathFreeObject(obj);
+    return (ret);
+}
+
+/**
  * virXPathBoolean:
  * @xpath: the XPath string to evaluate
  * @ctxt: an XPath context
@@ -578,18 +631,18 @@
                     _("Invalid parameter to virXPathNodeSet()"), 0);
         return (-1);
     }
+    if (list != NULL)
+        *list = NULL;
+
     obj = xmlXPathEval(BAD_CAST xpath, ctxt);
     if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
-        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) ||
-        (obj->nodesetval->nodeTab == NULL)) {
+        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr < 0)) {
         xmlXPathFreeObject(obj);
-        if (list != NULL)
-            *list = NULL;
         return (-1);
     }
 
     ret = obj->nodesetval->nodeNr;
-    if (list != NULL) {
+    if (list != NULL && ret) {
         if (VIR_ALLOC_N(*list, ret) < 0) {
             virXMLError(NULL, VIR_ERR_NO_MEMORY,
                         _("allocate string array"),
@@ -602,6 +655,13 @@
     }
     xmlXPathFreeObject(obj);
     return (ret);
+}
+
+char *
+virXMLPropString(xmlNodePtr node,
+                 const char *name)
+{
+    return (char *)xmlGetProp(node, BAD_CAST name);
 }
 
 /************************************************************************
diff -r f47fdc49c62f src/xml.h
--- a/src/xml.h	Sun Jun 22 17:58:48 2008 -0400
+++ b/src/xml.h	Sun Jun 22 18:31:20 2008 -0400
@@ -23,14 +23,27 @@
 int		virXPathNumber	(const char *xpath,
                                  xmlXPathContextPtr ctxt,
                                  double *value);
+int		virXPathInt	(const char *xpath,
+                                 xmlXPathContextPtr ctxt,
+                                 int *value);
+int		virXPathUInt	(const char *xpath,
+                                 xmlXPathContextPtr ctxt,
+                                 unsigned int *value);
 int		virXPathLong	(const char *xpath,
                                  xmlXPathContextPtr ctxt,
                                  long *value);
+int		virXPathULong	(const char *xpath,
+                                 xmlXPathContextPtr ctxt,
+                                 unsigned long *value);
 xmlNodePtr	virXPathNode	(const char *xpath,
                                  xmlXPathContextPtr ctxt);
 int		virXPathNodeSet	(const char *xpath,
                                  xmlXPathContextPtr ctxt,
                                  xmlNodePtr **list);
+
+char *          virXMLPropString(xmlNodePtr node,
+                                 const char *name);
+
 
 #if WITH_XEN || WITH_QEMU
 int		virParseCpuSet	(virConnectPtr conn,

-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]