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

Re: [libvirt] anyone implementing host device enumeration?



Hi Daniel -

  I'm not really ready to submit this yet (hence no [PATCH] in the
subject), but it is functional enough to play with, mostly with the
HAL-based driver, though the devkit-based one works too.  In particular,
I'm not yet happy with the code to gather capabilities and bus info
(though gather_capabilities in node_device_hal.c is close to what I'm
going for).  Also, many of the details (which properties do we care
about for which buses and capabilities?) that make this useful are
missing, though I've provided a few guesses to get started.  And
finally, it's had very little testing at this point.

  Configure --with-hal or --with-devkit to support one or both (if both
are configured, HAL is preferred, provided we can talk to hald on dbus).
There are quite a few TODO's in the patch (mostly little things).

The larger TODO's are (in (my) priority order):
 * generalize gather_capabilities (node_device_hal.c) to work for HAL
bus info as well
 * share the nodeDevices hash table among (non-remote) connections
(perhaps store it in the virNodeDriver?)
 * register for HAL events to keep device info up-to-date
 * finish virsh, python support (for now there's enough to get devs/xml)
 * support more capability & bus properties (in HAL)
 * generalize gather_capabilities to work for gathering bus & capability
info for both HAL & devkit drivers
 * figure out how devkit and HAL correlate, so we report device info
consistently
 * register for devkit events to keep device info up-to-date

Dave

P.S. I'm afraid the patch is rather large, but remember to skip the
generated files when looking it over:

 configure.in                        |   97 ++++++
 include/libvirt/libvirt.h           |   78 +++++
 include/libvirt/libvirt.h.in        |   78 +++++
 include/libvirt/virterror.h         |    4
 python/generator.py                 |   15 +
 python/libvir.c                     |  127 ++++++++
 python/libvirt-python-api.xml       |   17 +
 python/libvirt_wrap.h               |   10
 python/types.c                      |   15 +
 qemud/remote.c                      |  282 +++++++++++++++++++
 qemud/remote_dispatch_localvars.h   |   20 +
 qemud/remote_dispatch_proc_switch.h |   93 ++++++
 qemud/remote_dispatch_prototypes.h  |   11
 qemud/remote_protocol.c             |  220 +++++++++++++++
 qemud/remote_protocol.h             |  184 ++++++++++++
 qemud/remote_protocol.x             |  117 +++++++
 src/Makefile.am                     |   19 +
 src/driver.h                        |   67 ++++
 src/hash.c                          |  181 ++++++++++++
 src/internal.h                      |   52 +++
 src/libvirt.c                       |  528
+++++++++++++++++++++++++++++++++++\
-
 src/libvirt_sym.version             |   14
 src/node_device.c                   |  262 +++++++++++++++++
 src/node_device.h                   |   38 ++
 src/node_device_devkit.c            |  299 ++++++++++++++++++++
 src/node_device_hal.c               |  475
++++++++++++++++++++++++++++++++
 src/remote_internal.c               |  364 ++++++++++++++++++++++++
 src/virsh.c                         |   80 +++++
 src/virterror.c                     |   21 +
 29 files changed, 3757 insertions(+), 11 deletions(-)


On Fri, 2008-09-26 at 14:15 +0100, Daniel P. Berrange wrote:
> On Thu, Aug 21, 2008 at 03:18:57PM -0400, David Lively wrote:
> > Hi -
> > 
> >   I'm about to start working on host device enumeration, along the
> > (HAL-ish) lines of what was discussed back in April:
> >   https://www.redhat.com/archives/libvir-list/2008-April/msg00005.html
> > 
> >   I know the xml details haven't been fully fleshed out, but there seems
> > to be agreement that it will be a fairly direct mapping from (a subset
> > of the) HAL info to the details that we need in the xml.  Doubtless it
> > will take a while to figure out exactly what subset suffices (and, for
> > that matter, if everything needed is available via HAL ...), but I think
> > the work is well-defined for some of the obvious details (discussed in
> > the above thread) on which there's broad agreement.
> > 
> >   Is anyone working on such an implementation?
> 
> Did you ever start any work on this project ?  The oVirt guys really want
> this done asap, so if you've not started on it, or have a partial start
> to work from, I plan to make time to look at it next week
> 
> Regards,
> Daniel
diff --git a/configure.in b/configure.in
index 338aabf..143386a 100644
--- a/configure.in
+++ b/configure.in
@@ -1036,6 +1036,93 @@ test "$enable_shared" = no && lt_cv_objdir=.
 LV_LIBTOOL_OBJDIR=${lt_cv_objdir-.}
 AC_SUBST([LV_LIBTOOL_OBJDIR])
 
+dnl HAL or DeviceKit library for host device enumeration
+HAL_REQUIRED=0.0
+HAL_CFLAGS=
+HAL_LIBS=
+AC_ARG_WITH([hal],
+  [  --with-hal         use HAL for host device enumeration],
+  [],
+  [with_hal=check])
+
+if test "x$with_hal" = "xyes" -o "x$with_hal" = "xcheck"; then
+  PKG_CHECK_MODULES(HAL, hal >= $HAL_REQUIRED,
+    [with_hal=yes], [
+    if test "x$with_hal" = "xcheck" ; then
+       with_hal=no
+    else
+       AC_MSG_ERROR(
+         [You must install hal-devel >= $HAL_REQUIRED to compile libvirt])
+    fi
+  ])
+  if test "x$with_hal" = "xyes" ; then
+    AC_DEFINE_UNQUOTED([HAVE_HAL], 1,
+      [use HAL for host device enumeration])
+
+    old_CFLAGS=$CFLAGS
+    old_LDFLAGS=$LDFLAGS
+    CFLAGS="$CFLAGS $HAL_CFLAGS"
+    LDFLAGS="$LDFLAGS $HAL_LIBS"
+    AC_CHECK_FUNCS([libhal_get_all_devices],,[with_hal=no])
+    CFLAGS="$old_CFLAGS"
+    LDFLAGS="$old_LDFLAGS"
+  fi
+fi
+AM_CONDITIONAL([HAVE_HAL], [test "x$with_hal" = "xyes"])
+AC_SUBST([HAL_CFLAGS])
+AC_SUBST([HAL_LIBS])
+
+DEVKIT_REQUIRED=0.0
+DEVKIT_CFLAGS=
+DEVKIT_LIBS=
+AC_ARG_WITH([devkit],
+  [  --with-devkit      use DeviceKit for host device enumeration],
+  [],
+  [with_devkit=check])
+
+dnl Extra check needed while devkit pkg-config info missing glib2 dependency
+PKG_CHECK_MODULES(GLIB2, glib-2.0 >= 0.0,,[
+  if test "x$with_devkit" = "xcheck"; then
+    with_devkit=no
+  elif test "x$with_devkit" = "xyes"; then
+    AC_MSG_ERROR([required package DeviceKit requires package glib-2.0])
+  fi])
+
+if test "x$with_devkit" = "xyes" -o "x$with_devkit" = "xcheck"; then
+  PKG_CHECK_MODULES(DEVKIT, devkit-gobject >= $DEVKIT_REQUIRED,
+    [with_devkit=yes], [
+    if test "x$with_devkit" = "xcheck" ; then
+       with_devkit=no
+    else
+       AC_MSG_ERROR(
+         [You must install DeviceKit-devel >= $DEVKIT_REQUIRED to compile libvirt])
+    fi
+  ])
+  if test "x$with_devkit" = "xyes" ; then
+    AC_DEFINE_UNQUOTED([HAVE_DEVKIT], 1,
+      [use DeviceKit for host device enumeration])
+
+    dnl Add glib2 flags explicitly while devkit pkg-config info missing glib2 dependency
+    DEVKIT_CFLAGS="$GLIB2_CFLAGS $DEVKIT_CFLAGS"
+    DEVKIT_LIBS="$GLIB2_LIBS $DEVKIT_LIBS"
+
+    dnl Add more flags apparently required for devkit to work properly
+    DEVKIT_CFLAGS="$DEVKIT_CFLAGS -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT"
+
+    old_CFLAGS=$CFLAGS
+    old_LDFLAGS=$LDFLAGS
+    CFLAGS="$CFLAGS $DEVKIT_CFLAGS"
+    LDFLAGS="$LDFLAGS $DEVKIT_LIBS"
+    AC_CHECK_FUNCS([devkit_client_connect],,[with_devkit=no])
+    CFLAGS="$old_CFLAGS"
+    LDFLAGS="$old_LDFLAGS"
+  fi
+fi
+AM_CONDITIONAL([HAVE_DEVKIT], [test "x$with_devkit" = "xyes"])
+AC_SUBST([DEVKIT_CFLAGS])
+AC_SUBST([DEVKIT_LIBS])
+
+
 # very annoying
 rm -f COPYING
 cp COPYING.LIB COPYING
@@ -1110,6 +1197,16 @@ AC_MSG_NOTICE([  numactl: $NUMACTL_CFLAGS $NUMACTL_LIBS])
 else
 AC_MSG_NOTICE([  numactl: no])
 fi
+if test "$with_hal" = "yes" ; then
+AC_MSG_NOTICE([  hal: $HAL_CFLAGS $HAL_LIBS])
+else
+AC_MSG_NOTICE([  hal: no])
+fi
+if test "$with_devkit" = "yes" ; then
+AC_MSG_NOTICE([  devkit: $DEVKIT_CFLAGS $DEVKIT_LIBS])
+else
+AC_MSG_NOTICE([  devkit: no])
+fi
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Test suite])
 AC_MSG_NOTICE([])
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h
index d519452..2d50017 100644
--- a/include/libvirt/libvirt.h
+++ b/include/libvirt/libvirt.h
@@ -987,6 +987,84 @@ char *                  virStorageVolGetXMLDesc         (virStorageVolPtr pool,
 
 char *                  virStorageVolGetPath            (virStorageVolPtr vol);
 
+/*
+ * Host device enumeration
+ */
+
+/**
+ * virNodeDevice:
+ *
+ * A virNodeDevice contains a node (host) device details.
+ */
+
+typedef struct _virNodeDevice virNodeDevice;
+
+/**
+ * virNodeDevicePtr:
+ *
+ * A virNodeDevicePtr is a pointer to a virNodeDevice structure.  Get
+ * one via virNodeDeviceLookupByKey, virNodeDeviceLookupByName, or
+ * virNodeDeviceCreate.  Be sure to Call virNodeDeviceFree when done
+ * using a virNodeDevicePtr obtained from any of the above functions to
+ * avoid leaking memory.
+ */
+
+typedef virNodeDevice *virNodeDevicePtr;
+
+
+int                     virNodeNumOfDevices     (virConnectPtr conn);
+
+int                     virNodeListDevices      (virConnectPtr conn,
+                                                 char **const names,
+                                                 int maxnames);
+
+int                     virNodeNumOfDevicesByCap (virConnectPtr conn,
+                                                  const char *cap);
+
+int                     virNodeListDevicesByCap (virConnectPtr conn,
+                                                 const char *cap,
+                                                 char **const names,
+                                                 int maxnames);
+
+int                     virNodeNumOfDevicesByBus (virConnectPtr conn,
+                                                  const char *bus);
+
+int                     virNodeListDevicesByBus (virConnectPtr conn,
+                                                 const char *bus,
+                                                 char **const names,
+                                                 int maxnames);
+
+virNodeDevicePtr        virNodeDeviceLookupByName (virConnectPtr conn,
+                                                   const char *name);
+
+virNodeDevicePtr        virNodeDeviceLookupByKey (virConnectPtr conn,
+                                                  const char *key);
+
+const char *            virNodeDeviceGetKey      (virNodeDevicePtr dev);
+
+const char *            virNodeDeviceGetName     (virNodeDevicePtr dev);
+
+const char *            virNodeDeviceGetParentKey(virNodeDevicePtr dev);
+
+const char *            virNodeDeviceGetBus      (virNodeDevicePtr dev);
+
+int                     virNodeDeviceNumOfCaps   (virNodeDevicePtr dev);
+
+int                     virNodeDeviceListCaps    (virNodeDevicePtr dev,
+                                                  char **const names,
+                                                  int maxnames);
+
+char *                  virNodeDeviceGetXMLDesc (virNodeDevicePtr dev,
+                                                 unsigned int flags);
+
+virNodeDevicePtr        virNodeDeviceCreate     (virConnectPtr conn,
+                                                 const char *xml);
+
+int                     virNodeDeviceDestroy    (virNodeDevicePtr dev);
+
+int                     virNodeDeviceFree       (virNodeDevicePtr dev);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 24b5680..ea67936 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -987,6 +987,84 @@ char *                  virStorageVolGetXMLDesc         (virStorageVolPtr pool,
 
 char *                  virStorageVolGetPath            (virStorageVolPtr vol);
 
+/*
+ * Host device enumeration
+ */
+
+/**
+ * virNodeDevice:
+ *
+ * A virNodeDevice contains a node (host) device details.
+ */
+
+typedef struct _virNodeDevice virNodeDevice;
+
+/**
+ * virNodeDevicePtr:
+ *
+ * A virNodeDevicePtr is a pointer to a virNodeDevice structure.  Get
+ * one via virNodeDeviceLookupByKey, virNodeDeviceLookupByName, or
+ * virNodeDeviceCreate.  Be sure to Call virNodeDeviceFree when done
+ * using a virNodeDevicePtr obtained from any of the above functions to
+ * avoid leaking memory.
+ */
+
+typedef virNodeDevice *virNodeDevicePtr;
+
+
+int                     virNodeNumOfDevices     (virConnectPtr conn);
+
+int                     virNodeListDevices      (virConnectPtr conn,
+                                                 char **const names,
+                                                 int maxnames);
+
+int                     virNodeNumOfDevicesByCap (virConnectPtr conn,
+                                                  const char *cap);
+
+int                     virNodeListDevicesByCap (virConnectPtr conn,
+                                                 const char *cap,
+                                                 char **const names,
+                                                 int maxnames);
+
+int                     virNodeNumOfDevicesByBus (virConnectPtr conn,
+                                                  const char *bus);
+
+int                     virNodeListDevicesByBus (virConnectPtr conn,
+                                                 const char *bus,
+                                                 char **const names,
+                                                 int maxnames);
+
+virNodeDevicePtr        virNodeDeviceLookupByName (virConnectPtr conn,
+                                                   const char *name);
+
+virNodeDevicePtr        virNodeDeviceLookupByKey (virConnectPtr conn,
+                                                  const char *key);
+
+const char *            virNodeDeviceGetKey      (virNodeDevicePtr dev);
+
+const char *            virNodeDeviceGetName     (virNodeDevicePtr dev);
+
+const char *            virNodeDeviceGetParentKey(virNodeDevicePtr dev);
+
+const char *            virNodeDeviceGetBus      (virNodeDevicePtr dev);
+
+int                     virNodeDeviceNumOfCaps   (virNodeDevicePtr dev);
+
+int                     virNodeDeviceListCaps    (virNodeDevicePtr dev,
+                                                  char **const names,
+                                                  int maxnames);
+
+char *                  virNodeDeviceGetXMLDesc (virNodeDevicePtr dev,
+                                                 unsigned int flags);
+
+virNodeDevicePtr        virNodeDeviceCreate     (virConnectPtr conn,
+                                                 const char *xml);
+
+int                     virNodeDeviceDestroy    (virNodeDevicePtr dev);
+
+int                     virNodeDeviceFree       (virNodeDevicePtr dev);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 28a9746..408b1c2 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -58,6 +58,7 @@ typedef enum {
     VIR_FROM_STORAGE,   /* Error from storage driver */
     VIR_FROM_NETWORK,   /* Error from network config */
     VIR_FROM_DOMAIN,    /* Error from domain config */
+    VIR_FROM_NODE,      /* Error from node driver */
 } virErrorDomain;
 
 
@@ -146,6 +147,9 @@ typedef enum {
     VIR_WAR_NO_STORAGE, /* failed to start storage */
     VIR_ERR_NO_STORAGE_POOL, /* storage pool not found */
     VIR_ERR_NO_STORAGE_VOL, /* storage pool not found */
+    VIR_WAR_NO_NODE, /* failed to start node driver */
+    VIR_ERR_INVALID_NODE_DEVICE,/* invalid node device object */
+    VIR_ERR_NO_NODE_DEVICE,/* node device not found */
 } virErrorNumber;
 
 /**
diff --git a/python/generator.py b/python/generator.py
index c706b19..8e2ac6b 100755
--- a/python/generator.py
+++ b/python/generator.py
@@ -258,6 +258,11 @@ py_types = {
     'const virConnectPtr':  ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
     'virConnect *':  ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
     'const virConnect *':  ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
+
+    'virNodeDevicePtr':  ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
+    'const virNodeDevicePtr':  ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
+    'virNodeDevice *':  ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
+    'const virNodeDevice *':  ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
 }
 
 py_return_types = {
@@ -315,6 +320,9 @@ skip_impl = (
     'virStoragePoolListVolumes',
     'virDomainBlockPeek',
     'virDomainMemoryPeek',
+    'virNodeListDevicesByCap',
+    'virNodeListDevices',
+    'virNodeListDevicesByBus',
 )
 
 
@@ -332,6 +340,12 @@ skip_function = (
     'virCopyLastError', # Python API is called virGetLastError instead
     'virConnectOpenAuth', # Python C code is manually written
     'virDefaultErrorFunc', # Python virErrorFuncHandler impl calls this from C
+    'virNodeDeviceGetKey',
+    'virNodeDeviceGetName',
+    'virNodeDeviceGetParentKey',
+    'virNodeDeviceGetBus',
+    'virNodeDeviceNumOfCaps',
+    'virNodeDeviceListCaps',
 )
 
 
@@ -613,6 +627,7 @@ classes_destructors = {
     "virStoragePool": "virStoragePoolFree",
     "virStorageVol": "virStorageVolFree",
     "virConnect": "virConnectClose",
+    "virNodeDevice" : "virNodeDeviceFree"
 }
 
 functions_noexcept = {
diff --git a/python/libvir.c b/python/libvir.c
index 9cc0c81..3b248e6 100644
--- a/python/libvir.c
+++ b/python/libvir.c
@@ -1466,6 +1466,130 @@ libvirt_virStoragePoolLookupByUUID(PyObject *self ATTRIBUTE_UNUSED, PyObject *ar
     return(py_retval);
 }
 
+static PyObject *
+libvirt_virNodeListDevices(PyObject *self ATTRIBUTE_UNUSED,
+                           PyObject *args) {
+    PyObject *py_retval;
+    char **names = NULL;
+    int c_retval, i;
+    virConnectPtr conn;
+    PyObject *pyobj_conn;
+
+
+    if (!PyArg_ParseTuple(args, (char *)"O:virNodeListDevices", &pyobj_conn))
+        return(NULL);
+    conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn);
+
+    c_retval = virNodeNumOfDevices(conn);
+    if (c_retval < 0)
+        return VIR_PY_NONE;
+
+    if (c_retval) {
+        names = malloc(sizeof(*names) * c_retval);
+        if (!names)
+            return VIR_PY_NONE;
+        c_retval = virNodeListDevices(conn, names, c_retval);
+        if (c_retval < 0) {
+            free(names);
+            return VIR_PY_NONE;
+        }
+    }
+    py_retval = PyList_New(c_retval);
+
+    if (names) {
+        for (i = 0;i < c_retval;i++) {
+            PyList_SetItem(py_retval, i, libvirt_constcharPtrWrap(names[i]));
+            free(names[i]);
+        }
+        free(names);
+    }
+
+    return(py_retval);
+}
+
+static PyObject *
+libvirt_virNodeListDevicesByCap(PyObject *self ATTRIBUTE_UNUSED,
+                                PyObject *args) {
+    PyObject *py_retval;
+    char **names = NULL;
+    int c_retval, i;
+    virConnectPtr conn;
+    PyObject *pyobj_conn;
+    char *cap;
+
+
+    if (!PyArg_ParseTuple(args, (char *)"Oz:virNodeListDevicesByCap", &pyobj_conn, &cap))
+        return(NULL);
+    conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn);
+
+    c_retval = virNodeNumOfDevicesByCap(conn, cap);
+    if (c_retval < 0)
+        return VIR_PY_NONE;
+
+    if (c_retval) {
+        names = malloc(sizeof(*names) * c_retval);
+        if (!names)
+            return VIR_PY_NONE;
+        c_retval = virNodeListDevicesByCap(conn, cap, names, c_retval);
+        if (c_retval < 0) {
+            free(names);
+            return VIR_PY_NONE;
+        }
+    }
+    py_retval = PyList_New(c_retval);
+
+    if (names) {
+        for (i = 0;i < c_retval;i++) {
+            PyList_SetItem(py_retval, i, libvirt_constcharPtrWrap(names[i]));
+            free(names[i]);
+        }
+        free(names);
+    }
+
+    return(py_retval);
+}
+
+static PyObject *
+libvirt_virNodeListDevicesByBus(PyObject *self ATTRIBUTE_UNUSED,
+                                PyObject *args) {
+    PyObject *py_retval;
+    char **names = NULL;
+    int c_retval, i;
+    virConnectPtr conn;
+    PyObject *pyobj_conn;
+    char *bus;
+
+
+    if (!PyArg_ParseTuple(args, (char *)"Oz:virNodeListDevicesByBus", &pyobj_conn, &bus))
+        return(NULL);
+    conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn);
+
+    c_retval = virNodeNumOfDevicesByBus(conn, bus);
+    if (c_retval < 0)
+        return VIR_PY_NONE;
+
+    if (c_retval) {
+        names = malloc(sizeof(*names) * c_retval);
+        if (!names)
+            return VIR_PY_NONE;
+        c_retval = virNodeListDevicesByBus(conn, bus, names, c_retval);
+        if (c_retval < 0) {
+            free(names);
+            return VIR_PY_NONE;
+        }
+    }
+    py_retval = PyList_New(c_retval);
+
+    if (names) {
+        for (i = 0;i < c_retval;i++) {
+            PyList_SetItem(py_retval, i, libvirt_constcharPtrWrap(names[i]));
+            free(names[i]);
+        }
+        free(names);
+    }
+
+    return(py_retval);
+}
 
 
 /************************************************************************
@@ -1511,6 +1635,9 @@ static PyMethodDef libvirtMethods[] = {
     {(char *) "virStoragePoolGetUUID", libvirt_virStoragePoolGetUUID, METH_VARARGS, NULL},
     {(char *) "virStoragePoolGetUUIDString", libvirt_virStoragePoolGetUUIDString, METH_VARARGS, NULL},
     {(char *) "virStoragePoolLookupByUUID", libvirt_virStoragePoolLookupByUUID, METH_VARARGS, NULL},
+    {(char *) "virNodeListDevices", libvirt_virNodeListDevices, METH_VARARGS, NULL},
+    {(char *) "virNodeListDevicesByBus", libvirt_virNodeListDevicesByBus, METH_VARARGS, NULL},
+    {(char *) "virNodeListDevicesByCap", libvirt_virNodeListDevicesByCap, METH_VARARGS, NULL},
     {NULL, NULL, 0, NULL}
 };
 
diff --git a/python/libvirt-python-api.xml b/python/libvirt-python-api.xml
index f3b82fc..2227e43 100644
--- a/python/libvirt-python-api.xml
+++ b/python/libvirt-python-api.xml
@@ -160,5 +160,22 @@
       <return type='int *' info='the list of information or None in case of error'/>
       <arg name='vol' type='virStorageVolPtr' info='a storage vol object'/>
     </function>
+    <function name='virNodeListDevices' file='python'>
+      <info>list the node devices, stores the pointers to the names in @names</info>
+      <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/>
+      <return type='str *' info='the list of Names or None in case of error'/>
+    </function>
+    <function name='virNodeListDevicesByBus' file='python'>
+      <info>list the node devices on a bus, stores the pointers to the names in @names</info>
+      <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/>
+      <arg name='bus' type='const unsigned char *' info='bus name'/>
+      <return type='str *' info='the list of Names or None in case of error'/>
+    </function>
+    <function name='virNodeListDevicesByCap' file='python'>
+      <info>list the node devices with a given capability, stores the pointers to the names in @names</info>
+      <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/>
+      <arg name='cap' type='const unsigned char *' info='capability name'/>
+      <return type='str *' info='the list of Names or None in case of error'/>
+    </function>
   </symbols>
 </api>
diff --git a/python/libvirt_wrap.h b/python/libvirt_wrap.h
index f8ecb76..802bf36 100644
--- a/python/libvirt_wrap.h
+++ b/python/libvirt_wrap.h
@@ -65,6 +65,15 @@ typedef struct {
     virStorageVolPtr obj;
 } PyvirStorageVol_Object;
 
+#define PyvirNodeDevice_Get(v) (((v) == Py_None) ? NULL : \
+        (((PyvirNodeDevice_Object *)(v))->obj))
+
+typedef struct {
+    PyObject_HEAD
+    virNodeDevicePtr obj;
+} PyvirNodeDevice_Object;
+
+
 
 PyObject * libvirt_intWrap(int val);
 PyObject * libvirt_longWrap(long val);
@@ -78,6 +87,7 @@ PyObject * libvirt_virDomainPtrWrap(virDomainPtr node);
 PyObject * libvirt_virNetworkPtrWrap(virNetworkPtr node);
 PyObject * libvirt_virStoragePoolPtrWrap(virStoragePoolPtr node);
 PyObject * libvirt_virStorageVolPtrWrap(virStorageVolPtr node);
+PyObject * libvirt_virNodeDevicePtrWrap(virNodeDevicePtr node);
 
 
 /* Provide simple macro statement wrappers (adapted from GLib, in turn from Perl):
diff --git a/python/types.c b/python/types.c
index 8e08d25..aa0555b 100644
--- a/python/types.c
+++ b/python/types.c
@@ -162,3 +162,18 @@ libvirt_virConnectPtrWrap(virConnectPtr node)
                                      NULL);
     return (ret);
 }
+
+PyObject *
+libvirt_virNodeDevicePtrWrap(virNodeDevicePtr node)
+{
+    PyObject *ret;
+
+    if (node == NULL) {
+        Py_INCREF(Py_None);
+        return (Py_None);
+    }
+    ret =
+        PyCObject_FromVoidPtrAndDesc((void *) node, (char *) "virNodeDevicePtr",
+                                     NULL);
+    return (ret);
+}
diff --git a/qemud/remote.c b/qemud/remote.c
index 3e43dcf..92c8817 100644
--- a/qemud/remote.c
+++ b/qemud/remote.c
@@ -68,6 +68,7 @@ static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr do
 static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src);
 static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src);
 static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src);
+static void make_nonnull_node_device (remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src);
 
 #include "remote_dispatch_prototypes.h"
 
@@ -3626,6 +3627,265 @@ remoteDispatchStorageVolLookupByPath (struct qemud_server *server ATTRIBUTE_UNUS
 }
 
 
+/***************************************************************
+ *     NODE INFO APIS
+ **************************************************************/
+
+static int
+remoteDispatchNodeNumOfDevices (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                struct qemud_client *client,
+                                remote_message_header *req,
+                                void *args ATTRIBUTE_UNUSED,
+                                remote_node_num_of_devices_ret *ret)
+{
+    CHECK_CONN(client);
+
+    ret->num = virNodeNumOfDevices (client->conn);
+    if (ret->num == -1) return -1;
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeListDevices (struct qemud_server *server ATTRIBUTE_UNUSED,
+                               struct qemud_client *client,
+                               remote_message_header *req,
+                               remote_node_list_devices_args *args,
+                               remote_node_list_devices_ret *ret)
+{
+    CHECK_CONN(client);
+
+    if (args->maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        remoteDispatchError (client, req,
+                             "%s", _("maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX"));
+        return -2;
+    }
+
+    /* Allocate return buffer. */
+    if (VIR_ALLOC_N(ret->names.names_val, args->maxnames) < 0) {
+        remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, NULL);
+        return -2;
+    }
+
+    ret->names.names_len =
+        virNodeListDevices (client->conn,
+                            ret->names.names_val, args->maxnames);
+    if (ret->names.names_len == -1) {
+        VIR_FREE(ret->names.names_val);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeNumOfDevicesByBus (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                     struct qemud_client *client,
+                                     remote_message_header *req,
+                                     remote_node_num_of_devices_by_bus_args *args,
+                                     remote_node_num_of_devices_by_bus_ret *ret)
+{
+    CHECK_CONN(client);
+
+    ret->num = virNodeNumOfDevicesByBus (client->conn, args->bus);
+    if (ret->num == -1) return -1;
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeListDevicesByBus (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                    struct qemud_client *client,
+                                    remote_message_header *req,
+                                    remote_node_list_devices_by_bus_args *args,
+                                    remote_node_list_devices_by_bus_ret *ret)
+{
+    CHECK_CONN(client);
+
+    if (args->maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        remoteDispatchError (client, req,
+                             "%s", _("maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX"));
+        return -2;
+    }
+
+    /* Allocate return buffer. */
+    if (VIR_ALLOC_N(ret->names.names_val, args->maxnames) < 0) {
+        remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, NULL);
+        return -2;
+    }
+
+    ret->names.names_len =
+        virNodeListDevicesByBus (client->conn, args->bus,
+                                 ret->names.names_val, args->maxnames);
+    if (ret->names.names_len == -1) {
+        VIR_FREE(ret->names.names_val);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeNumOfDevicesByCap (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                     struct qemud_client *client,
+                                     remote_message_header *req,
+                                     remote_node_num_of_devices_by_cap_args *args,
+                                     remote_node_num_of_devices_by_cap_ret *ret)
+{
+    CHECK_CONN(client);
+
+    ret->num = virNodeNumOfDevicesByCap (client->conn, args->cap);
+    if (ret->num == -1) return -1;
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeListDevicesByCap (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                    struct qemud_client *client,
+                                    remote_message_header *req,
+                                    remote_node_list_devices_by_cap_args *args,
+                                    remote_node_list_devices_by_cap_ret *ret)
+{
+    CHECK_CONN(client);
+
+    if (args->maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        remoteDispatchError (client, req,
+                             "%s", _("maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX"));
+        return -2;
+    }
+
+    /* Allocate return buffer. */
+    if (VIR_ALLOC_N(ret->names.names_val, args->maxnames) < 0) {
+        remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, NULL);
+        return -2;
+    }
+
+    ret->names.names_len =
+        virNodeListDevicesByCap (client->conn, args->cap,
+                                 ret->names.names_val, args->maxnames);
+    if (ret->names.names_len == -1) {
+        VIR_FREE(ret->names.names_val);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceLookupByKey (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                     struct qemud_client *client,
+                                     remote_message_header *req,
+                                     remote_node_device_lookup_by_key_args *args,
+                                     remote_node_device_lookup_by_key_ret *ret)
+{
+    virNodeDevicePtr dev;
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceLookupByKey (client->conn, args->key);
+    if (dev == NULL) return -1;
+
+    make_nonnull_node_device (&ret->dev, dev);
+    virNodeDeviceFree(dev);
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                      struct qemud_client *client,
+                                      remote_message_header *req,
+                                      remote_node_device_lookup_by_name_args *args,
+                                      remote_node_device_lookup_by_name_ret *ret)
+{
+    virNodeDevicePtr dev;
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceLookupByName (client->conn, args->name);
+    if (dev == NULL) return -1;
+
+    make_nonnull_node_device (&ret->dev, dev);
+    virNodeDeviceFree(dev);
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                 struct qemud_client *client,
+                                 remote_message_header *req,
+                                 remote_node_device_dump_xml_args *args,
+                                 remote_node_device_dump_xml_ret *ret)
+{
+    virNodeDevicePtr dev;
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceLookupByKey(client->conn, args->key);
+    if (dev == NULL) {
+        remoteDispatchError (client, req, "%s", _("node_device not found"));
+        return -2;
+    }
+
+    /* remoteDispatchClientRequest will free this. */
+    ret->xml = virNodeDeviceGetXMLDesc (dev, args->flags);
+    if (!ret->xml) {
+        virNodeDeviceFree(dev);
+        return -1;
+    }
+    virNodeDeviceFree(dev);
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceCreate (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                struct qemud_client *client,
+                                remote_message_header *req,
+                                remote_node_device_create_args *args,
+                                remote_node_device_create_ret *ret)
+{
+    virNodeDevicePtr dev;
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceCreate (client->conn, args->xml);
+    if (dev == NULL) return -1;
+
+    make_nonnull_node_device (&ret->dev, dev);
+    virNodeDeviceFree(dev);
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceDestroy (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                struct qemud_client *client,
+                                remote_message_header *req,
+                                remote_node_device_destroy_args *args,
+                                void *ret ATTRIBUTE_UNUSED)
+{
+    virNodeDevicePtr dev;
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceLookupByKey(client->conn, args->key);
+    if (dev == NULL) {
+        remoteDispatchError (client, req, "%s", _("node_device not found"));
+        return -2;
+    }
+
+    if (virNodeDeviceDestroy (dev) < 0) {
+        virNodeDeviceFree(dev);
+        return -1;
+    }
+    virNodeDeviceFree(dev);
+    return 0;
+}
+
 /*----- Helpers. -----*/
 
 /* get_nonnull_domain and get_nonnull_network turn an on-wire
@@ -3696,3 +3956,25 @@ make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr
     vol_dst->name = strdup (vol_src->name);
     vol_dst->key = strdup (vol_src->key);
 }
+
+static void
+make_nonnull_node_device (remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src)
+{
+    int ncaps, i;
+
+    dev_dst->key = strdup(dev_src->key);
+    dev_dst->name = strdup(dev_src->name);
+    dev_dst->parent_key = dev_src->parent_key ? strdup(dev_src->parent_key) : strdup("");
+    dev_dst->bus_name = dev_src->bus_name ? strdup(dev_src->bus_name) : strdup("");
+    ncaps = 0;
+    while (dev_src->cap_names && dev_src->cap_names[ncaps])
+        ++ncaps;
+    if (ncaps > 0) {
+        if (VIR_ALLOC_N(dev_dst->cap_names.cap_names_val, ncaps) < 0)
+            // TODO: complain
+            return;
+        for (i = 0; i < ncaps; i++)
+            dev_dst->cap_names.cap_names_val[i] = strdup(dev_src->cap_names[i]);
+    }
+    dev_dst->cap_names.cap_names_len = ncaps;
+}
diff --git a/qemud/remote_dispatch_localvars.h b/qemud/remote_dispatch_localvars.h
index 18d71e9..1dcd356 100644
--- a/qemud/remote_dispatch_localvars.h
+++ b/qemud/remote_dispatch_localvars.h
@@ -23,6 +23,8 @@ remote_get_hostname_ret lv_remote_get_hostname_ret;
 remote_network_undefine_args lv_remote_network_undefine_args;
 remote_domain_create_args lv_remote_domain_create_args;
 remote_network_destroy_args lv_remote_network_destroy_args;
+remote_node_num_of_devices_by_bus_args lv_remote_node_num_of_devices_by_bus_args;
+remote_node_num_of_devices_by_bus_ret lv_remote_node_num_of_devices_by_bus_ret;
 remote_storage_vol_lookup_by_key_args lv_remote_storage_vol_lookup_by_key_args;
 remote_storage_vol_lookup_by_key_ret lv_remote_storage_vol_lookup_by_key_ret;
 remote_storage_pool_lookup_by_uuid_args lv_remote_storage_pool_lookup_by_uuid_args;
@@ -33,6 +35,8 @@ remote_list_defined_networks_ret lv_remote_list_defined_networks_ret;
 remote_network_create_xml_args lv_remote_network_create_xml_args;
 remote_network_create_xml_ret lv_remote_network_create_xml_ret;
 remote_open_args lv_remote_open_args;
+remote_node_device_create_args lv_remote_node_device_create_args;
+remote_node_device_create_ret lv_remote_node_device_create_ret;
 remote_storage_pool_refresh_args lv_remote_storage_pool_refresh_args;
 remote_storage_vol_lookup_by_path_args lv_remote_storage_vol_lookup_by_path_args;
 remote_storage_vol_lookup_by_path_ret lv_remote_storage_vol_lookup_by_path_ret;
@@ -40,6 +44,8 @@ remote_list_domains_args lv_remote_list_domains_args;
 remote_list_domains_ret lv_remote_list_domains_ret;
 remote_network_define_xml_args lv_remote_network_define_xml_args;
 remote_network_define_xml_ret lv_remote_network_define_xml_ret;
+remote_node_list_devices_by_cap_args lv_remote_node_list_devices_by_cap_args;
+remote_node_list_devices_by_cap_ret lv_remote_node_list_devices_by_cap_ret;
 remote_get_type_ret lv_remote_get_type_ret;
 remote_domain_block_peek_args lv_remote_domain_block_peek_args;
 remote_domain_block_peek_ret lv_remote_domain_block_peek_ret;
@@ -80,8 +86,11 @@ remote_domain_resume_args lv_remote_domain_resume_args;
 remote_network_get_bridge_name_args lv_remote_network_get_bridge_name_args;
 remote_network_get_bridge_name_ret lv_remote_network_get_bridge_name_ret;
 remote_domain_destroy_args lv_remote_domain_destroy_args;
+remote_node_num_of_devices_by_cap_args lv_remote_node_num_of_devices_by_cap_args;
+remote_node_num_of_devices_by_cap_ret lv_remote_node_num_of_devices_by_cap_ret;
 remote_find_storage_pool_sources_args lv_remote_find_storage_pool_sources_args;
 remote_find_storage_pool_sources_ret lv_remote_find_storage_pool_sources_ret;
+remote_node_num_of_devices_ret lv_remote_node_num_of_devices_ret;
 remote_auth_sasl_step_args lv_remote_auth_sasl_step_args;
 remote_auth_sasl_step_ret lv_remote_auth_sasl_step_ret;
 remote_domain_migrate_finish_args lv_remote_domain_migrate_finish_args;
@@ -91,11 +100,17 @@ remote_domain_get_vcpus_ret lv_remote_domain_get_vcpus_ret;
 remote_domain_get_scheduler_parameters_args lv_remote_domain_get_scheduler_parameters_args;
 remote_domain_get_scheduler_parameters_ret lv_remote_domain_get_scheduler_parameters_ret;
 remote_node_get_info_ret lv_remote_node_get_info_ret;
+remote_node_device_dump_xml_args lv_remote_node_device_dump_xml_args;
+remote_node_device_dump_xml_ret lv_remote_node_device_dump_xml_ret;
+remote_node_device_lookup_by_name_args lv_remote_node_device_lookup_by_name_args;
+remote_node_device_lookup_by_name_ret lv_remote_node_device_lookup_by_name_ret;
 remote_network_lookup_by_name_args lv_remote_network_lookup_by_name_args;
 remote_network_lookup_by_name_ret lv_remote_network_lookup_by_name_ret;
 remote_domain_memory_peek_args lv_remote_domain_memory_peek_args;
 remote_domain_memory_peek_ret lv_remote_domain_memory_peek_ret;
 remote_num_of_defined_domains_ret lv_remote_num_of_defined_domains_ret;
+remote_node_list_devices_args lv_remote_node_list_devices_args;
+remote_node_list_devices_ret lv_remote_node_list_devices_ret;
 remote_domain_block_stats_args lv_remote_domain_block_stats_args;
 remote_domain_block_stats_ret lv_remote_domain_block_stats_ret;
 remote_domain_detach_device_args lv_remote_domain_detach_device_args;
@@ -121,6 +136,8 @@ remote_num_of_defined_storage_pools_ret lv_remote_num_of_defined_storage_pools_r
 remote_domain_core_dump_args lv_remote_domain_core_dump_args;
 remote_list_defined_storage_pools_args lv_remote_list_defined_storage_pools_args;
 remote_list_defined_storage_pools_ret lv_remote_list_defined_storage_pools_ret;
+remote_node_list_devices_by_bus_args lv_remote_node_list_devices_by_bus_args;
+remote_node_list_devices_by_bus_ret lv_remote_node_list_devices_by_bus_ret;
 remote_domain_get_max_memory_args lv_remote_domain_get_max_memory_args;
 remote_domain_get_max_memory_ret lv_remote_domain_get_max_memory_ret;
 remote_num_of_domains_ret lv_remote_num_of_domains_ret;
@@ -136,6 +153,8 @@ remote_storage_vol_get_path_ret lv_remote_storage_vol_get_path_ret;
 remote_domain_lookup_by_id_args lv_remote_domain_lookup_by_id_args;
 remote_domain_lookup_by_id_ret lv_remote_domain_lookup_by_id_ret;
 remote_domain_attach_device_args lv_remote_domain_attach_device_args;
+remote_node_device_lookup_by_key_args lv_remote_node_device_lookup_by_key_args;
+remote_node_device_lookup_by_key_ret lv_remote_node_device_lookup_by_key_ret;
 remote_num_of_networks_ret lv_remote_num_of_networks_ret;
 remote_storage_pool_get_info_args lv_remote_storage_pool_get_info_args;
 remote_storage_pool_get_info_ret lv_remote_storage_pool_get_info_ret;
@@ -158,6 +177,7 @@ remote_storage_vol_dump_xml_args lv_remote_storage_vol_dump_xml_args;
 remote_storage_vol_dump_xml_ret lv_remote_storage_vol_dump_xml_ret;
 remote_domain_dump_xml_args lv_remote_domain_dump_xml_args;
 remote_domain_dump_xml_ret lv_remote_domain_dump_xml_ret;
+remote_node_device_destroy_args lv_remote_node_device_destroy_args;
 remote_get_max_vcpus_args lv_remote_get_max_vcpus_args;
 remote_get_max_vcpus_ret lv_remote_get_max_vcpus_ret;
 remote_domain_migrate_perform_args lv_remote_domain_migrate_perform_args;
diff --git a/qemud/remote_dispatch_proc_switch.h b/qemud/remote_dispatch_proc_switch.h
index 767b142..77ec9ff 100644
--- a/qemud/remote_dispatch_proc_switch.h
+++ b/qemud/remote_dispatch_proc_switch.h
@@ -518,6 +518,48 @@ case REMOTE_PROC_NETWORK_UNDEFINE:
         args = (char *) &lv_remote_network_undefine_args;
         memset (&lv_remote_network_undefine_args, 0, sizeof lv_remote_network_undefine_args);
         break;
+case REMOTE_PROC_NODE_DEVICE_CREATE:
+        fn = (dispatch_fn) remoteDispatchNodeDeviceCreate;
+        args_filter = (xdrproc_t) xdr_remote_node_device_create_args;
+        args = (char *) &lv_remote_node_device_create_args;
+        memset (&lv_remote_node_device_create_args, 0, sizeof lv_remote_node_device_create_args);
+        ret_filter = (xdrproc_t) xdr_remote_node_device_create_ret;
+        ret = (char *) &lv_remote_node_device_create_ret;
+        memset (&lv_remote_node_device_create_ret, 0, sizeof lv_remote_node_device_create_ret);
+        break;
+case REMOTE_PROC_NODE_DEVICE_DESTROY:
+        fn = (dispatch_fn) remoteDispatchNodeDeviceDestroy;
+        args_filter = (xdrproc_t) xdr_remote_node_device_destroy_args;
+        args = (char *) &lv_remote_node_device_destroy_args;
+        memset (&lv_remote_node_device_destroy_args, 0, sizeof lv_remote_node_device_destroy_args);
+        break;
+case REMOTE_PROC_NODE_DEVICE_DUMP_XML:
+        fn = (dispatch_fn) remoteDispatchNodeDeviceDumpXml;
+        args_filter = (xdrproc_t) xdr_remote_node_device_dump_xml_args;
+        args = (char *) &lv_remote_node_device_dump_xml_args;
+        memset (&lv_remote_node_device_dump_xml_args, 0, sizeof lv_remote_node_device_dump_xml_args);
+        ret_filter = (xdrproc_t) xdr_remote_node_device_dump_xml_ret;
+        ret = (char *) &lv_remote_node_device_dump_xml_ret;
+        memset (&lv_remote_node_device_dump_xml_ret, 0, sizeof lv_remote_node_device_dump_xml_ret);
+        break;
+case REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_KEY:
+        fn = (dispatch_fn) remoteDispatchNodeDeviceLookupByKey;
+        args_filter = (xdrproc_t) xdr_remote_node_device_lookup_by_key_args;
+        args = (char *) &lv_remote_node_device_lookup_by_key_args;
+        memset (&lv_remote_node_device_lookup_by_key_args, 0, sizeof lv_remote_node_device_lookup_by_key_args);
+        ret_filter = (xdrproc_t) xdr_remote_node_device_lookup_by_key_ret;
+        ret = (char *) &lv_remote_node_device_lookup_by_key_ret;
+        memset (&lv_remote_node_device_lookup_by_key_ret, 0, sizeof lv_remote_node_device_lookup_by_key_ret);
+        break;
+case REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_NAME:
+        fn = (dispatch_fn) remoteDispatchNodeDeviceLookupByName;
+        args_filter = (xdrproc_t) xdr_remote_node_device_lookup_by_name_args;
+        args = (char *) &lv_remote_node_device_lookup_by_name_args;
+        memset (&lv_remote_node_device_lookup_by_name_args, 0, sizeof lv_remote_node_device_lookup_by_name_args);
+        ret_filter = (xdrproc_t) xdr_remote_node_device_lookup_by_name_ret;
+        ret = (char *) &lv_remote_node_device_lookup_by_name_ret;
+        memset (&lv_remote_node_device_lookup_by_name_ret, 0, sizeof lv_remote_node_device_lookup_by_name_ret);
+        break;
 case REMOTE_PROC_NODE_GET_CELLS_FREE_MEMORY:
         fn = (dispatch_fn) remoteDispatchNodeGetCellsFreeMemory;
         args_filter = (xdrproc_t) xdr_remote_node_get_cells_free_memory_args;
@@ -539,6 +581,57 @@ case REMOTE_PROC_NODE_GET_INFO:
         ret = (char *) &lv_remote_node_get_info_ret;
         memset (&lv_remote_node_get_info_ret, 0, sizeof lv_remote_node_get_info_ret);
         break;
+case REMOTE_PROC_NODE_LIST_DEVICES:
+        fn = (dispatch_fn) remoteDispatchNodeListDevices;
+        args_filter = (xdrproc_t) xdr_remote_node_list_devices_args;
+        args = (char *) &lv_remote_node_list_devices_args;
+        memset (&lv_remote_node_list_devices_args, 0, sizeof lv_remote_node_list_devices_args);
+        ret_filter = (xdrproc_t) xdr_remote_node_list_devices_ret;
+        ret = (char *) &lv_remote_node_list_devices_ret;
+        memset (&lv_remote_node_list_devices_ret, 0, sizeof lv_remote_node_list_devices_ret);
+        break;
+case REMOTE_PROC_NODE_LIST_DEVICES_BY_BUS:
+        fn = (dispatch_fn) remoteDispatchNodeListDevicesByBus;
+        args_filter = (xdrproc_t) xdr_remote_node_list_devices_by_bus_args;
+        args = (char *) &lv_remote_node_list_devices_by_bus_args;
+        memset (&lv_remote_node_list_devices_by_bus_args, 0, sizeof lv_remote_node_list_devices_by_bus_args);
+        ret_filter = (xdrproc_t) xdr_remote_node_list_devices_by_bus_ret;
+        ret = (char *) &lv_remote_node_list_devices_by_bus_ret;
+        memset (&lv_remote_node_list_devices_by_bus_ret, 0, sizeof lv_remote_node_list_devices_by_bus_ret);
+        break;
+case REMOTE_PROC_NODE_LIST_DEVICES_BY_CAP:
+        fn = (dispatch_fn) remoteDispatchNodeListDevicesByCap;
+        args_filter = (xdrproc_t) xdr_remote_node_list_devices_by_cap_args;
+        args = (char *) &lv_remote_node_list_devices_by_cap_args;
+        memset (&lv_remote_node_list_devices_by_cap_args, 0, sizeof lv_remote_node_list_devices_by_cap_args);
+        ret_filter = (xdrproc_t) xdr_remote_node_list_devices_by_cap_ret;
+        ret = (char *) &lv_remote_node_list_devices_by_cap_ret;
+        memset (&lv_remote_node_list_devices_by_cap_ret, 0, sizeof lv_remote_node_list_devices_by_cap_ret);
+        break;
+case REMOTE_PROC_NODE_NUM_OF_DEVICES:
+        fn = (dispatch_fn) remoteDispatchNodeNumOfDevices;
+        ret_filter = (xdrproc_t) xdr_remote_node_num_of_devices_ret;
+        ret = (char *) &lv_remote_node_num_of_devices_ret;
+        memset (&lv_remote_node_num_of_devices_ret, 0, sizeof lv_remote_node_num_of_devices_ret);
+        break;
+case REMOTE_PROC_NODE_NUM_OF_DEVICES_BY_BUS:
+        fn = (dispatch_fn) remoteDispatchNodeNumOfDevicesByBus;
+        args_filter = (xdrproc_t) xdr_remote_node_num_of_devices_by_bus_args;
+        args = (char *) &lv_remote_node_num_of_devices_by_bus_args;
+        memset (&lv_remote_node_num_of_devices_by_bus_args, 0, sizeof lv_remote_node_num_of_devices_by_bus_args);
+        ret_filter = (xdrproc_t) xdr_remote_node_num_of_devices_by_bus_ret;
+        ret = (char *) &lv_remote_node_num_of_devices_by_bus_ret;
+        memset (&lv_remote_node_num_of_devices_by_bus_ret, 0, sizeof lv_remote_node_num_of_devices_by_bus_ret);
+        break;
+case REMOTE_PROC_NODE_NUM_OF_DEVICES_BY_CAP:
+        fn = (dispatch_fn) remoteDispatchNodeNumOfDevicesByCap;
+        args_filter = (xdrproc_t) xdr_remote_node_num_of_devices_by_cap_args;
+        args = (char *) &lv_remote_node_num_of_devices_by_cap_args;
+        memset (&lv_remote_node_num_of_devices_by_cap_args, 0, sizeof lv_remote_node_num_of_devices_by_cap_args);
+        ret_filter = (xdrproc_t) xdr_remote_node_num_of_devices_by_cap_ret;
+        ret = (char *) &lv_remote_node_num_of_devices_by_cap_ret;
+        memset (&lv_remote_node_num_of_devices_by_cap_ret, 0, sizeof lv_remote_node_num_of_devices_by_cap_ret);
+        break;
 case REMOTE_PROC_NUM_OF_DEFINED_DOMAINS:
         fn = (dispatch_fn) remoteDispatchNumOfDefinedDomains;
         ret_filter = (xdrproc_t) xdr_remote_num_of_defined_domains_ret;
diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h
index 950ad05..cb0a0ce 100644
--- a/qemud/remote_dispatch_prototypes.h
+++ b/qemud/remote_dispatch_prototypes.h
@@ -70,9 +70,20 @@ static int remoteDispatchNetworkLookupByName (struct qemud_server *server, struc
 static int remoteDispatchNetworkLookupByUuid (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_uuid_args *args, remote_network_lookup_by_uuid_ret *ret);
 static int remoteDispatchNetworkSetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_set_autostart_args *args, void *ret);
 static int remoteDispatchNetworkUndefine (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_undefine_args *args, void *ret);
+static int remoteDispatchNodeDeviceCreate (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_device_create_args *args, remote_node_device_create_ret *ret);
+static int remoteDispatchNodeDeviceDestroy (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_device_destroy_args *args, void *ret);
+static int remoteDispatchNodeDeviceDumpXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_device_dump_xml_args *args, remote_node_device_dump_xml_ret *ret);
+static int remoteDispatchNodeDeviceLookupByKey (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_device_lookup_by_key_args *args, remote_node_device_lookup_by_key_ret *ret);
+static int remoteDispatchNodeDeviceLookupByName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_device_lookup_by_name_args *args, remote_node_device_lookup_by_name_ret *ret);
 static int remoteDispatchNodeGetCellsFreeMemory (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_get_cells_free_memory_args *args, remote_node_get_cells_free_memory_ret *ret);
 static int remoteDispatchNodeGetFreeMemory (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_node_get_free_memory_ret *ret);
 static int remoteDispatchNodeGetInfo (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_node_get_info_ret *ret);
+static int remoteDispatchNodeListDevices (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_list_devices_args *args, remote_node_list_devices_ret *ret);
+static int remoteDispatchNodeListDevicesByBus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_list_devices_by_bus_args *args, remote_node_list_devices_by_bus_ret *ret);
+static int remoteDispatchNodeListDevicesByCap (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_list_devices_by_cap_args *args, remote_node_list_devices_by_cap_ret *ret);
+static int remoteDispatchNodeNumOfDevices (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_node_num_of_devices_ret *ret);
+static int remoteDispatchNodeNumOfDevicesByBus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_num_of_devices_by_bus_args *args, remote_node_num_of_devices_by_bus_ret *ret);
+static int remoteDispatchNodeNumOfDevicesByCap (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_num_of_devices_by_cap_args *args, remote_node_num_of_devices_by_cap_ret *ret);
 static int remoteDispatchNumOfDefinedDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_domains_ret *ret);
 static int remoteDispatchNumOfDefinedNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_networks_ret *ret);
 static int remoteDispatchNumOfDefinedStoragePools (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_storage_pools_ret *ret);
diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c
index be1d6d8..6da98d4 100644
--- a/qemud/remote_protocol.c
+++ b/qemud/remote_protocol.c
@@ -84,6 +84,25 @@ xdr_remote_nonnull_storage_vol (XDR *xdrs, remote_nonnull_storage_vol *objp)
 }
 
 bool_t
+xdr_remote_nonnull_node_device (XDR *xdrs, remote_nonnull_node_device *objp)
+{
+        char **objp_cpp0 = (char **) (void *) &objp->cap_names.cap_names_val;
+
+         if (!xdr_remote_nonnull_string (xdrs, &objp->key))
+                 return FALSE;
+         if (!xdr_remote_nonnull_string (xdrs, &objp->name))
+                 return FALSE;
+         if (!xdr_remote_nonnull_string (xdrs, &objp->parent_key))
+                 return FALSE;
+         if (!xdr_remote_nonnull_string (xdrs, &objp->bus_name))
+                 return FALSE;
+         if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->cap_names.cap_names_len, REMOTE_NODE_DEVICE_CAPS_LIST_MAX,
+                sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
 xdr_remote_domain (XDR *xdrs, remote_domain *objp)
 {
 
@@ -120,6 +139,15 @@ xdr_remote_storage_vol (XDR *xdrs, remote_storage_vol *objp)
 }
 
 bool_t
+xdr_remote_node_device (XDR *xdrs, remote_node_device *objp)
+{
+
+         if (!xdr_pointer (xdrs, (char **)objp, sizeof (remote_nonnull_node_device), (xdrproc_t) xdr_remote_nonnull_node_device))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
 xdr_remote_error (XDR *xdrs, remote_error *objp)
 {
 
@@ -1943,6 +1971,198 @@ xdr_remote_storage_vol_get_path_ret (XDR *xdrs, remote_storage_vol_get_path_ret
 }
 
 bool_t
+xdr_remote_node_num_of_devices_ret (XDR *xdrs, remote_node_num_of_devices_ret *objp)
+{
+
+         if (!xdr_int (xdrs, &objp->num))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_list_devices_args (XDR *xdrs, remote_node_list_devices_args *objp)
+{
+
+         if (!xdr_int (xdrs, &objp->maxnames))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_list_devices_ret (XDR *xdrs, remote_node_list_devices_ret *objp)
+{
+        char **objp_cpp0 = (char **) (void *) &objp->names.names_val;
+
+         if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->names.names_len, REMOTE_NODE_DEVICE_NAME_LIST_MAX,
+                sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_num_of_devices_by_bus_args (XDR *xdrs, remote_node_num_of_devices_by_bus_args *objp)
+{
+
+         if (!xdr_remote_nonnull_string (xdrs, &objp->bus))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_num_of_devices_by_bus_ret (XDR *xdrs, remote_node_num_of_devices_by_bus_ret *objp)
+{
+
+         if (!xdr_int (xdrs, &objp->num))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_list_devices_by_bus_args (XDR *xdrs, remote_node_list_devices_by_bus_args *objp)
+{
+
+         if (!xdr_int (xdrs, &objp->maxnames))
+                 return FALSE;
+         if (!xdr_remote_nonnull_string (xdrs, &objp->bus))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_list_devices_by_bus_ret (XDR *xdrs, remote_node_list_devices_by_bus_ret *objp)
+{
+        char **objp_cpp0 = (char **) (void *) &objp->names.names_val;
+
+         if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->names.names_len, REMOTE_NODE_DEVICE_NAME_LIST_MAX,
+                sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_num_of_devices_by_cap_args (XDR *xdrs, remote_node_num_of_devices_by_cap_args *objp)
+{
+
+         if (!xdr_remote_nonnull_string (xdrs, &objp->cap))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_num_of_devices_by_cap_ret (XDR *xdrs, remote_node_num_of_devices_by_cap_ret *objp)
+{
+
+         if (!xdr_int (xdrs, &objp->num))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_list_devices_by_cap_args (XDR *xdrs, remote_node_list_devices_by_cap_args *objp)
+{
+
+         if (!xdr_int (xdrs, &objp->maxnames))
+                 return FALSE;
+         if (!xdr_remote_nonnull_string (xdrs, &objp->cap))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_list_devices_by_cap_ret (XDR *xdrs, remote_node_list_devices_by_cap_ret *objp)
+{
+        char **objp_cpp0 = (char **) (void *) &objp->names.names_val;
+
+         if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->names.names_len, REMOTE_NODE_DEVICE_NAME_LIST_MAX,
+                sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_device_lookup_by_key_args (XDR *xdrs, remote_node_device_lookup_by_key_args *objp)
+{
+
+         if (!xdr_remote_nonnull_string (xdrs, &objp->key))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_device_lookup_by_key_ret (XDR *xdrs, remote_node_device_lookup_by_key_ret *objp)
+{
+
+         if (!xdr_remote_nonnull_node_device (xdrs, &objp->dev))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_device_lookup_by_name_args (XDR *xdrs, remote_node_device_lookup_by_name_args *objp)
+{
+
+         if (!xdr_remote_nonnull_string (xdrs, &objp->name))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_device_lookup_by_name_ret (XDR *xdrs, remote_node_device_lookup_by_name_ret *objp)
+{
+
+         if (!xdr_remote_nonnull_node_device (xdrs, &objp->dev))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_device_dump_xml_args (XDR *xdrs, remote_node_device_dump_xml_args *objp)
+{
+
+         if (!xdr_remote_nonnull_string (xdrs, &objp->key))
+                 return FALSE;
+         if (!xdr_u_int (xdrs, &objp->flags))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_device_dump_xml_ret (XDR *xdrs, remote_node_device_dump_xml_ret *objp)
+{
+
+         if (!xdr_remote_nonnull_string (xdrs, &objp->xml))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_device_create_args (XDR *xdrs, remote_node_device_create_args *objp)
+{
+
+         if (!xdr_remote_nonnull_string (xdrs, &objp->xml))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_device_create_ret (XDR *xdrs, remote_node_device_create_ret *objp)
+{
+
+         if (!xdr_remote_nonnull_node_device (xdrs, &objp->dev))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_node_device_destroy_args (XDR *xdrs, remote_node_device_destroy_args *objp)
+{
+
+         if (!xdr_remote_nonnull_string (xdrs, &objp->key))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
 xdr_remote_procedure (XDR *xdrs, remote_procedure *objp)
 {
 
diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h
index bcaa219..06a7e10 100644
--- a/qemud/remote_protocol.h
+++ b/qemud/remote_protocol.h
@@ -31,6 +31,8 @@ typedef remote_nonnull_string *remote_string;
 #define REMOTE_NETWORK_NAME_LIST_MAX 256
 #define REMOTE_STORAGE_POOL_NAME_LIST_MAX 256
 #define REMOTE_STORAGE_VOL_NAME_LIST_MAX 1024
+#define REMOTE_NODE_DEVICE_NAME_LIST_MAX 16384
+#define REMOTE_NODE_DEVICE_CAPS_LIST_MAX 16384
 #define REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX 16
 #define REMOTE_NODE_MAX_CELLS 1024
 #define REMOTE_AUTH_SASL_DATA_MAX 65536
@@ -66,6 +68,18 @@ struct remote_nonnull_storage_vol {
 };
 typedef struct remote_nonnull_storage_vol remote_nonnull_storage_vol;
 
+struct remote_nonnull_node_device {
+        remote_nonnull_string key;
+        remote_nonnull_string name;
+        remote_nonnull_string parent_key;
+        remote_nonnull_string bus_name;
+        struct {
+                u_int cap_names_len;
+                remote_nonnull_string *cap_names_val;
+        } cap_names;
+};
+typedef struct remote_nonnull_node_device remote_nonnull_node_device;
+
 typedef remote_nonnull_domain *remote_domain;
 
 typedef remote_nonnull_network *remote_network;
@@ -74,6 +88,8 @@ typedef remote_nonnull_storage_pool *remote_storage_pool;
 
 typedef remote_nonnull_storage_vol *remote_storage_vol;
 
+typedef remote_nonnull_node_device *remote_node_device;
+
 struct remote_error {
         int code;
         int domain;
@@ -1081,6 +1097,118 @@ struct remote_storage_vol_get_path_ret {
         remote_nonnull_string name;
 };
 typedef struct remote_storage_vol_get_path_ret remote_storage_vol_get_path_ret;
+
+struct remote_node_num_of_devices_ret {
+        int num;
+};
+typedef struct remote_node_num_of_devices_ret remote_node_num_of_devices_ret;
+
+struct remote_node_list_devices_args {
+        int maxnames;
+};
+typedef struct remote_node_list_devices_args remote_node_list_devices_args;
+
+struct remote_node_list_devices_ret {
+        struct {
+                u_int names_len;
+                remote_nonnull_string *names_val;
+        } names;
+};
+typedef struct remote_node_list_devices_ret remote_node_list_devices_ret;
+
+struct remote_node_num_of_devices_by_bus_args {
+        remote_nonnull_string bus;
+};
+typedef struct remote_node_num_of_devices_by_bus_args remote_node_num_of_devices_by_bus_args;
+
+struct remote_node_num_of_devices_by_bus_ret {
+        int num;
+};
+typedef struct remote_node_num_of_devices_by_bus_ret remote_node_num_of_devices_by_bus_ret;
+
+struct remote_node_list_devices_by_bus_args {
+        int maxnames;
+        remote_nonnull_string bus;
+};
+typedef struct remote_node_list_devices_by_bus_args remote_node_list_devices_by_bus_args;
+
+struct remote_node_list_devices_by_bus_ret {
+        struct {
+                u_int names_len;
+                remote_nonnull_string *names_val;
+        } names;
+};
+typedef struct remote_node_list_devices_by_bus_ret remote_node_list_devices_by_bus_ret;
+
+struct remote_node_num_of_devices_by_cap_args {
+        remote_nonnull_string cap;
+};
+typedef struct remote_node_num_of_devices_by_cap_args remote_node_num_of_devices_by_cap_args;
+
+struct remote_node_num_of_devices_by_cap_ret {
+        int num;
+};
+typedef struct remote_node_num_of_devices_by_cap_ret remote_node_num_of_devices_by_cap_ret;
+
+struct remote_node_list_devices_by_cap_args {
+        int maxnames;
+        remote_nonnull_string cap;
+};
+typedef struct remote_node_list_devices_by_cap_args remote_node_list_devices_by_cap_args;
+
+struct remote_node_list_devices_by_cap_ret {
+        struct {
+                u_int names_len;
+                remote_nonnull_string *names_val;
+        } names;
+};
+typedef struct remote_node_list_devices_by_cap_ret remote_node_list_devices_by_cap_ret;
+
+struct remote_node_device_lookup_by_key_args {
+        remote_nonnull_string key;
+};
+typedef struct remote_node_device_lookup_by_key_args remote_node_device_lookup_by_key_args;
+
+struct remote_node_device_lookup_by_key_ret {
+        remote_nonnull_node_device dev;
+};
+typedef struct remote_node_device_lookup_by_key_ret remote_node_device_lookup_by_key_ret;
+
+struct remote_node_device_lookup_by_name_args {
+        remote_nonnull_string name;
+};
+typedef struct remote_node_device_lookup_by_name_args remote_node_device_lookup_by_name_args;
+
+struct remote_node_device_lookup_by_name_ret {
+        remote_nonnull_node_device dev;
+};
+typedef struct remote_node_device_lookup_by_name_ret remote_node_device_lookup_by_name_ret;
+
+struct remote_node_device_dump_xml_args {
+        remote_nonnull_string key;
+        u_int flags;
+};
+typedef struct remote_node_device_dump_xml_args remote_node_device_dump_xml_args;
+
+struct remote_node_device_dump_xml_ret {
+        remote_nonnull_string xml;
+};
+typedef struct remote_node_device_dump_xml_ret remote_node_device_dump_xml_ret;
+
+struct remote_node_device_create_args {
+        remote_nonnull_string xml;
+};
+typedef struct remote_node_device_create_args remote_node_device_create_args;
+
+struct remote_node_device_create_ret {
+        remote_nonnull_node_device dev;
+};
+typedef struct remote_node_device_create_ret remote_node_device_create_ret;
+
+struct remote_node_device_destroy_args {
+        remote_nonnull_string key;
+};
+typedef struct remote_node_device_destroy_args remote_node_device_destroy_args;
 #define REMOTE_PROGRAM 0x20008086
 #define REMOTE_PROTOCOL_VERSION 1
 
@@ -1189,6 +1317,18 @@ enum remote_procedure {
         REMOTE_PROC_NODE_GET_FREE_MEMORY = 102,
         REMOTE_PROC_DOMAIN_BLOCK_PEEK = 103,
         REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104,
+        REMOTE_PROC_NODE_NUM_OF_DEVICES = 105,
+        REMOTE_PROC_NODE_LIST_DEVICES = 106,
+        REMOTE_PROC_NODE_NUM_OF_DEVICES_BY_CAP = 107,
+        REMOTE_PROC_NODE_LIST_DEVICES_BY_CAP = 108,
+        REMOTE_PROC_NODE_NUM_OF_DEVICES_BY_BUS = 109,
+        REMOTE_PROC_NODE_LIST_DEVICES_BY_BUS = 110,
+        REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_NAME = 111,
+        REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_KEY = 112,
+        REMOTE_PROC_NODE_DEVICE_DUMP_XML = 113,
+        REMOTE_PROC_NODE_DEVICE_CREATE = 114,
+        REMOTE_PROC_NODE_DEVICE_DESTROY = 115,
+        REMOTE_PROC_DOMAIN_ASYNC_EVENT = 117,
 };
 typedef enum remote_procedure remote_procedure;
 
@@ -1226,10 +1366,12 @@ extern  bool_t xdr_remote_nonnull_domain (XDR *, remote_nonnull_domain*);
 extern  bool_t xdr_remote_nonnull_network (XDR *, remote_nonnull_network*);
 extern  bool_t xdr_remote_nonnull_storage_pool (XDR *, remote_nonnull_storage_pool*);
 extern  bool_t xdr_remote_nonnull_storage_vol (XDR *, remote_nonnull_storage_vol*);
+extern  bool_t xdr_remote_nonnull_node_device (XDR *, remote_nonnull_node_device*);
 extern  bool_t xdr_remote_domain (XDR *, remote_domain*);
 extern  bool_t xdr_remote_network (XDR *, remote_network*);
 extern  bool_t xdr_remote_storage_pool (XDR *, remote_storage_pool*);
 extern  bool_t xdr_remote_storage_vol (XDR *, remote_storage_vol*);
+extern  bool_t xdr_remote_node_device (XDR *, remote_node_device*);
 extern  bool_t xdr_remote_error (XDR *, remote_error*);
 extern  bool_t xdr_remote_auth_type (XDR *, remote_auth_type*);
 extern  bool_t xdr_remote_vcpu_info (XDR *, remote_vcpu_info*);
@@ -1394,6 +1536,26 @@ extern  bool_t xdr_remote_storage_vol_get_info_args (XDR *, remote_storage_vol_g
 extern  bool_t xdr_remote_storage_vol_get_info_ret (XDR *, remote_storage_vol_get_info_ret*);
 extern  bool_t xdr_remote_storage_vol_get_path_args (XDR *, remote_storage_vol_get_path_args*);
 extern  bool_t xdr_remote_storage_vol_get_path_ret (XDR *, remote_storage_vol_get_path_ret*);
+extern  bool_t xdr_remote_node_num_of_devices_ret (XDR *, remote_node_num_of_devices_ret*);
+extern  bool_t xdr_remote_node_list_devices_args (XDR *, remote_node_list_devices_args*);
+extern  bool_t xdr_remote_node_list_devices_ret (XDR *, remote_node_list_devices_ret*);
+extern  bool_t xdr_remote_node_num_of_devices_by_bus_args (XDR *, remote_node_num_of_devices_by_bus_args*);
+extern  bool_t xdr_remote_node_num_of_devices_by_bus_ret (XDR *, remote_node_num_of_devices_by_bus_ret*);
+extern  bool_t xdr_remote_node_list_devices_by_bus_args (XDR *, remote_node_list_devices_by_bus_args*);
+extern  bool_t xdr_remote_node_list_devices_by_bus_ret (XDR *, remote_node_list_devices_by_bus_ret*);
+extern  bool_t xdr_remote_node_num_of_devices_by_cap_args (XDR *, remote_node_num_of_devices_by_cap_args*);
+extern  bool_t xdr_remote_node_num_of_devices_by_cap_ret (XDR *, remote_node_num_of_devices_by_cap_ret*);
+extern  bool_t xdr_remote_node_list_devices_by_cap_args (XDR *, remote_node_list_devices_by_cap_args*);
+extern  bool_t xdr_remote_node_list_devices_by_cap_ret (XDR *, remote_node_list_devices_by_cap_ret*);
+extern  bool_t xdr_remote_node_device_lookup_by_key_args (XDR *, remote_node_device_lookup_by_key_args*);
+extern  bool_t xdr_remote_node_device_lookup_by_key_ret (XDR *, remote_node_device_lookup_by_key_ret*);
+extern  bool_t xdr_remote_node_device_lookup_by_name_args (XDR *, remote_node_device_lookup_by_name_args*);
+extern  bool_t xdr_remote_node_device_lookup_by_name_ret (XDR *, remote_node_device_lookup_by_name_ret*);
+extern  bool_t xdr_remote_node_device_dump_xml_args (XDR *, remote_node_device_dump_xml_args*);
+extern  bool_t xdr_remote_node_device_dump_xml_ret (XDR *, remote_node_device_dump_xml_ret*);
+extern  bool_t xdr_remote_node_device_create_args (XDR *, remote_node_device_create_args*);
+extern  bool_t xdr_remote_node_device_create_ret (XDR *, remote_node_device_create_ret*);
+extern  bool_t xdr_remote_node_device_destroy_args (XDR *, remote_node_device_destroy_args*);
 extern  bool_t xdr_remote_procedure (XDR *, remote_procedure*);
 extern  bool_t xdr_remote_message_direction (XDR *, remote_message_direction*);
 extern  bool_t xdr_remote_message_status (XDR *, remote_message_status*);
@@ -1407,10 +1569,12 @@ extern bool_t xdr_remote_nonnull_domain ();
 extern bool_t xdr_remote_nonnull_network ();
 extern bool_t xdr_remote_nonnull_storage_pool ();
 extern bool_t xdr_remote_nonnull_storage_vol ();
+extern bool_t xdr_remote_nonnull_node_device ();
 extern bool_t xdr_remote_domain ();
 extern bool_t xdr_remote_network ();
 extern bool_t xdr_remote_storage_pool ();
 extern bool_t xdr_remote_storage_vol ();
+extern bool_t xdr_remote_node_device ();
 extern bool_t xdr_remote_error ();
 extern bool_t xdr_remote_auth_type ();
 extern bool_t xdr_remote_vcpu_info ();
@@ -1575,6 +1739,26 @@ extern bool_t xdr_remote_storage_vol_get_info_args ();
 extern bool_t xdr_remote_storage_vol_get_info_ret ();
 extern bool_t xdr_remote_storage_vol_get_path_args ();
 extern bool_t xdr_remote_storage_vol_get_path_ret ();
+extern bool_t xdr_remote_node_num_of_devices_ret ();
+extern bool_t xdr_remote_node_list_devices_args ();
+extern bool_t xdr_remote_node_list_devices_ret ();
+extern bool_t xdr_remote_node_num_of_devices_by_bus_args ();
+extern bool_t xdr_remote_node_num_of_devices_by_bus_ret ();
+extern bool_t xdr_remote_node_list_devices_by_bus_args ();
+extern bool_t xdr_remote_node_list_devices_by_bus_ret ();
+extern bool_t xdr_remote_node_num_of_devices_by_cap_args ();
+extern bool_t xdr_remote_node_num_of_devices_by_cap_ret ();
+extern bool_t xdr_remote_node_list_devices_by_cap_args ();
+extern bool_t xdr_remote_node_list_devices_by_cap_ret ();
+extern bool_t xdr_remote_node_device_lookup_by_key_args ();
+extern bool_t xdr_remote_node_device_lookup_by_key_ret ();
+extern bool_t xdr_remote_node_device_lookup_by_name_args ();
+extern bool_t xdr_remote_node_device_lookup_by_name_ret ();
+extern bool_t xdr_remote_node_device_dump_xml_args ();
+extern bool_t xdr_remote_node_device_dump_xml_ret ();
+extern bool_t xdr_remote_node_device_create_args ();
+extern bool_t xdr_remote_node_device_create_ret ();
+extern bool_t xdr_remote_node_device_destroy_args ();
 extern bool_t xdr_remote_procedure ();
 extern bool_t xdr_remote_message_direction ();
 extern bool_t xdr_remote_message_status ();
diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x
index f848ae5..927d4a4 100644
--- a/qemud/remote_protocol.x
+++ b/qemud/remote_protocol.x
@@ -86,6 +86,12 @@ const REMOTE_STORAGE_POOL_NAME_LIST_MAX = 256;
 /* Upper limit on lists of storage vol names. */
 const REMOTE_STORAGE_VOL_NAME_LIST_MAX = 1024;
 
+/* Upper limit on lists of node device names. */
+const REMOTE_NODE_DEVICE_NAME_LIST_MAX = 16384;
+
+/* Upper limit on lists of node device capabilities. */
+const REMOTE_NODE_DEVICE_CAPS_LIST_MAX = 16384;
+
 /* Upper limit on list of scheduler parameters. */
 const REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX = 16;
 
@@ -139,11 +145,21 @@ struct remote_nonnull_storage_vol {
     remote_nonnull_string key;
 };
 
+/* A node device which may not be NULL. */
+struct remote_nonnull_node_device {
+    remote_nonnull_string key;
+    remote_nonnull_string name;
+    remote_nonnull_string parent_key;
+    remote_nonnull_string bus_name;
+    remote_nonnull_string cap_names<REMOTE_NODE_DEVICE_CAPS_LIST_MAX>;
+};
+
 /* A domain or network which may be NULL. */
 typedef remote_nonnull_domain *remote_domain;
 typedef remote_nonnull_network *remote_network;
 typedef remote_nonnull_storage_pool *remote_storage_pool;
 typedef remote_nonnull_storage_vol *remote_storage_vol;
+typedef remote_nonnull_node_device *remote_node_device;
 
 /* Error message. See <virterror.h> for explanation of fields. */
 
@@ -965,6 +981,91 @@ struct remote_storage_vol_get_path_ret {
     remote_nonnull_string name;
 };
 
+/* Node driver calls: */
+
+struct remote_node_num_of_devices_ret {
+    int num;
+};
+
+struct remote_node_list_devices_args {
+    int maxnames;
+};
+
+struct remote_node_list_devices_ret {
+    remote_nonnull_string names<REMOTE_NODE_DEVICE_NAME_LIST_MAX>;
+};
+
+struct remote_node_num_of_devices_by_bus_args {
+    remote_nonnull_string bus;
+};
+
+struct remote_node_num_of_devices_by_bus_ret {
+    int num;
+};
+
+struct remote_node_list_devices_by_bus_args {
+    int maxnames;
+    remote_nonnull_string bus;
+};
+
+struct remote_node_list_devices_by_bus_ret {
+    remote_nonnull_string names<REMOTE_NODE_DEVICE_NAME_LIST_MAX>;
+};
+
+struct remote_node_num_of_devices_by_cap_args {
+    remote_nonnull_string cap;
+};
+
+struct remote_node_num_of_devices_by_cap_ret {
+    int num;
+};
+
+struct remote_node_list_devices_by_cap_args {
+    int maxnames;
+    remote_nonnull_string cap;
+};
+
+struct remote_node_list_devices_by_cap_ret {
+    remote_nonnull_string names<REMOTE_NODE_DEVICE_NAME_LIST_MAX>;
+};
+
+struct remote_node_device_lookup_by_key_args {
+    remote_nonnull_string key;
+};
+
+struct remote_node_device_lookup_by_key_ret {
+    remote_nonnull_node_device dev;
+};
+
+struct remote_node_device_lookup_by_name_args {
+    remote_nonnull_string name;
+};
+
+struct remote_node_device_lookup_by_name_ret {
+    remote_nonnull_node_device dev;
+};
+
+struct remote_node_device_dump_xml_args {
+    remote_nonnull_string key;
+    unsigned flags;
+};
+
+struct remote_node_device_dump_xml_ret {
+    remote_nonnull_string xml;
+};
+
+struct remote_node_device_create_args {
+    remote_nonnull_string xml;
+};
+
+struct remote_node_device_create_ret {
+    remote_nonnull_node_device dev;
+};
+
+struct remote_node_device_destroy_args {
+    remote_nonnull_string key;
+};
+
 /*----- Protocol. -----*/
 
 /* Define the program number, protocol version and procedure numbers here. */
@@ -1086,7 +1187,21 @@ enum remote_procedure {
     REMOTE_PROC_NODE_GET_FREE_MEMORY = 102,
 
     REMOTE_PROC_DOMAIN_BLOCK_PEEK = 103,
-    REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104
+    REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104,
+
+    REMOTE_PROC_NODE_NUM_OF_DEVICES = 105,
+    REMOTE_PROC_NODE_LIST_DEVICES = 106,
+    REMOTE_PROC_NODE_NUM_OF_DEVICES_BY_CAP = 107,
+    REMOTE_PROC_NODE_LIST_DEVICES_BY_CAP = 108,
+    REMOTE_PROC_NODE_NUM_OF_DEVICES_BY_BUS = 109,
+    REMOTE_PROC_NODE_LIST_DEVICES_BY_BUS = 110,
+    REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_NAME = 111,
+    REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_KEY = 112,
+    REMOTE_PROC_NODE_DEVICE_DUMP_XML = 113,
+    REMOTE_PROC_NODE_DEVICE_CREATE = 114,
+    REMOTE_PROC_NODE_DEVICE_DESTROY = 115,
+
+    REMOTE_PROC_DOMAIN_ASYNC_EVENT = 117
 };
 
 /* Custom RPC structure. */
diff --git a/src/Makefile.am b/src/Makefile.am
index 9845332..10fdd7d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,18 @@
 ## Process this file with automake to produce Makefile.in
 
+NODE_DEVICE_SOURCES = node_device.c node_device.h
+if HAVE_HAL
+NODE_DEVICE_CFLAGS = $(HAL_CFLAGS)
+NODE_DEVICE_LIBS = $(HAL_LIBS)
+NODE_DEVICE_SOURCES += node_device_hal.c
+else
+if HAVE_DEVKIT
+NODE_DEVICE_CFLAGS = $(DEVKIT_CFLAGS)
+NODE_DEVICE_LIBS = $(DEVKIT_LIBS)
+NODE_DEVICE_SOURCES += node_device_devkit.c
+endif
+endif
+
 INCLUDES = \
 	   -I$(top_srcdir)/gnulib/lib -I../gnulib/lib \
 	   -I../include \
@@ -10,6 +23,7 @@ INCLUDES = \
 	   $(SASL_CFLAGS) \
 	   $(SELINUX_CFLAGS) \
 	   $(NUMACTL_CFLAGS) \
+	   $(NODE_DEVICE_CFLAGS) \
 	   -DBINDIR=\""$(libexecdir)"\" \
 	   -DSBINDIR=\""$(sbindir)"\" \
 	   -DSYSCONF_DIR="\"$(sysconfdir)\"" \
@@ -143,6 +157,7 @@ libvirt_la_SOURCES =						\
 		libvirt.c					\
 		$(GENERIC_LIB_SOURCES)				\
 		$(DOMAIN_CONF_SOURCES)				\
+		$(NODE_DEVICE_SOURCES)				\
 		$(NETWORK_CONF_SOURCES)				\
 		$(STORAGE_CONF_SOURCES)
 
@@ -206,7 +221,7 @@ EXTRA_DIST +=							\
 
 
 libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) $(SELINUX_LIBS) \
-                    $(NUMACTL_LIBS) \
+                    $(NUMACTL_LIBS) $(NODE_DEVICE_LIBS) \
 		    @CYGWIN_EXTRA_LIBADD@ ../gnulib/lib/libgnu.la
 libvirt_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvirt_sym.version \
                      -version-info @LIBVIRT_VERSION_INFO@ \
@@ -301,7 +316,7 @@ libvirt_lxc_SOURCES =						\
 		$(GENERIC_LIB_SOURCES) 				\
 		$(DOMAIN_CONF_SOURCES)
 libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS)
-libvirt_lxc_LDADD = $(LIBXML_LIBS) ../gnulib/lib/libgnu.la
+libvirt_lxc_LDADD = $(LIBXML_LIBS) ../gnulib/lib/libgnu.la $(NODE_DEVICE_LIBS)
 libvirt_lxc_CFLAGS =  $(LIBPARTED_CFLAGS)
 endif
 endif
diff --git a/src/driver.h b/src/driver.h
index 655cd05..6a2ef82 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -601,6 +601,72 @@ struct _virStateDriver {
 };
 #endif
 
+
+typedef struct _virNodeDriver virNodeDriver;
+typedef virNodeDriver *virNodeDriverPtr;
+
+typedef int (*virDrvNodeNumOfDevices)(virConnectPtr conn);
+
+typedef int (*virDrvNodeListDevices)(virConnectPtr conn,
+                                     char **const names,
+                                     int maxnames);
+
+typedef int (*virDrvNodeNumOfDevicesByCap)(virConnectPtr conn,
+                                           const char *cap);
+
+typedef int (*virDrvNodeListDevicesByCap)(virConnectPtr conn,
+                                          const char *cap,
+                                          char **const names,
+                                          int maxnames);
+
+typedef int (*virDrvNodeNumOfDevicesByBus)(virConnectPtr conn,
+                                           const char *bus);
+
+typedef int (*virDrvNodeListDevicesByBus)(virConnectPtr conn,
+                                          const char *bus,
+                                          char **const names,
+                                          int maxnames);
+
+typedef virNodeDevicePtr (*virDrvNodeDeviceLookupByName)(virConnectPtr conn,
+                                                         const char *name);
+
+typedef virNodeDevicePtr (*virDrvNodeDeviceLookupByKey)(virConnectPtr conn,
+                                                        const char *key);
+
+typedef int (*virDrvNodeDeviceFree)(virNodeDevicePtr dev);
+
+typedef char * (*virDrvNodeDeviceDumpXML)(virNodeDevicePtr dev,
+                                          unsigned int flags);
+
+typedef virNodeDevicePtr (*virDrvNodeDeviceCreate)(virConnectPtr conn,
+                                                   const char *xml);
+
+typedef int (*virDrvNodeDeviceDestroy)(virNodeDevicePtr dev);
+
+/**
+ * _virNodeDriver:
+ *
+ * Structure associated with a virtualized node, independent of
+ * the hypervisor(s) in use, and entry points for it.
+ *
+ */
+struct _virNodeDriver {
+    const char * name;    /* the name of the driver */
+    virDrvOpen open;
+    virDrvClose close;
+    virDrvNodeNumOfDevices numOfDevices;
+    virDrvNodeListDevices listDevices;
+    virDrvNodeNumOfDevicesByCap numOfDevicesByCap;
+    virDrvNodeListDevicesByCap listDevicesByCap;
+    virDrvNodeNumOfDevicesByBus numOfDevicesByBus;
+    virDrvNodeListDevicesByBus listDevicesByBus;
+    virDrvNodeDeviceLookupByName deviceLookupByName;
+    virDrvNodeDeviceLookupByKey deviceLookupByKey;
+    virDrvNodeDeviceDumpXML deviceDumpXML;
+    virDrvNodeDeviceCreate deviceCreate;
+    virDrvNodeDeviceDestroy deviceDestroy;
+};
+
 /*
  * Registration
  * TODO: also need ways to (des)activate a given driver
@@ -609,6 +675,7 @@ struct _virStateDriver {
 int virRegisterDriver(virDriverPtr);
 int virRegisterNetworkDriver(virNetworkDriverPtr);
 int virRegisterStorageDriver(virStorageDriverPtr);
+int virRegisterNodeDriver(virNodeDriverPtr);
 #ifdef WITH_LIBVIRTD
 int virRegisterStateDriver(virStateDriverPtr);
 #endif
diff --git a/src/hash.c b/src/hash.c
index 01def44..8663de3 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -708,6 +708,9 @@ virGetConnect(void) {
     ret->storageVols = virHashCreate(20);
     if (ret->storageVols == NULL)
         goto failed;
+    ret->nodeDevices = virHashCreate(256);
+    if (ret->nodeDevices == NULL)
+        goto failed;
 
     pthread_mutex_init(&ret->lock, NULL);
 
@@ -724,6 +727,8 @@ failed:
             virHashFree(ret->storagePools, (virHashDeallocator) virStoragePoolFreeName);
         if (ret->storageVols != NULL)
             virHashFree(ret->storageVols, (virHashDeallocator) virStorageVolFreeName);
+        if (ret->nodeDevices != NULL)
+            virHashFree(ret->nodeDevices, (virHashDeallocator) virNodeDeviceFree);
 
         pthread_mutex_destroy(&ret->lock);
         VIR_FREE(ret);
@@ -751,6 +756,8 @@ virReleaseConnect(virConnectPtr conn) {
         virHashFree(conn->storagePools, (virHashDeallocator) virStoragePoolFreeName);
     if (conn->storageVols != NULL)
         virHashFree(conn->storageVols, (virHashDeallocator) virStorageVolFreeName);
+    if (conn->nodeDevices != NULL)
+        virHashFree(conn->nodeDevices, (virHashDeallocator) virNodeDeviceFree);
 
     virResetError(&conn->err);
     if (__lastErr.conn == conn)
@@ -1339,3 +1346,177 @@ virUnrefStorageVol(virStorageVolPtr vol) {
     pthread_mutex_unlock(&vol->conn->lock);
     return (refs);
 }
+
+
+/**
+ * virGetNodeDevice:
+ * @conn: the hypervisor connection
+ * @key: device key
+ * @name: device name
+ * @parent_key: parent device key
+ * @bus_name: device bus name (optional)
+ * @bus: device bus xml (optional)
+ * @cap_names: device capability names (optional)
+ * @caps: device capability xml (optional)
+ *
+ * Lookup if the device is already registered for that connection,
+ * if yes return a new pointer to it, if no allocate a new structure,
+ * and register it in the table. In any case a corresponding call to
+ * virFreeNodeDevice() is needed to not leak data.
+ *
+ * Returns a pointer to the node device, or NULL in case of failure
+ */
+virNodeDevicePtr
+__virGetNodeDevice(virConnectPtr conn,
+                   const char *key, const char *name,
+                   const char *parent_key,
+                   const char *bus_name, xmlNodePtr bus,
+                   char **cap_names, xmlNodePtr *caps)
+{
+    virNodeDevicePtr ret = NULL;
+
+    if ((!VIR_IS_CONNECT(conn)) || (key == NULL)) {
+        virHashError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return(NULL);
+    }
+    pthread_mutex_lock(&conn->lock);
+
+    ret = (virNodeDevicePtr) virHashLookup(conn->nodeDevices, key);
+    if (ret == NULL) {
+       if (VIR_ALLOC(ret) < 0) {
+            virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating node dev"));
+            goto error;
+        }
+        ret->magic = VIR_NODE_DEVICE_MAGIC;
+        ret->conn = conn;
+        ret->key = strdup(key);
+        if (ret->key == NULL) {
+            virHashError(conn, VIR_ERR_NO_MEMORY, _("copying node dev key"));
+            goto error;
+        }
+        ret->name = strdup(name);
+        if (ret->name == NULL) {
+            virHashError(conn, VIR_ERR_NO_MEMORY, _("copying node dev name"));
+            goto error;
+        }
+        if (parent_key) {
+            ret->parent_key = strdup(parent_key);
+            if (ret->parent_key == NULL) {
+                virHashError(conn, VIR_ERR_NO_MEMORY,
+                             _("copying node dev parent key"));
+                goto error;
+            }
+        }
+        if (bus_name) {
+            ret->bus_name = strdup(bus_name);
+            if (ret->name == NULL) {
+                virHashError(conn, VIR_ERR_NO_MEMORY,
+                             _("copying node dev bus name"));
+                goto error;
+            }
+        }
+        ret->bus = bus;
+        ret->cap_names = cap_names;
+        ret->caps = caps;
+
+        if (virHashAddEntry(conn->nodeDevices, key, ret) < 0) {
+            virHashError(conn, VIR_ERR_INTERNAL_ERROR,
+                         _("failed to add node dev to conn hash table"));
+            DEBUG("key '%s'", key);
+            goto error;
+        }
+        conn->refs++;
+    }
+    ret->refs++;
+    pthread_mutex_unlock(&conn->lock);
+    return(ret);
+
+error:
+    pthread_mutex_unlock(&conn->lock);
+    if (ret != NULL) {
+        VIR_FREE(ret->key);
+        VIR_FREE(ret->name);
+        VIR_FREE(ret->parent_key);
+        VIR_FREE(ret);
+    }
+    return(NULL);
+}
+
+
+/**
+ * virReleaseNodeDevice:
+ * @dev: the dev to release
+ *
+ * Unconditionally release all memory associated with a dev.
+ * The conn.lock mutex must be held prior to calling this, and will
+ * be released prior to this returning. The dev obj must not
+ * be used once this method returns.
+ *
+ * It will also unreference the associated connection object,
+ * which may also be released if its ref count hits zero.
+ */
+static void
+virReleaseNodeDevice(virNodeDevicePtr dev) {
+    int i;
+    virConnectPtr conn = dev->conn;
+    DEBUG("release dev %p %s", dev, dev->key);
+
+    if (virHashRemoveEntry(conn->nodeDevices, dev->key, NULL) < 0)
+        virHashError(conn, VIR_ERR_INTERNAL_ERROR,
+                     _("dev missing from connection hash table"));
+
+    dev->magic = -1;
+    VIR_FREE(dev->key);
+    VIR_FREE(dev->name);
+    VIR_FREE(dev->parent_key);
+    VIR_FREE(dev->bus_name);
+    if (dev->bus)
+        xmlFreeNode(dev->bus);
+    if (dev->cap_names)
+        for (i = 0; dev->cap_names[i]; i++)
+            VIR_FREE(dev->cap_names[i]);
+    VIR_FREE(dev->cap_names);
+    if (dev->caps)
+        for (i = 0; dev->caps[i]; i++)
+            xmlFreeNode(dev->caps[i]);
+    VIR_FREE(dev->caps);
+    VIR_FREE(dev);
+
+    DEBUG("unref connection %p %s %d", conn, conn->name, conn->refs);
+    conn->refs--;
+    if (conn->refs == 0) {
+        virReleaseConnect(conn);
+        /* Already unlocked mutex */
+        return;
+    }
+
+    pthread_mutex_unlock(&conn->lock);
+}
+
+
+/**
+ * virUnrefNodeDevice:
+ * @dev: the dev to unreference
+ *
+ * Unreference the dev. If the use count drops to zero, the structure is
+ * actually freed.
+ *
+ * Returns the reference count or -1 in case of failure.
+ */
+int
+virUnrefNodeDevice(virNodeDevicePtr dev) {
+    int refs;
+
+    pthread_mutex_lock(&dev->conn->lock);
+    DEBUG("unref dev %p %s %d", dev, dev->name, dev->refs);
+    dev->refs--;
+    refs = dev->refs;
+    if (refs == 0) {
+        virReleaseNodeDevice(dev);
+        /* Already unlocked mutex */
+        return (0);
+    }
+
+    pthread_mutex_unlock(&dev->conn->lock);
+    return (refs);
+}
diff --git a/src/internal.h b/src/internal.h
index d96504d..9a94f6d 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -7,6 +7,7 @@
 
 #include <errno.h>
 #include <limits.h>
+#include <libxml/tree.h>
 
 #ifdef HAVE_SYS_SYSLIMITS_H
 #include <sys/syslimits.h>
@@ -37,6 +38,7 @@
 #include "libvirt/libvirt.h"
 #include "libvirt/virterror.h"
 #include "driver.h"
+#include "node_device.h"
 
 /* On architectures which lack these limits, define them (ie. Cygwin).
  * Note that the libvirt code should be robust enough to handle the
@@ -188,6 +190,16 @@ extern int debugFlag;
 #define VIR_IS_STORAGE_VOL(obj)		((obj) && (obj)->magic==VIR_STORAGE_VOL_MAGIC)
 #define VIR_IS_CONNECTED_STORAGE_VOL(obj)	(VIR_IS_STORAGE_VOL(obj) && VIR_IS_CONNECT((obj)->conn))
 
+/**
+ * VIR_NODE_DEVICE_MAGIC:
+ *
+ * magic value used to protect the API when pointers to storage vol structures
+ * are passed down by the users.
+ */
+#define VIR_NODE_DEVICE_MAGIC                   0xDEAD5679
+#define VIR_IS_NODE_DEVICE(obj)                 ((obj) && (obj)->magic==VIR_NODE_DEVICE_MAGIC)
+#define VIR_IS_CONNECTED_NODE_DEVICE(obj)       (VIR_IS_NODE_DEVICE(obj) && VIR_IS_CONNECT((obj)->conn))
+
 /*
  * arbitrary limitations
  */
@@ -208,6 +220,7 @@ struct _virConnect {
     virDriverPtr      driver;
     virNetworkDriverPtr networkDriver;
     virStorageDriverPtr storageDriver;
+    virNodeDriverPtr  nodeDriver;
 
     /* Private data pointer which can be used by driver and
      * network driver as they wish.
@@ -216,6 +229,7 @@ struct _virConnect {
     void *            privateData;
     void *            networkPrivateData;
     void *            storagePrivateData;
+    void *            nodePrivateData;
 
     /* Per-connection error. */
     virError err;           /* the last error */
@@ -233,6 +247,7 @@ struct _virConnect {
     virHashTablePtr networks; /* hash table for known domains */
     virHashTablePtr storagePools;/* hash table for known storage pools */
     virHashTablePtr storageVols;/* hash table for known storage vols */
+    virHashTablePtr nodeDevices; /* maps device key to virNodeDevicePtr */
     int refs;                 /* reference count */
 };
 
@@ -292,6 +307,25 @@ struct _virStorageVol {
 };
 
 
+/**
+ * _virNodeDevice:
+ *
+ * Internal structure associated with a node device
+ */
+struct _virNodeDevice {
+    unsigned int magic;                 /* specific value to check */
+    int refs;                           /* reference count */
+    virConnectPtr conn;                 /* pointer back to the connection */
+    char *key;                          /* device key */
+    char *name;                         /* device name */
+    char *parent_key;                   /* parent device */
+    char *bus_name;                     /* (optional) bus name */
+    xmlNodePtr bus;                     /* (optional) bus descriptor */
+    char **cap_names;                   /* capability names (null-term) */
+    xmlNodePtr *caps;                   /* capability descs (null-term) */
+};
+
+
 /************************************************************************
  *									*
  *		API for error handling					*
@@ -311,6 +345,15 @@ void __virRaiseError(virConnectPtr conn,
   ATTRIBUTE_FORMAT(printf, 12, 13);
 const char *__virErrorMsg(virErrorNumber error, const char *info);
 
+void virLibConnError(virConnectPtr conn,
+                     virErrorNumber error,
+                     const char *info);
+
+void virLibConnWarning(virConnectPtr conn,
+                       virErrorNumber error,
+                       const char *info);
+
+
 /************************************************************************
  *									*
  *	API for domain/connections (de)allocations and lookups		*
@@ -337,11 +380,20 @@ virStorageVolPtr  __virGetStorageVol  (virConnectPtr conn,
                                        const char *name,
                                        const char *key);
 int               virUnrefStorageVol  (virStorageVolPtr vol);
+virNodeDevicePtr  __virGetNodeDevice  (virConnectPtr conn,
+                                       const char *key,
+                                       const char *name,
+                                       const char *parent_key,
+                                       const char *bus_name, xmlNodePtr bus,
+                                       char **cap_names, xmlNodePtr *caps);
+int               virUnrefNodeDevice  (virNodeDevicePtr vol);
 
 #define virGetDomain(c,n,u) __virGetDomain((c),(n),(u))
 #define virGetNetwork(c,n,u) __virGetNetwork((c),(n),(u))
 #define virGetStoragePool(c,n,u) __virGetStoragePool((c),(n),(u))
 #define virGetStorageVol(c,p,n,u) __virGetStorageVol((c),(p),(n),(u))
+#define virGetNodeDevice(c,k,n,p,bn,bu,cn,ca) \
+    __virGetNodeDevice((c),(k),(n),(p),(bn),(bu),(cn),(ca))
 
 #ifdef WITH_LIBVIRTD
 int __virStateInitialize(void);
diff --git a/src/libvirt.c b/src/libvirt.c
index ed98323..256b3da 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -70,6 +70,8 @@ static virNetworkDriverPtr virNetworkDriverTab[MAX_DRIVERS];
 static int virNetworkDriverTabCount = 0;
 static virStorageDriverPtr virStorageDriverTab[MAX_DRIVERS];
 static int virStorageDriverTabCount = 0;
+static virNodeDriverPtr virNodeDriverTab[MAX_DRIVERS];
+static int virNodeDriverTabCount = 0;
 #ifdef WITH_LIBVIRTD
 static virStateDriverPtr virStateDriverTab[MAX_DRIVERS];
 static int virStateDriverTabCount = 0;
@@ -286,20 +288,28 @@ virInitialize(void)
 #ifdef WITH_XEN
     if (xenUnifiedRegister () == -1) return -1;
 #endif
-#ifdef WITH_LIBVIRTD
-#ifdef WITH_QEMU
-    if (qemudRegister() == -1) return -1;
+#ifdef WITH_REMOTE
+    if (remoteRegister () == -1) return -1;
 #endif
 #ifdef WITH_OPENVZ
     if (openvzRegister() == -1) return -1;
 #endif
+#ifdef HAVE_HAL
+    registerCommonNodeFuncs(&HALNodeDriver);
+    if (virRegisterNodeDriver(&HALNodeDriver) == -1) return -1;
+#endif
+#ifdef HAVE_DEVKIT
+    registerCommonNodeFuncs(&DevkitNodeDriver);
+    if (virRegisterNodeDriver(&DevkitNodeDriver) == -1) return -1;
+#endif
+#ifdef WITH_LIBVIRTD
+#ifdef WITH_QEMU
+    if (qemudRegister() == -1) return -1;
+#endif
 #ifdef WITH_LXC
     if (lxcRegister() == -1) return -1;
 #endif
     if (storageRegister() == -1) return -1;
-#ifdef WITH_REMOTE
-    if (remoteRegister () == -1) return -1;
-#endif
 #endif
 
     return(0);
@@ -315,7 +325,7 @@ virInitialize(void)
  *
  * Handle an error at the connection level
  */
-static void
+void
 virLibConnError(virConnectPtr conn, virErrorNumber error, const char *info)
 {
     const char *errmsg;
@@ -336,7 +346,7 @@ virLibConnError(virConnectPtr conn, virErrorNumber error, const char *info)
  *
  * Handle an error at the connection level
  */
-static void
+void
 virLibConnWarning(virConnectPtr conn, virErrorNumber error, const char *info)
 {
     const char *errmsg;
@@ -454,6 +464,32 @@ virLibStorageVolError(virStorageVolPtr vol, virErrorNumber error,
 }
 
 /**
+ * virLibNodeDeviceError:
+ * @dev: the device if available
+ * @error: the error number
+ * @info: extra information string
+ *
+ * Handle an error at the node device level
+ */
+static void
+virLibNodeDeviceError(virNodeDevicePtr dev, virErrorNumber error,
+                      const char *info)
+{
+    virConnectPtr conn = NULL;
+    const char *errmsg;
+
+    if (error == VIR_ERR_OK)
+        return;
+
+    errmsg = __virErrorMsg(error, info);
+    if (error != VIR_ERR_INVALID_NODE_DEVICE)
+        conn = dev->conn;
+
+    __virRaiseError(conn, NULL, NULL, VIR_FROM_NODE, error, VIR_ERR_ERROR,
+                    errmsg, info, NULL, 0, 0, errmsg, info);
+}
+
+/**
  * virRegisterNetworkDriver:
  * @driver: pointer to a network driver block
  *
@@ -510,6 +546,34 @@ virRegisterStorageDriver(virStorageDriverPtr driver)
 }
 
 /**
+ * virRegisterNodeDriver:
+ * @driver: pointer to a node driver block
+ *
+ * Register a node driver
+ *
+ * Returns the driver priority or -1 in case of error.
+ */
+int
+virRegisterNodeDriver(virNodeDriverPtr driver)
+{
+    if (virInitialize() < 0)
+      return -1;
+
+    if (driver == NULL) {
+        virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return(-1);
+    }
+
+    if (virNodeDriverTabCount >= MAX_DRIVERS) {
+        virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return(-1);
+    }
+
+    virNodeDriverTab[virNodeDriverTabCount] = driver;
+    return virNodeDriverTabCount++;
+}
+
+/**
  * virRegisterDriver:
  * @driver: pointer to a driver block
  *
@@ -840,6 +904,33 @@ do_open (const char *name,
         }
     }
 
+    /* Node driver (optional) */
+    for (i = 0; i < virNodeDriverTabCount; i++) {
+        res = virNodeDriverTab[i]->open (ret, uri, auth, flags);
+        DEBUG("node driver %d %s returned %s",
+              i, virNodeDriverTab[i]->name,
+              res == VIR_DRV_OPEN_SUCCESS ? "SUCCESS" :
+              (res == VIR_DRV_OPEN_DECLINED ? "DECLINED" :
+               (res == VIR_DRV_OPEN_ERROR ? "ERROR" : "unknown status")));
+        if (res == VIR_DRV_OPEN_ERROR) {
+            if (STREQ(virNodeDriverTab[i]->name, "remote")) {
+                virLibConnWarning (NULL, VIR_WAR_NO_NODE,
+                                   "Is the libvirtd daemon running ?");
+            } else {
+                char *msg;
+                if (asprintf(&msg, "Is the %s daemon running?",
+                             virNodeDriverTab[i]->name) > 0) {
+                    virLibConnWarning (NULL, VIR_WAR_NO_NODE, msg);
+                    VIR_FREE(msg);
+                }
+            }
+            break;
+        } else if (res == VIR_DRV_OPEN_SUCCESS) {
+            ret->nodeDriver = virNodeDriverTab[i];
+            break;
+        }
+    }
+
     xmlFreeURI (uri);
 
     return ret;
@@ -960,6 +1051,8 @@ virConnectClose(virConnectPtr conn)
         conn->networkDriver->close (conn);
     if (conn->storageDriver)
         conn->storageDriver->close (conn);
+    if (conn->nodeDriver)
+        conn->nodeDriver->close (conn);
     conn->driver->close (conn);
 
     if (virUnrefConnect(conn) < 0)
@@ -5283,6 +5376,425 @@ virStorageVolGetPath(virStorageVolPtr vol)
 
 
 
+/**
+ * virNodeNumOfDevices:
+ * @conn: pointer to the hypervisor connection
+ *
+ * Provides the number of node devices.
+ *
+ * Returns the number of node devices or -1 in case of error
+ */
+int
+virNodeNumOfDevices(virConnectPtr conn)
+{
+    DEBUG("conn=%p", conn);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return (-1);
+    }
+
+    if (conn->nodeDriver && conn->nodeDriver->numOfDevices)
+        return conn->nodeDriver->numOfDevices (conn);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+/**
+ * virNodeListDevices:
+ * @conn: pointer to the hypervisor connection
+ * @names: array to collect the list of node device names
+ * @maxnames: size of @names
+ *
+ * Collect the list of node devices, and store their names in @names
+ *
+ * Returns the number of node devices found or -1 in case of error
+ */
+int
+virNodeListDevices(virConnectPtr conn, char **const names, int maxnames)
+{
+    DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return (-1);
+    }
+
+    if ((names == NULL) || (maxnames < 0)) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return (-1);
+    }
+
+    if (conn->nodeDriver && conn->nodeDriver->listDevices)
+        return conn->nodeDriver->listDevices (conn, names, maxnames);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+/**
+ * virNodeNumOfDevicesByBus:
+ * @conn: pointer to the hypervisor connection
+ * @bus: bus name
+ *
+ * Provides the number of node devices on a given bus.
+ *
+ * Returns the number of node devices on the bus or -1 in case of error
+ */
+int
+virNodeNumOfDevicesByBus(virConnectPtr conn, const char *bus)
+{
+    DEBUG("conn=%p, bus=%p", conn, bus);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return (-1);
+    }
+
+    if (bus == NULL) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return (-1);
+    }
+
+    if (conn->nodeDriver && conn->nodeDriver->numOfDevicesByBus)
+        return conn->nodeDriver->numOfDevicesByBus (conn, bus);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+/**
+ * virNodeListDevicesByBus:
+ * @conn: pointer to the hypervisor connection
+ * @bus: bus name
+ * @names: array to collect the list of node device names
+ * @maxnames: size of @names
+ *
+ * Collect the list of node devices on the given bus, and store
+ * their names in @names
+ *
+ * Returns the number of node devices found on the bus or -1 in case of error
+ */
+int
+virNodeListDevicesByBus(virConnectPtr conn, const char *bus,
+                        char **const names, int maxnames)
+{
+    DEBUG("conn=%p, bus=%p, names=%p, maxnames=%d", conn, bus, names, maxnames);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return (-1);
+    }
+
+    if ((names == NULL) || (maxnames < 0) || (bus == NULL)) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return (-1);
+    }
+
+    if (conn->nodeDriver && conn->nodeDriver->listDevicesByBus)
+        return conn->nodeDriver->listDevicesByBus (conn, bus, names, maxnames);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+/**
+ * virNodeNumOfDevicesByCap:
+ * @conn: pointer to the hypervisor connection
+ * @cap: capability name
+ *
+ * Provides the number of node devices with the given capability.
+ *
+ * Returns the number of node devices with the capability or -1 in case of error
+ */
+int
+virNodeNumOfDevicesByCap(virConnectPtr conn, const char *cap)
+{
+    DEBUG("conn=%p, cap=%p", conn, cap);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return (-1);
+    }
+
+    if (cap == NULL) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return (-1);
+    }
+
+    if (conn->nodeDriver && conn->nodeDriver->numOfDevicesByCap)
+        return conn->nodeDriver->numOfDevicesByCap (conn, cap);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+/**
+ * virNodeListDevicesByCap:
+ * @conn: pointer to the hypervisor connection
+ * @cap: capability name
+ * @names: array to collect the list of node device names
+ * @maxnames: size of @names
+ *
+ * Collect the list of node devices with the given capability, and store
+ * their names in @names
+ *
+ * Returns the number of node devices found with the capability or -1 in case of error
+ */
+int
+virNodeListDevicesByCap(virConnectPtr conn, const char *cap,
+                        char **const names, int maxnames)
+{
+    DEBUG("conn=%p, cap=%p, names=%p, maxnames=%d", conn, cap, names, maxnames);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return (-1);
+    }
+
+    if ((names == NULL) || (maxnames < 0) || (cap == NULL)) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return (-1);
+    }
+
+    if (conn->nodeDriver && conn->nodeDriver->listDevicesByCap)
+        return conn->nodeDriver->listDevicesByCap (conn, cap, names, maxnames);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+/**
+ * virNodeDeviceLookupByName:
+ * @conn: pointer to the hypervisor connection
+ * @name: unique device name
+ *
+ * Lookup a node device by its name.
+ *
+ * Returns a virNodeDevicePtr if found, NULL otherwise.
+ */
+virNodeDevicePtr virNodeDeviceLookupByName(virConnectPtr conn, const char *name)
+{
+    DEBUG("conn=%p, name=%p", conn, name);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return NULL;
+    }
+
+    if (name == NULL) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return NULL;
+    }
+
+    if (conn->nodeDriver && conn->nodeDriver->deviceLookupByName)
+        return conn->nodeDriver->deviceLookupByName (conn, name);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return NULL;
+}
+
+
+/**
+ * virNodeDeviceLookupByKey:
+ * @conn: pointer to the hypervisor connection
+ * @key: unique device key
+ *
+ * Lookup a node device by its key.
+ *
+ * Returns a virNodeDevicePtr if found, NULL otherwise.
+ */
+virNodeDevicePtr virNodeDeviceLookupByKey(virConnectPtr conn, const char *key)
+{
+    DEBUG("conn=%p, key=%p", conn, key);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return NULL;
+    }
+
+    if (key == NULL) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return NULL;
+    }
+
+    if (conn->nodeDriver && conn->nodeDriver->deviceLookupByKey)
+        return conn->nodeDriver->deviceLookupByKey (conn, key);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return NULL;
+}
+
+
+/**
+ * virNodeDeviceGetXMLDesc:
+ * @dev: pointer to the node device
+ * @flags: flags for XML generation (unused, pass 0)
+ *
+ * Fetch an XML document describing all aspects of
+ * the device.
+ *
+ * Return the XML document, or NULL on error
+ */
+char *virNodeDeviceGetXMLDesc(virNodeDevicePtr dev, unsigned int flags)
+{
+    virConnectPtr conn;
+
+    DEBUG("dev=%p", dev);
+    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
+        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
+        return NULL;
+    }
+
+    conn = dev->conn;
+    DEBUG("conn=%p", conn);
+
+    if (conn->nodeDriver && conn->nodeDriver->deviceDumpXML)
+        return conn->nodeDriver->deviceDumpXML (dev, flags);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return NULL;
+}
+
+
+/**
+ * virNodeDeviceCreate:
+ * @conn: pointer to the hypervisor connection
+ * @xml: XML description for new device
+ *
+ * Creates a new node device, as described by the XML.
+ * [Need to describe what kinds of devices can be created this way]
+ *
+ * Returns a pointer to the new device, or NULL for error.
+ */
+virNodeDevicePtr virNodeDeviceCreate(virConnectPtr conn,
+                                     const char *xml)
+{
+    DEBUG("conn=%p, xml=%p", conn, xml);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return NULL;
+    }
+
+    if (xml == NULL) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return NULL;
+    }
+
+    if (conn->nodeDriver && conn->nodeDriver->deviceCreate)
+        return conn->nodeDriver->deviceCreate (conn, xml);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return NULL;
+}
+
+/**
+ * virNodeDeviceDestroy:
+ * @dev: pointer to the created device
+ *
+ * Removes a device created by virNodeDeviceCreate.
+ *
+ * Returns 0 for success, <0 for failure.
+ */
+int virNodeDeviceDestroy(virNodeDevicePtr dev)
+{
+    virConnectPtr conn;
+
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
+        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
+        return (-1);
+    }
+
+    conn = dev->conn;
+
+    if (conn->nodeDriver && conn->nodeDriver->deviceDestroy)
+        return conn->nodeDriver->deviceDestroy (dev);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+const char *virNodeDeviceGetKey(virNodeDevicePtr dev)
+{
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+    return dev->key;
+}
+
+const char *virNodeDeviceGetName(virNodeDevicePtr dev)
+{
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+    return dev->name;
+}
+
+const char *virNodeDeviceGetParentKey(virNodeDevicePtr dev)
+{
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+    return dev->parent_key;
+}
+
+const char *virNodeDeviceGetBus(virNodeDevicePtr dev)
+{
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+    return dev->bus_name;
+}
+
+int virNodeDeviceNumOfCaps(virNodeDevicePtr dev)
+{
+    int ncaps = 0;
+
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+    while (dev->cap_names && dev->cap_names[ncaps] != NULL)
+        ++ncaps;
+    return ncaps;
+}
+
+int virNodeDeviceListCaps(virNodeDevicePtr dev,
+                          char **const names,
+                          int maxnames)
+{
+    int n = 0;
+
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+    while (dev->cap_names && dev->cap_names[n] && n < maxnames) {
+        names[n] = dev->cap_names[n];
+        n++;
+    }
+    return n;
+}
+
+
+/**
+ * virNodeDeviceFree:
+ * @dev: pointer to the node device
+ *
+ * Drops a reference to the node device, freeing it if
+ * this was the last reference.
+ *
+ * Returns the 0 for success, -1 for error.
+ */
+int virNodeDeviceFree(virNodeDevicePtr dev)
+{
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+
+    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
+        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
+        return (-1);
+    }
+    if (virUnrefNodeDevice(dev) < 0)
+        return (-1);
+    return(0);
+}
+
+
 /* Not for public use.  Combines the elements of a virStringList
  * into a single string.
  */
diff --git a/src/libvirt_sym.version b/src/libvirt_sym.version
index b8c470c..e214560 100644
--- a/src/libvirt_sym.version
+++ b/src/libvirt_sym.version
@@ -146,6 +146,19 @@
 	virStorageVolGetXMLDesc;
 	virStorageVolGetPath;
 
+        virNodeNumOfDevices;
+        virNodeListDevices;
+        virNodeNumOfDevicesByCap;
+        virNodeListDevicesByCap;
+        virNodeNumOfDevicesByBus;
+        virNodeListDevicesByBus;
+        virNodeDeviceLookupByName;
+        virNodeDeviceLookupByKey;
+        virNodeDeviceFree;
+        virNodeDeviceGetXMLDesc;
+        virNodeDeviceCreate;
+        virNodeDeviceDestroy;
+
         /* Symbols with __ are private only
            for use by the libvirtd daemon.
            They are not part of stable ABI
@@ -165,6 +178,7 @@
 	__virGetNetwork;
 	__virGetStoragePool;
 	__virGetStorageVol;
+	__virGetNodeDevice;
 
 	__virEventRegisterImpl;
 
diff --git a/src/node_device.c b/src/node_device.c
new file mode 100644
index 0000000..1c9b1f8
--- /dev/null
+++ b/src/node_device.c
@@ -0,0 +1,262 @@
+/*
+ * node_device.c: node device enumeration
+ *
+ * Copyright (C) 2008 Virtual Iron Software, Inc.
+ * Copyright (C) 2008 David F. Lively
+ *
+ * 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: David F. Lively <dlively virtualiron com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "internal.h"
+#include "memory.h"
+
+
+struct _stringv {
+    int len;
+    char **const base;
+    const int maxlen;
+};
+
+typedef struct _stringv stringv;
+
+struct _cond_list_args {
+    int (*cond)(const virNodeDevicePtr dev, void *data);
+    void *data;
+    stringv outv;
+};
+
+typedef struct _cond_list_args cond_list_args;
+
+static void append_key(const void *_dev ATTRIBUTE_UNUSED,
+                       const char *key, const void *_outv)
+{
+    stringv *outv = (void *)_outv;
+    if (outv->len < outv->maxlen)
+        outv->base[outv->len++] = strdup(key);
+}
+
+static void cond_count(const void *_dev,
+                       const char *key ATTRIBUTE_UNUSED,
+                       const void *_args)
+{
+    cond_list_args *args = (void *)_args;
+
+    /* Increment args->outv.len when cond(dev, data) is true
+     */
+    if (args->cond((const virNodeDevicePtr)_dev, args->data))
+        ++args->outv.len;
+}
+
+static void cond_append_key(const void *_dev,
+                            const char *key,
+                            const void *_args)
+{
+    cond_list_args *args = (void *)_args;
+
+    /* Increment len and append key to names when cond(dev, data)
+     * is true and len doesn't exceed maxlen; avoid invoking cond()
+     * if we're already at maxlen.
+     */
+    if (args->outv.len < args->outv.maxlen &&
+        args->cond((const virNodeDevicePtr)_dev, args->data))
+        append_key(NULL, key, &args->outv);
+}
+
+
+static int device_on_bus(const virNodeDevicePtr dev, void *_bus)
+{
+    const char *bus = _bus;
+    return STREQ(dev->bus_name, bus);
+}
+
+static int device_has_cap(const virNodeDevicePtr dev, void *_cap)
+{
+    const char *cap = _cap;
+    int i;
+
+    for (i = 0; dev->cap_names[i]; i++)
+        if (STREQ(dev->cap_names[i], cap))
+            return 1;
+    return 0;
+}
+
+
+static int nodeNumOfDevices(virConnectPtr conn)
+{
+    return virHashSize(conn->nodeDevices);
+}
+
+static int
+nodeListDevices(virConnectPtr conn, char **const names, int maxnames)
+{
+    stringv outv = {
+        .base = names,
+        .maxlen = maxnames,
+        .len = 0
+    };
+
+    if (virHashForEach(conn->nodeDevices, append_key, &outv) < 0)
+        return -1;
+
+    return outv.len;
+}
+
+
+static int nodeNumOfDevicesByBus(virConnectPtr conn, const char *bus)
+{
+    cond_list_args args = { .cond = device_on_bus,
+                            .data = (void *)bus,
+                            /* outv.base/maxlen not used by cond_count */
+                            .outv = { .len = 0 } };
+    if (virHashForEach(conn->nodeDevices, cond_count, &args) < 0)
+        return -1;
+    return args.outv.len;
+}
+
+static int nodeListDevicesByBus(virConnectPtr conn,
+                                const char *bus,
+                                char **const names,
+                                int maxnames)
+{
+    cond_list_args args = { .cond = device_on_bus,
+                            .data = (void *)bus,
+                            .outv = { .len = 0,
+                                      .base = names,
+                                      .maxlen = maxnames } };
+    if (virHashForEach(conn->nodeDevices, cond_append_key, &args) < 0)
+        return -1;
+    return args.outv.len;
+}
+
+static int nodeNumOfDevicesByCap(virConnectPtr conn,
+                                 const char *cap)
+{
+    cond_list_args args = { .cond = device_has_cap,
+                            .data = (void *)cap,
+                            /* outv.base/maxlen not used by cond_count */
+                            .outv = { .len = 0 } };
+    if (virHashForEach(conn->nodeDevices, cond_count, &args) < 0)
+        return -1;
+    return args.outv.len;
+}
+
+static int nodeListDevicesByCap(virConnectPtr conn,
+                                const char *cap,
+                                char **const names,
+                                int maxnames)
+{
+    cond_list_args args = { .cond = device_has_cap,
+                            .data = (void *)cap,
+                            .outv = { .len = 0,
+                                      .base = names,
+                                      .maxlen = maxnames } };
+    if (virHashForEach(conn->nodeDevices, cond_append_key, &args) < 0)
+        return -1;
+    return args.outv.len;
+}
+
+static int device_has_name(const void *_dev,
+                           const char *key ATTRIBUTE_UNUSED,
+                           const void *_name)
+{
+    const virNodeDevice *dev = _dev;
+    const char *name = _name;
+    return STREQ(dev->name, name);
+}
+
+static virNodeDevicePtr nodeDeviceLookupByName(virConnectPtr conn,
+                                               const char *name)
+{
+    return (virNodeDevicePtr)virHashSearch(conn->nodeDevices,
+                                           device_has_name,
+                                           name);
+}
+
+static virNodeDevicePtr nodeDeviceLookupByKey(virConnectPtr conn,
+                                              const char *key)
+{
+    virNodeDevicePtr device;
+
+    pthread_mutex_lock(&conn->lock);
+    device = virHashLookup(conn->nodeDevices, key);
+    if (device)
+        device->refs++;
+    pthread_mutex_unlock(&conn->lock);
+    return device;
+}
+
+static char *nodeDeviceDumpXML(virNodeDevicePtr dev,
+                               unsigned int flags ATTRIBUTE_UNUSED)
+{
+    xmlBufferPtr buf = xmlBufferCreate();
+    char *xml;
+    int i;
+
+    if (buf == NULL)
+        return NULL;
+
+    xmlBufferCCat(buf, "<device>\n  <name>");
+    xmlBufferCCat(buf, dev->name);
+    xmlBufferCCat(buf, "</name>\n  <key>");
+    xmlBufferCCat(buf, dev->key);
+    xmlBufferCCat(buf, "</key>\n");
+    if (dev->parent_key) {
+        xmlBufferCCat(buf, "  <parent>");
+        xmlBufferCCat(buf, dev->parent_key);
+        xmlBufferCCat(buf, "</parent>\n");
+    }
+    if (dev->bus) {
+        xmlBufferCCat(buf, "  ");
+        xmlNodeDump(buf, NULL, dev->bus, 1, 1);
+        xmlBufferCCat(buf, "\n");
+    }
+    if (dev->caps) {
+        for (i = 0; dev->caps[i]; i++) {
+            if (dev->caps[i]->parent == NULL) {
+                xmlBufferCCat(buf, "  ");
+                xmlNodeDump(buf, NULL, dev->caps[i], 1, 1);
+                xmlBufferCCat(buf, "\n");
+            }
+        }
+    }
+    xmlBufferCCat(buf, "</device>\n");
+
+    /* TODO: Can we avoid this copy? */
+    xml = strdup((const char *)xmlBufferContent(buf));
+    xmlBufferFree(buf);
+
+    return xml;
+}
+
+
+void registerCommonNodeFuncs(virNodeDriverPtr driver)
+{
+    driver->numOfDevices = nodeNumOfDevices;
+    driver->listDevices = nodeListDevices;
+    driver->numOfDevicesByCap = nodeNumOfDevicesByCap;
+    driver->listDevicesByCap = nodeListDevicesByCap;
+    driver->numOfDevicesByBus = nodeNumOfDevicesByBus;
+    driver->listDevicesByBus = nodeListDevicesByBus;
+    driver->deviceLookupByName = nodeDeviceLookupByName;
+    driver->deviceLookupByKey = nodeDeviceLookupByKey;
+    driver->deviceDumpXML = nodeDeviceDumpXML;
+}
diff --git a/src/node_device.h b/src/node_device.h
new file mode 100644
index 0000000..1527014
--- /dev/null
+++ b/src/node_device.h
@@ -0,0 +1,38 @@
+/*
+ * node_device.h: node device enumeration
+ *
+ * Copyright (C) 2008 Virtual Iron Software, Inc.
+ * Copyright (C) 2008 David F. Lively
+ *
+ * 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: David F. Lively <dlively virtualiron com>
+ */
+
+#ifndef __VIR_NODE_DEVICE_H__
+#define __VIR_NODE_DEVICE_H__
+
+#include "libvirt/libvirt.h"
+
+void registerCommonNodeFuncs(virNodeDriverPtr driver);
+
+#ifdef HAVE_HAL
+virNodeDriver HALNodeDriver;
+#endif
+#ifdef HAVE_DEVKIT
+virNodeDriver DevkitNodeDriver;
+#endif
+
+#endif /* __VIR_NODE_DEVICE_H__ */
diff --git a/src/node_device_devkit.c b/src/node_device_devkit.c
new file mode 100644
index 0000000..87b857b
--- /dev/null
+++ b/src/node_device_devkit.c
@@ -0,0 +1,299 @@
+/*
+ * node_device_devkit.c: node device enumeration - DeviceKit-based implementation
+ *
+ * Copyright (C) 2008 Virtual Iron Software, Inc.
+ * Copyright (C) 2008 David F. Lively
+ *
+ * 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: David F. Lively <dlively virtualiron com>
+ */
+
+#include <config.h>
+#include <devkit-gobject.h>
+
+#include "internal.h"
+#include "memory.h"
+
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
+
+/* TODO: Refactor this file with node_device_hal.c.
+ */
+
+static int gather_capabilities(DevkitDevice *dkdev,
+                               char ***cap_names_p,
+                               xmlNodePtr **caps_p)
+{
+    const char *subsys = devkit_device_get_subsystem(dkdev);
+    const char *device_file = devkit_device_get_device_file(dkdev);
+    int rv = ENOMEM;
+    char **cap_names = NULL;
+    xmlNodePtr *caps = NULL;
+    xmlNodePtr child = NULL;
+    int i, num_caps = STREQ(subsys, "block") ? 2 : 1;
+
+    if (VIR_ALLOC_N(cap_names, num_caps + 1) < 0 ||
+        VIR_ALLOC_N(caps, num_caps + 1) < 0)
+        goto failure;
+
+    caps[0] = xmlNewNode(NULL, BAD_CAST "capability");
+    if (caps[0] == NULL) goto failure;
+    if (xmlSetProp(caps[0], BAD_CAST "type", BAD_CAST subsys) == NULL)
+        goto failure;
+
+    if (device_file && *device_file) {
+        child = xmlNewNode(NULL, BAD_CAST "device");
+        if (child == NULL) goto failure;
+        xmlNodeSetContent(child, BAD_CAST device_file);
+        if (xmlAddChild(caps[0], child) == NULL) goto failure;
+        child = NULL;
+    }
+
+    if (STREQ(subsys, "block")) {
+        const char *id_type = devkit_device_get_property(dkdev, "ID_TYPE");
+        if (id_type && *id_type) {
+            caps[1] = xmlNewNode(NULL, BAD_CAST "storage");
+            if (caps[1] == NULL) goto failure;
+            child = xmlNewNode(NULL, BAD_CAST "drive_type");
+            if (child == NULL) goto failure;
+            xmlNodeSetContent(child, BAD_CAST id_type);
+            if (xmlAddChild(caps[1], child) == NULL) goto failure;
+        }
+    } else if (STREQ(subsys, "input")) {
+        const char *id_class = devkit_device_get_property(dkdev, "ID_CLASS");
+        if (id_class && *id_class) {
+            if (STREQ(id_class, "kbd"))
+                id_class = "keyboard";
+            child = xmlNewNode(NULL, BAD_CAST id_class);
+            if (child == NULL) goto failure;
+            if (xmlAddChild(caps[0], child) == NULL) goto failure;
+        }
+    }
+
+    *cap_names_p = cap_names;
+    *caps_p = caps;
+    return 0;
+
+ failure:
+    if (cap_names)
+        for (i = 0; cap_names[i]; i++)
+            VIR_FREE(cap_names[i]);
+    VIR_FREE(cap_names);
+    if (caps)
+        for (i = 0; caps[i]; i++)
+            xmlFreeNode(caps[i]);
+    VIR_FREE(caps);
+    xmlFreeNode(child);
+    return rv;
+
+}
+
+
+static int gather_bus_info(DevkitDevice *dkdev,
+                           const char *bus_name,
+                           xmlNodePtr *bus_p)
+{
+    int rv = ENOMEM;
+    const char *vendor = devkit_device_get_property(dkdev, "ID_VENDOR");
+    xmlNodePtr bus = NULL;
+    xmlNodePtr child = NULL;
+
+    bus = xmlNewNode(NULL, BAD_CAST "bus");
+    if (bus == NULL) goto failure;
+
+    if (xmlSetProp(bus, BAD_CAST "type", BAD_CAST bus_name) == NULL)
+        goto failure;
+
+    if (vendor) {
+        child = xmlNewNode(NULL, BAD_CAST "vendor");
+        if (child == NULL) goto failure;
+        if (xmlAddChild(bus, child) == NULL) goto failure;
+        xmlNodeSetContent(child, BAD_CAST vendor);
+    }
+
+    *bus_p = bus;
+    return 0;
+
+ failure:
+    xmlFreeNode(bus);
+    xmlFreeNode(child);
+    return rv;
+}
+
+
+static void dev_create(void *_dkdev, void *_conn)
+{
+    virConnectPtr conn = _conn;
+    DevkitDevice *dkdev = _dkdev;
+    const char *key = devkit_device_get_native_path(dkdev);
+    const char *bus_name = devkit_device_get_property(dkdev, "ID_BUS");
+    const char *name;
+    char *parent_key = NULL;
+    xmlNodePtr bus = NULL;
+    char **cap_names = NULL;
+    xmlNodePtr *caps = NULL;
+    virNodeDevicePtr dev = NULL;
+    int rv, i;
+
+    if (key == NULL)
+        key = devkit_device_get_device_file(dkdev);
+    if (key == NULL) {
+        DEBUG("no native path or device file for dev %p", dkdev);
+        return;
+    }
+
+    name = strrchr(key, '/');
+    if (name == NULL) {
+        name = key;
+    } else {
+        parent_key = strndup(key, name - key);
+        ++name;
+    }
+
+    if (bus_name) {
+        rv = gather_bus_info(dkdev, bus_name, &bus);
+        if (rv != 0) {
+            DEBUG("gather_bus_info failed (%d) for dev %s", rv, key);
+            goto failure;
+        }
+    }
+
+    rv = gather_capabilities(dkdev, &cap_names, &caps);
+    if (rv != 0 && rv != ENOENT) {
+        DEBUG("gather_capabilities failed (%d) for dev %s", rv, key);
+        goto failure;
+    }
+
+    dev = virGetNodeDevice(conn, key, name, parent_key,
+                           bus_name, bus,
+                           cap_names, caps);
+    if (dev == NULL) {
+        DEBUG("virGetNodeDevice failed for dev %s", key);
+        goto failure;
+    }
+    VIR_FREE(bus_name);
+    VIR_FREE(parent_key);
+
+    return;
+
+ failure:
+    xmlFreeNode(bus);
+    VIR_FREE(bus_name);
+    VIR_FREE(parent_key);
+    if (cap_names) {
+        for (i = 0; cap_names[i]; i++)
+            VIR_FREE(cap_names[i]);
+        VIR_FREE(cap_names);
+    }
+    if (caps) {
+        for (i = 0; caps[i]; i++)
+            xmlFreeNode(caps[i]);
+        VIR_FREE(caps);
+    }
+    if (dev)
+        virNodeDeviceFree(dev);
+}
+
+/*
+ * Host device enumeration (Devkit implementation)
+ */
+
+#define CONN_DEVKIT_CLIENT(conn) ((DevkitClient *)((conn)->nodePrivateData))
+
+static virDrvOpenStatus DevkitNodeDrvOpen(virConnectPtr conn,
+                                          xmlURIPtr uri ATTRIBUTE_UNUSED,
+                                          virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                                          int flags ATTRIBUTE_UNUSED)
+{
+    DevkitClient *devkit_client = NULL;
+    GError *err = NULL;
+    GList *devs;
+
+    // TODO: Is it really ok to call this multiple times??
+    //       Is there something analogous to call on close?
+    g_type_init();
+
+    /* Get new devkit_client and connect to daemon */
+    devkit_client = devkit_client_new(NULL);
+    if (devkit_client == NULL) {
+        VIR_DEBUG(__FILE__, "%s", "devkit_client_new returned NULL");
+        goto open_failure;
+    }
+    conn->nodePrivateData = devkit_client;
+    if (!devkit_client_connect(devkit_client, &err)) {
+        VIR_DEBUG(__FILE__, "%s", "devkit_client_connect failed");
+        goto open_failure;
+    }
+
+    /* Populate with known devices */
+    devs = devkit_client_enumerate_by_subsystem(devkit_client, NULL, &err);
+    if (err) {
+        VIR_DEBUG(__FILE__, "%s", "devkit_client_enumerate_by_subsystem failed");
+        devs = NULL;
+        goto open_failure;
+    }
+    g_list_foreach(devs, dev_create, conn);
+
+    // TODO: register to get DeviceKit events on device changes
+    //       and coordinate with this initial scan and subsequent
+    //       queries and other operations
+
+    return VIR_DRV_OPEN_SUCCESS;
+
+ open_failure:
+    if (err) {
+        VIR_DEBUG(__FILE__, "\terror[%d]: %s", err->code, err->message);
+        g_error_free(err);
+    }
+    if (devs) {
+        g_list_foreach(devs, (GFunc)g_object_unref, NULL);
+        g_list_free(devs);
+    }
+    if (devkit_client)
+        g_object_unref(devkit_client);
+
+    return VIR_DRV_OPEN_ERROR;
+
+}
+
+static int DevkitNodeDrvClose(virConnectPtr conn)
+{
+    DevkitClient *devkit_client = CONN_DEVKIT_CLIENT(conn);
+
+    if (devkit_client)
+        g_object_unref(devkit_client);
+
+    return 0;
+}
+
+static virNodeDevicePtr DevkitNodeDeviceCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                               const char *xml ATTRIBUTE_UNUSED)
+{
+    return 0;
+}
+
+static int DevkitNodeDeviceDestroy(virNodeDevicePtr dev ATTRIBUTE_UNUSED)
+{
+    return 0;
+}
+
+virNodeDriver DevkitNodeDriver = {
+    .name = "DevkitNodeDriver",
+    .open = DevkitNodeDrvOpen,
+    .close = DevkitNodeDrvClose,
+    .deviceCreate = DevkitNodeDeviceCreate,
+    .deviceDestroy = DevkitNodeDeviceDestroy,
+};
diff --git a/src/node_device_hal.c b/src/node_device_hal.c
new file mode 100644
index 0000000..bfcd871
--- /dev/null
+++ b/src/node_device_hal.c
@@ -0,0 +1,475 @@
+/*
+ * node_device_hal.c: node device enumeration - HAL-based implementation
+ *
+ * Copyright (C) 2008 Virtual Iron Software, Inc.
+ * Copyright (C) 2008 David F. Lively
+ *
+ * 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: David F. Lively <dlively virtualiron com>
+ */
+
+#include <config.h>
+#include <libhal.h>
+
+#include "internal.h"
+#include "buf.h"
+#include "memory.h"
+
+/*
+ * Host device enumeration (HAL implementation)
+ */
+
+#define CONN_HAL_CTX(conn) ((LibHalContext *)((conn)->nodePrivateData))
+
+static char *dev_get_prop(LibHalContext *ctxt, const char *udi,
+                          const char *prop_fmt, ...)
+{
+    char *prop = NULL;
+    char *value = NULL;
+    va_list args;
+
+    va_start(args, prop_fmt);
+    if (vasprintf(&prop, prop_fmt, args) <= 0)
+        return NULL;
+    value = libhal_device_get_property_string(ctxt, udi, prop, NULL);
+    va_end(args);
+    VIR_FREE(prop);
+
+    return value;
+}
+
+
+static char *dev_get_int_prop(LibHalContext *ctxt, const char *udi,
+                              const char *prop_fmt, ...)
+{
+    char *prop = NULL;
+    char *value = NULL;
+    int intval;
+    va_list args;
+    DBusError err;
+
+    va_start(args, prop_fmt);
+    if (vasprintf(&prop, prop_fmt, args) <= 0)
+        return NULL;
+    dbus_error_init(&err);
+    intval = libhal_device_get_property_int(ctxt, udi, prop, &err);
+    va_end(args);
+    VIR_FREE(prop);
+    if (dbus_error_is_set(&err)) {
+        dbus_error_free(&err);
+        return NULL;
+    }
+    if (asprintf(&value, "%d", intval) < 0)
+        return NULL;
+
+    return value;
+}
+
+
+struct _caps_tbl_entry {
+    const char *cap_name;
+    int (*gather_fn)(LibHalContext *ctx, const char *udi,
+                     const char *cap_name, xmlNodePtr cap,
+                     void *data);
+    void *data;
+};
+
+typedef struct _caps_tbl_entry caps_tbl_entry;
+
+
+static int gather_cap_elements(LibHalContext *ctx, const char *udi,
+                               const char *cap_name, xmlNodePtr cap,
+                               void *_elt_names)
+{
+    const char **elt_names = _elt_names;
+    char *value = NULL;
+    int rv = ENOMEM;
+    int i;
+
+    if (elt_names == NULL)
+        return 0;
+
+    for (i = 0; elt_names[i]; i++) {
+        value = dev_get_prop(ctx, udi, "%s.%s", cap_name, elt_names[i]);
+        if (value) {
+            xmlNodePtr elt = xmlNewNode(NULL, BAD_CAST elt_names[i]);
+            if (elt == NULL) goto failure;
+            xmlNodeSetContent(elt, BAD_CAST value);
+            VIR_FREE(value);
+            if (xmlAddChild(cap, elt) == NULL) {
+                xmlFreeNode(elt);
+                goto failure;
+            }
+        }
+    }
+
+    return 0;
+
+ failure:
+    VIR_FREE(value);
+    return rv;
+}
+
+static const char *block_elts[] = { "device", NULL };
+static const char *storage_elts[] = { "drive_type", NULL };
+
+static caps_tbl_entry caps_tbl[] = {
+    /* Table of allowed capabilities.  A NULL in the second field
+     * means we'll relay the presence of the capability, but nothing
+     * else (e.g., "<capability type='wake_on_lan' />".  Otherwise
+     * the field identifies a caps_gather_fn called to gather the other
+     * capability details, and the third field an opaque data pointer
+     * (void *) to pass arbitrary gather_fn-specific data.
+     */
+    { "block", gather_cap_elements, &block_elts },
+    { "button", NULL, NULL },
+    { "cpufreq_control", NULL, NULL },
+    { "ieee1394", NULL, NULL },
+    { "input", NULL, NULL },
+    { "input.joystick", NULL, NULL },
+    { "input.keyboard", NULL, NULL },
+    { "input.keypad", NULL, NULL },
+    { "input.keys", NULL, NULL },
+    { "input.mouse", NULL, NULL },
+    { "input.tablet", NULL, NULL },
+    { "net", NULL, NULL },
+    { "net.80203", NULL, NULL },
+    { "net.80211", NULL, NULL },
+    { "processor", NULL, NULL },
+    { "sound", NULL, NULL },
+    { "storage", gather_cap_elements, &storage_elts },
+    { "storage.cdrom", NULL, NULL },
+    { "storage.raid", NULL, NULL },
+    { "scsi_generic", NULL, NULL },
+    { "scsi_host", NULL, NULL },
+    { "scsi_host.vport", NULL, NULL },
+    { "serial", NULL, NULL },
+    { "wake_on_lan", NULL, NULL } };
+static int caps_tbl_sorted = 0;
+
+/* qsort comparator */
+static int cmpstringp(const void *p1, const void *p2)
+{
+    /* from man 3 qsort */
+    return strcmp(* (char * const *) p1, * (char * const *) p2);
+}
+
+static int gather_capabilities(LibHalContext *ctx, const char *udi,
+                               char ***cap_names_p, xmlNodePtr **caps_p)
+{
+    size_t caps_tbl_len = sizeof(caps_tbl) / sizeof(caps_tbl[0]);
+    int rv = ENOMEM;
+    char **hal_cap_names = NULL;
+    char **cap_names = NULL;
+    xmlNodePtr *caps = NULL;
+    int num_hal_caps, num_caps, i, j;
+    const caps_tbl_entry *entry;
+    const char *last_dot;
+
+    if (! caps_tbl_sorted) {
+        qsort(caps_tbl, caps_tbl_len, sizeof(caps_tbl[0]), cmpstringp);
+        caps_tbl_sorted = 1;
+    }
+    hal_cap_names = libhal_device_get_property_strlist(ctx, udi,
+                                                       "info.capabilities",
+                                                       NULL);
+    if (hal_cap_names == NULL)
+        return ENOENT;
+
+    num_caps = num_hal_caps = 0;
+    while (hal_cap_names[num_hal_caps]) {
+        entry = bsearch(&hal_cap_names[num_hal_caps], caps_tbl,
+                        caps_tbl_len, sizeof(caps_tbl[0]),
+                        cmpstringp);
+        if (entry == NULL)
+            VIR_FREE(hal_cap_names[num_hal_caps]);
+        else
+            ++num_caps;
+        ++num_hal_caps;
+    }
+
+    if (VIR_ALLOC_N(cap_names, num_caps + 1) < 0 ||
+        VIR_ALLOC_N(caps, num_caps + 1) < 0)
+        goto failure;
+
+    for (i = 0, j = 0; j < num_caps && i < num_hal_caps; i++) {
+        if (hal_cap_names[i] == NULL)
+            continue;
+        cap_names[j] = hal_cap_names[i];
+        last_dot = strrchr(cap_names[j], '.');
+        caps[j] = xmlNewNode(NULL, BAD_CAST "capability");
+        if (caps[j] == NULL) goto failure;
+        if (xmlSetProp(caps[j], BAD_CAST "type",BAD_CAST cap_names[j]) == NULL)
+            goto failure;
+        if (last_dot) {
+            /* Connect to parent capability node */
+            int k;
+            for (k = j - 1; k >= 0; k--)
+                if (strncmp(cap_names[j], cap_names[k],
+                            last_dot - cap_names[j]) == 0)
+                    if (xmlAddChild(caps[k], caps[j]) == NULL)
+                        goto failure;
+        }
+        j++;
+    }
+    VIR_FREE(hal_cap_names);
+
+    for (i = 0; i < num_caps; i++) {
+        entry = bsearch(&cap_names[i], caps_tbl,
+                        caps_tbl_len, sizeof(caps_tbl[0]),
+                        cmpstringp);
+        if (entry->gather_fn) {
+            rv = entry->gather_fn(ctx, udi, cap_names[i], caps[i],
+                                  entry->data);
+            if (rv != 0) goto failure;
+        }
+    }
+
+    *cap_names_p = cap_names;
+    *caps_p = caps;
+    return 0;
+
+ failure:
+    if (hal_cap_names)
+        for (i = 0; hal_cap_names[i]; i++)
+            VIR_FREE(hal_cap_names[i]);
+    VIR_FREE(hal_cap_names);
+    if (cap_names)
+        for (i = 0; cap_names[i]; i++)
+            VIR_FREE(cap_names[i]);
+    VIR_FREE(cap_names);
+    if (caps)
+        for (i = 0; caps[i]; i++)
+            xmlFreeNode(caps[i]);
+    VIR_FREE(caps);
+    return rv;
+}
+
+static int gather_bus_info(LibHalContext *ctx ATTRIBUTE_UNUSED,
+                           const char *udi, const char *bus_name,
+                           xmlNodePtr *bus_p)
+{
+    int rv = ENOMEM;
+    char *vendor = NULL;
+    char *vendor_id = NULL;
+    char *product = NULL;
+    char *product_id = NULL;
+    xmlNodePtr bus = NULL;
+
+    bus = xmlNewNode(NULL, BAD_CAST "bus");
+    if (bus == NULL) goto failure;
+
+    if (xmlSetProp(bus, BAD_CAST "type", BAD_CAST bus_name) == NULL)
+        goto failure;
+
+    vendor = dev_get_prop(ctx, udi, "%s.vendor", bus_name);
+    if (vendor == NULL)
+        vendor = dev_get_prop(ctx, udi, "info.vendor");
+    if (vendor) {
+        xmlNodePtr child = xmlNewNode(NULL, BAD_CAST "vendor");
+        if (child == NULL) goto failure;
+        if (xmlAddChild(bus, child) == NULL) goto failure;
+        xmlNodeSetContent(child, BAD_CAST vendor);
+        VIR_FREE(vendor);
+        vendor_id = dev_get_int_prop(ctx, udi, "%s.vendor_id", bus_name);
+        if (vendor_id) {
+            if (xmlSetProp(child, BAD_CAST "id", BAD_CAST vendor_id) == NULL)
+                goto failure;
+            VIR_FREE(vendor_id);
+        }
+    }
+
+    product = dev_get_prop(ctx, udi, "%s.product", bus_name);
+    if (product == NULL)
+        product = dev_get_prop(ctx, udi, "info.product");
+    if (product) {
+        xmlNodePtr child = xmlNewNode(NULL, BAD_CAST "product");
+        if (child == NULL) goto failure;
+        if (xmlAddChild(bus, child) == NULL) goto failure;
+        xmlNodeSetContent(child, BAD_CAST product);
+        VIR_FREE(product);
+        product_id = dev_get_int_prop(ctx, udi, "%s.product_id", bus_name);
+        if (product_id) {
+            if (xmlSetProp(child, BAD_CAST "id", BAD_CAST product_id) == NULL)
+                goto failure;
+            VIR_FREE(product_id);
+        }
+    }
+
+    *bus_p = bus;
+    return 0;
+
+ failure:
+    VIR_FREE(vendor);
+    VIR_FREE(vendor_id);
+    VIR_FREE(product);
+    VIR_FREE(product_id);
+    xmlFreeNode(bus);
+    return rv;
+}
+
+
+static void dev_create(virConnectPtr conn, const char *udi)
+{
+    LibHalContext *ctx = CONN_HAL_CTX(conn);
+    char *bus_name = dev_get_prop(ctx, udi, "info.subsystem");
+    char *parent_key = dev_get_prop(ctx, udi, "info.parent");
+    xmlNodePtr bus = NULL;
+    char **cap_names = NULL;
+    xmlNodePtr *caps = NULL;
+    const char *name;
+    virNodeDevicePtr dev;
+    int rv, i;
+
+    name = strrchr(udi, '/');
+    if (name == NULL)
+        name = udi;
+    else
+        ++name;
+
+    if (bus_name) {
+        rv = gather_bus_info(ctx, udi, bus_name, &bus);
+        if (rv != 0) goto failure;
+    }
+
+    rv = gather_capabilities(ctx, udi, &cap_names, &caps);
+    if (rv != 0 && rv != ENOENT) goto failure;
+
+    dev = virGetNodeDevice(conn, udi, name, parent_key,
+                           bus_name, bus,
+                           cap_names, caps);
+    if (dev == NULL) goto failure;
+    VIR_FREE(bus_name);
+    VIR_FREE(parent_key);
+
+    return;
+
+ failure:
+    xmlFreeNode(bus);
+    VIR_FREE(bus_name);
+    VIR_FREE(parent_key);
+    if (cap_names) {
+        for (i = 0; cap_names[i]; i++)
+            VIR_FREE(cap_names[i]);
+        VIR_FREE(cap_names);
+    }
+    if (caps) {
+        for (i = 0; caps[i]; i++)
+            xmlFreeNode(caps[i]);
+        VIR_FREE(caps);
+    }
+    if (dev)
+        virNodeDeviceFree(dev);
+
+    VIR_DEBUG(__FILE__, "FAILED TO ADD dev %s\n", udi);
+}
+
+static virDrvOpenStatus HALNodeDrvOpen(virConnectPtr conn,
+                                       xmlURIPtr uri ATTRIBUTE_UNUSED,
+                                       virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                                       int flags ATTRIBUTE_UNUSED)
+{
+    LibHalContext *hal_ctx = NULL;
+    DBusConnection *dbus_conn = NULL;
+    DBusError err;
+    char **udi = NULL;
+    int num_devs, i;
+
+    /* Allocate and initialize a new HAL context */
+    dbus_error_init(&err);
+    hal_ctx = libhal_ctx_new();
+    if (hal_ctx == NULL) {
+        fprintf(stderr, "%s: libhal_ctx_new returned NULL\n", __FUNCTION__);
+        goto open_failure;
+    }
+    conn->nodePrivateData = hal_ctx;
+    dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+    if (dbus_conn == NULL) {
+        fprintf(stderr, "%s: dbus_bus_get failed\n", __FUNCTION__);
+        goto open_failure;
+    }
+    if (!libhal_ctx_set_dbus_connection(hal_ctx, dbus_conn)) {
+        fprintf(stderr, "%s: libhal_ctx_set_dbus_connection failed\n",
+                __FUNCTION__);
+        goto open_failure;
+    }
+    if (!libhal_ctx_init(hal_ctx, &err)) {
+        fprintf(stderr, "%s: libhal_ctx_init failed\n", __FUNCTION__);
+        goto open_failure;
+    }
+
+    /* Populate with known devices */
+    udi = libhal_get_all_devices(hal_ctx, &num_devs, &err);
+    if (udi == NULL) {
+        fprintf(stderr, "%s: libhal_get_all_devices failed\n", __FUNCTION__);
+        goto open_failure;
+    }
+    for (i = 0; i < num_devs; i++) {
+        dev_create(conn, udi[i]);
+        free(udi[i]);
+        udi[i] = NULL;
+    }
+    free(udi);
+
+    // TODO: register to get HAL notifications on device changes
+    //       and coordinate with this initial scan and subsequent
+    //       queries and other operations
+
+    return VIR_DRV_OPEN_SUCCESS;
+
+ open_failure:
+    if (dbus_error_is_set(&err)) {
+        fprintf(stderr, "\t%s: %s\n", err.name, err.message);
+        dbus_error_free(&err);
+    }
+    // TODO: unref all existing node devices in conn->devices
+    if (hal_ctx)
+        (void)libhal_ctx_free(hal_ctx);
+    if (udi) {
+        for (i = 0; i < num_devs; i++)
+            free(udi[i]);
+        free(udi);
+    }
+
+    return VIR_DRV_OPEN_ERROR;
+
+}
+
+static int HALNodeDrvClose(virConnectPtr conn)
+{
+    // TODO: unref all existing node devices in conn->devices
+    return ! libhal_ctx_free(CONN_HAL_CTX(conn));
+}
+
+
+static virNodeDevicePtr HALNodeDeviceCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                     const char *xml ATTRIBUTE_UNUSED)
+{
+    return NULL;
+}
+
+static int HALNodeDeviceDestroy(virNodeDevicePtr dev ATTRIBUTE_UNUSED)
+{
+    return -1;
+}
+
+virNodeDriver HALNodeDriver = {
+    .name = "HALNodeDriver",
+    .open = HALNodeDrvOpen,
+    .close = HALNodeDrvClose,
+    .deviceCreate = HALNodeDeviceCreate,
+    .deviceDestroy = HALNodeDeviceDestroy,
+};
diff --git a/src/remote_internal.c b/src/remote_internal.c
index 66de9d5..ceddad1 100644
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -126,6 +126,14 @@ struct private_data {
         return (retcode);                                               \
     }
 
+#define GET_NODE_PRIVATE(conn,retcode)                               \
+    struct private_data *priv = (struct private_data *) (conn)->nodePrivateData; \
+    if (!priv || priv->magic != MAGIC) {                                \
+        error (conn, VIR_ERR_INVALID_ARG,                               \
+               _("tried to use a closed or uninitialised handle"));     \
+        return (retcode);                                               \
+    }
+
 
 enum {
     REMOTE_CALL_IN_OPEN = 1,
@@ -155,6 +163,7 @@ static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domai
 static virNetworkPtr get_nonnull_network (virConnectPtr conn, remote_nonnull_network network);
 static virStoragePoolPtr get_nonnull_storage_pool (virConnectPtr conn, remote_nonnull_storage_pool pool);
 static virStorageVolPtr get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol);
+static virNodeDevicePtr get_nonnull_node_device (virConnectPtr conn, remote_nonnull_node_device dev);
 static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src);
 static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src);
 static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src);
@@ -3583,6 +3592,326 @@ remoteStorageVolGetPath (virStorageVolPtr vol)
 
 /*----------------------------------------------------------------------*/
 
+static virDrvOpenStatus remoteNodeDrvOpen(virConnectPtr conn,
+                                          xmlURIPtr uri ATTRIBUTE_UNUSED,
+                                          virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                                          int flags ATTRIBUTE_UNUSED)
+{
+    if (conn &&
+        conn->driver &&
+        STREQ (conn->driver->name, "remote")) {
+        /* If we're here, the remote driver is already
+         * in use due to a) a QEMU uri, or b) a remote
+         * URI. So we can re-use existing connection
+         */
+        conn->nodePrivateData = conn->privateData;
+        return VIR_DRV_OPEN_SUCCESS;
+    }
+
+    /* Decline open.  Will fallback to appropriate local node driver. */
+    // TODO: Why doesn't remoteStorageOpen do the same??
+    // TODO: Why doesn't remoteNetworkOpen do the same??
+    return VIR_DRV_OPEN_DECLINED;
+}
+
+static int remoteNodeDrvClose(virConnectPtr conn)
+{
+    int ret = 0;
+    GET_NODE_PRIVATE (conn, -1);
+    if (priv->localUses) {
+        priv->localUses--;
+        if (!priv->localUses) {
+            ret = doRemoteClose(conn, priv);
+            VIR_FREE(priv);
+            conn->nodePrivateData = NULL;
+        }
+    }
+    return ret;
+}
+
+static int remoteNodeNumOfDevices(virConnectPtr conn)
+{
+    remote_node_num_of_devices_ret ret;
+    GET_STORAGE_PRIVATE (conn, -1);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_NUM_OF_DEVICES,
+              (xdrproc_t) xdr_void, (char *) NULL,
+              (xdrproc_t) xdr_remote_node_num_of_devices_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+
+static int remoteNodeListDevices(virConnectPtr conn,
+                                 char **const names,
+                                 int maxnames)
+{
+    int i;
+    remote_node_list_devices_args args;
+    remote_node_list_devices_ret ret;
+    GET_STORAGE_PRIVATE (conn, -1);
+
+    if (maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        error (conn, VIR_ERR_RPC, _("too many device names requested"));
+        return -1;
+    }
+    args.maxnames = maxnames;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_LIST_DEVICES,
+              (xdrproc_t) xdr_remote_node_list_devices_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.names.names_len > maxnames) {
+        error (conn, VIR_ERR_RPC, _("too many device names received"));
+        xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret);
+        return -1;
+    }
+
+    /* This call is caller-frees (although that isn't clear from
+     * the documentation).  However xdr_free will free up both the
+     * names and the list of pointers, so we have to strdup the
+     * names here.
+     */
+    for (i = 0; i < ret.names.names_len; ++i)
+        names[i] = strdup (ret.names.names_val[i]);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret);
+
+    return ret.names.names_len;
+}
+
+
+static int remoteNodeNumOfDevicesByBus(virConnectPtr conn,
+                                       const char *bus)
+{
+    remote_node_num_of_devices_by_bus_args args;
+    remote_node_num_of_devices_by_bus_ret ret;
+    GET_STORAGE_PRIVATE (conn, -1);
+
+    args.bus = (char *)bus;
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_NUM_OF_DEVICES_BY_BUS,
+              (xdrproc_t) xdr_remote_node_num_of_devices_by_bus_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_num_of_devices_by_bus_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+
+static int remoteNodeListDevicesByBus(virConnectPtr conn,
+                                      const char *bus,
+                                      char **const names,
+                                      int maxnames)
+{
+    int i;
+    remote_node_list_devices_by_bus_args args;
+    remote_node_list_devices_by_bus_ret ret;
+    GET_STORAGE_PRIVATE (conn, -1);
+
+    if (maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        error (conn, VIR_ERR_RPC, _("too many device names requested"));
+        return -1;
+    }
+    args.maxnames = maxnames;
+    args.bus = (char *)bus;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_LIST_DEVICES_BY_BUS,
+              (xdrproc_t) xdr_remote_node_list_devices_by_bus_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_list_devices_by_bus_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.names.names_len > maxnames) {
+        error (conn, VIR_ERR_RPC, _("too many device names received"));
+        xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret);
+        return -1;
+    }
+
+    /* This call is caller-frees (although that isn't clear from
+     * the documentation).  However xdr_free will free up both the
+     * names and the list of pointers, so we have to strdup the
+     * names here.
+     */
+    for (i = 0; i < ret.names.names_len; ++i)
+        names[i] = strdup (ret.names.names_val[i]);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_list_devices_by_bus_ret, (char *) &ret);
+
+    return ret.names.names_len;
+}
+
+
+static int remoteNodeNumOfDevicesByCap(virConnectPtr conn,
+                                       const char *cap)
+{
+    remote_node_num_of_devices_by_cap_args args;
+    remote_node_num_of_devices_by_cap_ret ret;
+    GET_STORAGE_PRIVATE (conn, -1);
+
+    args.cap = (char *)cap;
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_NUM_OF_DEVICES_BY_CAP,
+              (xdrproc_t) xdr_remote_node_num_of_devices_by_cap_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_num_of_devices_by_cap_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+static int remoteNodeListDevicesByCap(virConnectPtr conn,
+                                      const char *cap,
+                                      char **const names,
+                                      int maxnames)
+{
+    int i;
+    remote_node_list_devices_by_cap_args args;
+    remote_node_list_devices_by_cap_ret ret;
+    GET_STORAGE_PRIVATE (conn, -1);
+
+    if (maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        error (conn, VIR_ERR_RPC, _("too many device names requested"));
+        return -1;
+    }
+    args.maxnames = maxnames;
+    args.cap = (char *)cap;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_LIST_DEVICES_BY_CAP,
+              (xdrproc_t) xdr_remote_node_list_devices_by_cap_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_list_devices_by_cap_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.names.names_len > maxnames) {
+        error (conn, VIR_ERR_RPC, _("too many device names received"));
+        xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret);
+        return -1;
+    }
+
+    /* This call is caller-frees (although that isn't clear from
+     * the documentation).  However xdr_free will free up both the
+     * names and the list of pointers, so we have to strdup the
+     * names here.
+     */
+    for (i = 0; i < ret.names.names_len; ++i)
+        names[i] = strdup (ret.names.names_val[i]);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_list_devices_by_cap_ret, (char *) &ret);
+
+    return ret.names.names_len;
+}
+
+static virNodeDevicePtr remoteNodeDeviceLookupByKey(virConnectPtr conn,
+                                                    const char *key)
+{
+    remote_node_device_lookup_by_key_args args;
+    remote_node_device_lookup_by_key_ret ret;
+    virNodeDevicePtr dev;
+    GET_STORAGE_PRIVATE (conn, NULL);
+
+    args.key = (char *)key;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_KEY,
+              (xdrproc_t) xdr_remote_node_device_lookup_by_key_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_device_lookup_by_key_ret, (char *) &ret) == -1)
+        return NULL;
+
+    dev = get_nonnull_node_device(conn, ret.dev);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_device_lookup_by_key_ret, (char *) &ret);
+
+    return dev;
+}
+
+static virNodeDevicePtr remoteNodeDeviceLookupByName(virConnectPtr conn,
+                                                    const char *name)
+{
+    remote_node_device_lookup_by_name_args args;
+    remote_node_device_lookup_by_name_ret ret;
+    virNodeDevicePtr dev;
+    GET_STORAGE_PRIVATE (conn, NULL);
+
+    args.name = (char *)name;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_NAME,
+              (xdrproc_t) xdr_remote_node_device_lookup_by_name_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_device_lookup_by_name_ret, (char *) &ret) == -1)
+        return NULL;
+
+    dev = get_nonnull_node_device(conn, ret.dev);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_device_lookup_by_name_ret, (char *) &ret);
+
+    return dev;
+}
+
+static char *remoteNodeDeviceDumpXML(virNodeDevicePtr dev,
+                                     unsigned int flags)
+{
+    remote_node_device_dump_xml_args args;
+    remote_node_device_dump_xml_ret ret;
+    GET_STORAGE_PRIVATE (dev->conn, NULL);
+
+    args.key = dev->key;
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_DUMP_XML,
+              (xdrproc_t) xdr_remote_node_device_dump_xml_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_device_dump_xml_ret, (char *) &ret) == -1)
+        return NULL;
+
+    /* Caller frees. */
+    return ret.xml;
+}
+
+static virNodeDevicePtr remoteNodeDeviceCreate(virConnectPtr conn,
+                                               const char *xml)
+{
+    remote_node_device_create_args args;
+    remote_node_device_create_ret ret;
+    virNodeDevicePtr dev;
+    GET_STORAGE_PRIVATE (conn, NULL);
+
+    args.xml = (char *)xml;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_DEVICE_CREATE,
+              (xdrproc_t) xdr_remote_node_device_create_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_device_create_ret, (char *) &ret) == -1)
+        return NULL;
+
+    dev = get_nonnull_node_device(conn, ret.dev);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_device_create_ret, (char *) &ret);
+
+    return dev;
+}
+
+static int remoteNodeDeviceDestroy(virNodeDevicePtr dev)
+{
+    remote_node_device_destroy_args args;
+    GET_STORAGE_PRIVATE (dev->conn, -1);
+
+    args.key = dev->key;
+
+    if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_DESTROY,
+              (xdrproc_t) xdr_remote_node_device_destroy_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+
+/*----------------------------------------------------------------------*/
+
 static int
 remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open,
                     virConnectAuthPtr auth
@@ -4783,6 +5112,22 @@ get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol)
     return virGetStorageVol (conn, vol.pool, vol.name, vol.key);
 }
 
+static virNodeDevicePtr
+get_nonnull_node_device (virConnectPtr conn, remote_nonnull_node_device dev)
+{
+    const char *bus_name =  (dev.bus_name && *dev.bus_name) ? dev.bus_name : NULL;
+    char **cap_names;
+    int i;
+
+    if (VIR_ALLOC_N(cap_names, dev.cap_names.cap_names_len + 1) < 0)
+        // TODO: complain
+        return NULL;
+    for (i = 0; i < dev.cap_names.cap_names_len; i++)
+        cap_names[i] = strdup(dev.cap_names.cap_names_val[i]);
+    return virGetNodeDevice (conn, dev.key, dev.name, dev.parent_key,
+                             bus_name, NULL, cap_names, NULL);
+}
+
 /* Make remote_nonnull_domain and remote_nonnull_network. */
 static void
 make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src)
@@ -4935,6 +5280,24 @@ static virStorageDriver storage_driver = {
     .volGetPath = remoteStorageVolGetPath,
 };
 
+static virNodeDriver node_driver = {
+    .name = "remote",
+    .open = remoteNodeDrvOpen,
+    .close = remoteNodeDrvClose,
+    .numOfDevices = remoteNodeNumOfDevices,
+    .listDevices = remoteNodeListDevices,
+    .numOfDevicesByCap = remoteNodeNumOfDevicesByCap,
+    .listDevicesByCap = remoteNodeListDevicesByCap,
+    .numOfDevicesByBus = remoteNodeNumOfDevicesByBus,
+    .listDevicesByBus = remoteNodeListDevicesByBus,
+    .deviceLookupByName = remoteNodeDeviceLookupByName,
+    .deviceLookupByKey = remoteNodeDeviceLookupByKey,
+    .deviceDumpXML = remoteNodeDeviceDumpXML,
+    .deviceCreate = remoteNodeDeviceCreate,
+    .deviceDestroy = remoteNodeDeviceDestroy,
+};
+
+
 #ifdef WITH_LIBVIRTD
 static virStateDriver state_driver = {
     remoteStartup,
@@ -4958,6 +5321,7 @@ remoteRegister (void)
     if (virRegisterDriver (&driver) == -1) return -1;
     if (virRegisterNetworkDriver (&network_driver) == -1) return -1;
     if (virRegisterStorageDriver (&storage_driver) == -1) return -1;
+    if (virRegisterNodeDriver (&node_driver) == -1) return -1;
 #ifdef WITH_LIBVIRTD
     if (virRegisterStateDriver (&state_driver) == -1) return -1;
 #endif
diff --git a/src/virsh.c b/src/virsh.c
index 00f928f..f51eaaf 100644
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -4383,6 +4383,83 @@ cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 }
 
 /*
+ * "node-list-devices" command
+ */
+static const vshCmdInfo info_node_list_devices[] = {
+    {"syntax", "node-list-devices"},
+    {"help", gettext_noop("enumerate devices on this host")},
+    {NULL, NULL}
+};
+
+static int
+cmdNodeListDevices (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+    char **devices;
+    int num_devices, i;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    num_devices = virNodeNumOfDevices(ctl->conn);
+    if (num_devices < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to count node devices"));
+        return FALSE;
+    } else if (num_devices == 0) {
+        return TRUE;
+    }
+
+    devices = vshMalloc(ctl, sizeof(char *) * num_devices);
+    num_devices = virNodeListDevices(ctl->conn, devices, num_devices);
+    if (num_devices < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to list node devices"));
+        free(devices);
+        return FALSE;
+    }
+    for (i = 0; i < num_devices; i++) {
+        vshPrint(ctl, "%s\n", devices[i]);
+        free(devices[i]);
+    }
+    free(devices);
+    return TRUE;
+}
+
+/*
+ * "node-device-dumpxml" command
+ */
+static const vshCmdInfo info_node_device_dumpxml[] = {
+    {"syntax", "node-device-dumpxml <device>"},
+    {"help", gettext_noop("node device details in XML")},
+    {"desc", gettext_noop("Output the node device details as an XML dump to stdout.")},
+    {NULL, NULL}
+};
+
+
+static const vshCmdOptDef opts_node_device_dumpxml[] = {
+    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("device key")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdNodeDeviceDumpXML (vshControl *ctl, const vshCmd *cmd)
+{
+    const char *key;
+    virNodeDevicePtr device;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+    if (!(key = vshCommandOptString(cmd, "device", NULL)))
+        return FALSE;
+    if (!(device = virNodeDeviceLookupByKey(ctl->conn, key))) {
+        vshError(ctl, FALSE, "%s '%s'", _("Could not find matching device"), key);
+        return FALSE;
+    }
+
+    vshPrint(ctl, "%s\n", virNodeDeviceGetXMLDesc(device, 0));
+    virNodeDeviceFree(device);
+    return TRUE;
+}
+
+/*
  * "hostkey" command
  */
 static const vshCmdInfo info_hostname[] = {
@@ -5534,6 +5611,9 @@ static const vshCmdDef commands[] = {
     {"net-uuid", cmdNetworkUuid, opts_network_uuid, info_network_uuid},
     {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo},
 
+    {"node-list-devices", cmdNodeListDevices, NULL, info_node_list_devices},
+    {"node-device-dumpxml", cmdNodeDeviceDumpXML, opts_node_device_dumpxml, info_node_device_dumpxml},
+
     {"pool-autostart", cmdPoolAutostart, opts_pool_autostart, info_pool_autostart},
     {"pool-build", cmdPoolBuild, opts_pool_build, info_pool_build},
     {"pool-create", cmdPoolCreate, opts_pool_create, info_pool_create},
diff --git a/src/virterror.c b/src/virterror.c
index 4aa7f42..7780421 100644
--- a/src/virterror.c
+++ b/src/virterror.c
@@ -310,6 +310,9 @@ virDefaultErrorFunc(virErrorPtr err)
         case VIR_FROM_DOMAIN:
             dom = "Domain Config ";
             break;
+        case VIR_FROM_NODE:
+            dom = "Node Driver ";
+            break;
 
     }
     if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
@@ -719,6 +722,24 @@ __virErrorMsg(virErrorNumber error, const char *info)
                 else
                         errmsg = _("Failed to find a storage driver: %s");
                 break;
+        case VIR_WAR_NO_NODE:
+                if (info == NULL)
+                        errmsg = _("Failed to find a node driver");
+                else
+                        errmsg = _("Failed to find a node driver: %s");
+                break;
+        case VIR_ERR_INVALID_NODE_DEVICE:
+                if (info == NULL)
+                        errmsg = _("invalid node device pointer");
+                else
+                        errmsg = _("invalid node device pointer in %s");
+                break;
+        case VIR_ERR_NO_NODE_DEVICE:
+                if (info == NULL)
+                        errmsg = _("Node device not found");
+                else
+                        errmsg = _("Node device not found: %s");
+                break;
     }
     return (errmsg);
 }

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