[libvirt] PATCH: 0/7: Final node device patches to be committed

FYI, this is a repost of the final node device patches I intend to commit tomorrow. There's a few changes based on Mark's feedback which I'll note against each patch... Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

This patch contains the public API for node devices. Changes since last post are - Remove the ByCap methods - the main list method now takes an optionally NULL, cap arguent. This simplified virsh usage later too which is nice - Change VIR_FROM_DEVMONITOR to VIR_FROM_NODEDEV to match rest of public naming scheme - Remove unused NodeDeviceCreate/Destroy methods. We can re-add them when we need them Daniel diff -r 2e1f852dff74 include/libvirt/libvirt.h --- a/include/libvirt/libvirt.h Wed Nov 19 18:00:40 2008 +0000 +++ b/include/libvirt/libvirt.h Thu Nov 20 17:43:17 2008 +0000 @@ -995,6 +995,59 @@ unsigned int flags); /* + * 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, + const char *cap, + unsigned int flags); + +int virNodeListDevices (virConnectPtr conn, + const char *cap, + char **const names, + int maxnames, + unsigned int flags); + +virNodeDevicePtr virNodeDeviceLookupByName (virConnectPtr conn, + const char *name); + +const char * virNodeDeviceGetName (virNodeDevicePtr dev); + +const char * virNodeDeviceGetParent (virNodeDevicePtr dev); + +int virNodeDeviceNumOfCaps (virNodeDevicePtr dev); + +int virNodeDeviceListCaps (virNodeDevicePtr dev, + char **const names, + int maxnames); + +char * virNodeDeviceGetXMLDesc (virNodeDevicePtr dev, + unsigned int flags); + +int virNodeDeviceFree (virNodeDevicePtr dev); + +/* * Domain Event Notification */ diff -r 2e1f852dff74 include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in Wed Nov 19 18:00:40 2008 +0000 +++ b/include/libvirt/libvirt.h.in Thu Nov 20 17:43:17 2008 +0000 @@ -995,6 +995,59 @@ unsigned int flags); /* + * 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, + const char *cap, + unsigned int flags); + +int virNodeListDevices (virConnectPtr conn, + const char *cap, + char **const names, + int maxnames, + unsigned int flags); + +virNodeDevicePtr virNodeDeviceLookupByName (virConnectPtr conn, + const char *name); + +const char * virNodeDeviceGetName (virNodeDevicePtr dev); + +const char * virNodeDeviceGetParent (virNodeDevicePtr dev); + +int virNodeDeviceNumOfCaps (virNodeDevicePtr dev); + +int virNodeDeviceListCaps (virNodeDevicePtr dev, + char **const names, + int maxnames); + +char * virNodeDeviceGetXMLDesc (virNodeDevicePtr dev, + unsigned int flags); + +int virNodeDeviceFree (virNodeDevicePtr dev); + +/* * Domain Event Notification */ diff -r 2e1f852dff74 include/libvirt/virterror.h --- a/include/libvirt/virterror.h Wed Nov 19 18:00:40 2008 +0000 +++ b/include/libvirt/virterror.h Thu Nov 20 17:43:17 2008 +0000 @@ -59,6 +59,7 @@ VIR_FROM_NETWORK, /* Error from network config */ VIR_FROM_DOMAIN, /* Error from domain config */ VIR_FROM_UML, /* Error at the UML driver */ + VIR_FROM_NODEDEV, /* Error from node device monitor */ } virErrorDomain; @@ -149,6 +150,9 @@ 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 -r 2e1f852dff74 src/datatypes.c --- a/src/datatypes.c Wed Nov 19 18:00:40 2008 +0000 +++ b/src/datatypes.c Thu Nov 20 17:43:17 2008 +0000 @@ -140,6 +140,9 @@ 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); @@ -156,6 +159,8 @@ 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); @@ -183,6 +188,8 @@ 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 (virLastErr.conn == conn) @@ -771,3 +778,126 @@ pthread_mutex_unlock(&vol->conn->lock); return (refs); } + + +/** + * virGetNodeDevice: + * @conn: the hypervisor connection + * @name: device name (unique on node) + * + * 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 *name) +{ + virNodeDevicePtr ret = NULL; + + if ((!VIR_IS_CONNECT(conn)) || (name == NULL)) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(NULL); + } + pthread_mutex_lock(&conn->lock); + + ret = (virNodeDevicePtr) virHashLookup(conn->nodeDevices, name); + if (ret == NULL) { + if (VIR_ALLOC(ret) < 0) { + virLibConnError(conn, VIR_ERR_NO_MEMORY, _("allocating node dev")); + goto error; + } + ret->magic = VIR_NODE_DEVICE_MAGIC; + ret->conn = conn; + ret->name = strdup(name); + if (ret->name == NULL) { + virLibConnError(conn, VIR_ERR_NO_MEMORY, _("copying node dev name")); + goto error; + } + + if (virHashAddEntry(conn->nodeDevices, name, ret) < 0) { + virLibConnError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to add node dev to conn hash table")); + 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->name); + 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) { + virConnectPtr conn = dev->conn; + DEBUG("release dev %p %s", dev, dev->name); + + if (virHashRemoveEntry(conn->nodeDevices, dev->name, NULL) < 0) + virLibConnError(conn, VIR_ERR_INTERNAL_ERROR, + _("dev missing from connection hash table")); + + dev->magic = -1; + VIR_FREE(dev->name); + VIR_FREE(dev); + + DEBUG("unref connection %p %d", conn, 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 -r 2e1f852dff74 src/datatypes.h --- a/src/datatypes.h Wed Nov 19 18:00:40 2008 +0000 +++ b/src/datatypes.h Thu Nov 20 17:43:17 2008 +0000 @@ -78,6 +78,16 @@ #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)) + /** * _virConnect: @@ -93,6 +103,7 @@ virDriverPtr driver; virNetworkDriverPtr networkDriver; virStorageDriverPtr storageDriver; + virDeviceMonitorPtr deviceMonitor; /* Private data pointer which can be used by driver and * network driver as they wish. @@ -101,6 +112,7 @@ void * privateData; void * networkPrivateData; void * storagePrivateData; + void * devMonPrivateData; /* Per-connection error. */ virError err; /* the last error */ @@ -118,6 +130,7 @@ 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; /* hash table for known node devices */ int refs; /* reference count */ }; @@ -176,6 +189,19 @@ char key[PATH_MAX]; /* unique key for storage vol */ }; +/** + * _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 *name; /* device name (unique on node) */ +}; + + /************************************************************************ * * * API for domain/connections (de)allocations and lookups * @@ -203,4 +229,8 @@ const char *key); int virUnrefStorageVol(virStorageVolPtr vol); +virNodeDevicePtr virGetNodeDevice(virConnectPtr conn, + const char *name); +int virUnrefNodeDevice(virNodeDevicePtr dev); + #endif diff -r 2e1f852dff74 src/driver.h --- a/src/driver.h Wed Nov 19 18:00:40 2008 +0000 +++ b/src/driver.h Thu Nov 20 17:43:17 2008 +0000 @@ -635,6 +635,54 @@ }; #endif + +typedef struct _virDeviceMonitor virDeviceMonitor; +typedef virDeviceMonitor *virDeviceMonitorPtr; + +typedef int (*virDevMonNumOfDevices)(virConnectPtr conn, + const char *cap, + unsigned int flags); + +typedef int (*virDevMonListDevices)(virConnectPtr conn, + const char *cap, + char **const names, + int maxnames, + unsigned int flags); + +typedef virNodeDevicePtr (*virDevMonDeviceLookupByName)(virConnectPtr conn, + const char *name); + +typedef char * (*virDevMonDeviceDumpXML)(virNodeDevicePtr dev, + unsigned int flags); + +typedef char * (*virDevMonDeviceGetParent)(virNodeDevicePtr dev); + +typedef int (*virDevMonDeviceNumOfCaps)(virNodeDevicePtr dev); + +typedef int (*virDevMonDeviceListCaps)(virNodeDevicePtr dev, + char **const names, + int maxnames); + +/** + * _virDeviceMonitor: + * + * Structure associated with monitoring the devices + * on a virtualized node. + * + */ +struct _virDeviceMonitor { + const char * name; /* the name of the driver */ + virDrvOpen open; + virDrvClose close; + virDevMonNumOfDevices numOfDevices; + virDevMonListDevices listDevices; + virDevMonDeviceLookupByName deviceLookupByName; + virDevMonDeviceDumpXML deviceDumpXML; + virDevMonDeviceGetParent deviceGetParent; + virDevMonDeviceNumOfCaps deviceNumOfCaps; + virDevMonDeviceListCaps deviceListCaps; +}; + /* * Registration * TODO: also need ways to (des)activate a given driver @@ -643,6 +691,7 @@ int virRegisterDriver(virDriverPtr); int virRegisterNetworkDriver(virNetworkDriverPtr); int virRegisterStorageDriver(virStorageDriverPtr); +int virRegisterDeviceMonitor(virDeviceMonitorPtr); #ifdef WITH_LIBVIRTD int virRegisterStateDriver(virStateDriverPtr); #endif diff -r 2e1f852dff74 src/libvirt.c --- a/src/libvirt.c Wed Nov 19 18:00:40 2008 +0000 +++ b/src/libvirt.c Thu Nov 20 17:43:17 2008 +0000 @@ -70,6 +70,8 @@ static int virNetworkDriverTabCount = 0; static virStorageDriverPtr virStorageDriverTab[MAX_DRIVERS]; static int virStorageDriverTabCount = 0; +static virDeviceMonitorPtr virDeviceMonitorTab[MAX_DRIVERS]; +static int virDeviceMonitorTabCount = 0; #ifdef WITH_LIBVIRTD static virStateDriverPtr virStateDriverTab[MAX_DRIVERS]; static int virStateDriverTabCount = 0; @@ -449,6 +451,32 @@ } /** + * 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_NODEDEV, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +/** * virRegisterNetworkDriver: * @driver: pointer to a network driver block * @@ -508,6 +536,34 @@ virStorageDriverTab[virStorageDriverTabCount] = driver; return virStorageDriverTabCount++; +} + +/** + * virRegisterDeviceMonitor: + * @driver: pointer to a device monitor block + * + * Register a device monitor + * + * Returns the driver priority or -1 in case of error. + */ +int +virRegisterDeviceMonitor(virDeviceMonitorPtr driver) +{ + if (virInitialize() < 0) + return -1; + + if (driver == NULL) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + if (virDeviceMonitorTabCount >= MAX_DRIVERS) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + virDeviceMonitorTab[virDeviceMonitorTabCount] = driver; + return virDeviceMonitorTabCount++; } /** @@ -806,6 +862,33 @@ } } + /* Node driver (optional) */ + for (i = 0; i < virDeviceMonitorTabCount; i++) { + res = virDeviceMonitorTab[i]->open (ret, auth, flags); + DEBUG("node driver %d %s returned %s", + i, virDeviceMonitorTab[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(virDeviceMonitorTab[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?", + virDeviceMonitorTab[i]->name) > 0) { + virLibConnWarning (NULL, VIR_WAR_NO_NODE, msg); + VIR_FREE(msg); + } + } + break; + } else if (res == VIR_DRV_OPEN_SUCCESS) { + ret->deviceMonitor = virDeviceMonitorTab[i]; + break; + } + } + return ret; failed: @@ -923,6 +1006,8 @@ conn->networkDriver->close (conn); if (conn->storageDriver) conn->storageDriver->close (conn); + if (conn->deviceMonitor) + conn->deviceMonitor->close (conn); conn->driver->close (conn); if (virUnrefConnect(conn) < 0) @@ -5374,6 +5459,257 @@ + +/** + * virNodeNumOfDevices: + * @conn: pointer to the hypervisor connection + * @cap: capability name + * @flags: flags (unused, pass 0) + * + * Provides the number of node devices. + * + * If the optional 'cap' argument is non-NULL, then the count + * will be restricted to devices with the specified capability + * + * Returns the number of node devices or -1 in case of error + */ +int +virNodeNumOfDevices(virConnectPtr conn, const char *cap, unsigned int flags) +{ + DEBUG("conn=%p, cap=%s, flags=%d", conn, cap, flags); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + if (flags != 0) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (conn->deviceMonitor && conn->deviceMonitor->numOfDevices) + return conn->deviceMonitor->numOfDevices (conn, cap, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virNodeListDevices: + * @conn: pointer to the hypervisor connection + * @cap: capability name + * @names: array to collect the list of node device names + * @maxnames: size of @names + * @flags: flags (unused, pass 0) + * + * Collect the list of node devices, and store their names in @names + * + * If the optional 'cap' argument is non-NULL, then the count + * will be restricted to devices with the specified capability + * + * Returns the number of node devices found or -1 in case of error + */ +int +virNodeListDevices(virConnectPtr conn, + const char *cap, + char **const names, int maxnames, + unsigned int flags) +{ + DEBUG("conn=%p, cap=%s, names=%p, maxnames=%d, flags=%d", + conn, cap, names, maxnames, flags); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + if ((flags != 0) || (names == NULL) || (maxnames < 0)) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (conn->deviceMonitor && conn->deviceMonitor->listDevices) + return conn->deviceMonitor->listDevices (conn, cap, names, maxnames, flags); + + 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->deviceMonitor && conn->deviceMonitor->deviceLookupByName) + return conn->deviceMonitor->deviceLookupByName (conn, name); + + 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) +{ + 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 NULL; + } + + if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceDumpXML) + return dev->conn->deviceMonitor->deviceDumpXML (dev, flags); + + virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + +/** + * virNodeDeviceGetName: + * @dev: the device + * + * Returns the device name. + */ +const char *virNodeDeviceGetName(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 NULL; + } + + return dev->name; +} + +/** + * virNodeDeviceGetParent: + * @dev: the device + * + * Returns the name of the device's parent, or NULL if the + * device has no parent. + */ +const char *virNodeDeviceGetParent(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 NULL; + } + + if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceGetParent) + return dev->conn->deviceMonitor->deviceGetParent (dev); + + virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + +/** + * virNodeDeviceNumOfCaps: + * @dev: the device + * + * Returns the number of capabilities supported by the device. + */ +int virNodeDeviceNumOfCaps(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 (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceNumOfCaps) + return dev->conn->deviceMonitor->deviceNumOfCaps (dev); + + virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +/** + * virNodeDeviceListCaps: + * @dev: the device + * @names: array to collect the list of capability names + * @maxnames: size of @names + * + * Lists the names of the capabilities supported by the device. + * + * Returns the number of capability names listed in @names. + */ +int virNodeDeviceListCaps(virNodeDevicePtr dev, + char **const names, + int maxnames) +{ + DEBUG("dev=%p, conn=%p, names=%p, maxnames=%d", + dev, dev ? dev->conn : NULL, names, maxnames); + + if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) { + virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__); + return -1; + } + + if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceListCaps) + return dev->conn->deviceMonitor->deviceListCaps (dev, names, maxnames); + + virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * 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); +} + + /* * Domain Event Notification */ diff -r 2e1f852dff74 src/libvirt_sym.version.in --- a/src/libvirt_sym.version.in Wed Nov 19 18:00:40 2008 +0000 +++ b/src/libvirt_sym.version.in Thu Nov 20 17:43:17 2008 +0000 @@ -232,6 +232,21 @@ virEventRegisterImpl; virConnectDomainEventRegister; virConnectDomainEventDeregister; + + virNodeNumOfDevices; + virNodeListDevices; + virNodeNumOfDevicesByCap; + virNodeListDevicesByCap; + virNodeDeviceLookupByName; + virNodeDeviceFree; + virNodeDeviceGetXMLDesc; + virNodeDeviceCreate; + virNodeDeviceDestroy; + virNodeDeviceGetName; + virNodeDeviceGetParent; + virNodeDeviceNumOfCaps; + virNodeDeviceListCaps; + } LIBVIRT_0.4.5; /* .... define new API here using predicted next version number .... */ diff -r 2e1f852dff74 src/virterror.c --- a/src/virterror.c Wed Nov 19 18:00:40 2008 +0000 +++ b/src/virterror.c Thu Nov 20 17:43:17 2008 +0000 @@ -309,6 +309,9 @@ break; case VIR_FROM_DOMAIN: dom = "Domain Config "; + break; + case VIR_FROM_NODEDEV: + dom = "Node Device "; break; case VIR_FROM_UML: dom = "UML "; @@ -721,6 +724,24 @@ 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); } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Nov 20, 2008 at 05:51:39PM +0000, Daniel P. Berrange wrote:
This patch contains the public API for node devices. Changes since last post are
- Remove the ByCap methods - the main list method now takes an optionally NULL, cap arguent. This simplified virsh usage later too which is nice
- Change VIR_FROM_DEVMONITOR to VIR_FROM_NODEDEV to match rest of public naming scheme
- Remove unused NodeDeviceCreate/Destroy methods. We can re-add them when we need them
Okay, that follows the earlier feedback from Mark and Dave, +1 Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This is primarily the internal XML handling for node devices Changes since last time - Remove the duplicate mac address information - Remove unused 'originating_device' Daniel diff -r 28bfbe4fa22f src/Makefile.am --- a/src/Makefile.am Thu Nov 20 17:37:11 2008 +0000 +++ b/src/Makefile.am Thu Nov 20 17:43:01 2008 +0000 @@ -136,6 +136,10 @@ storage_driver.h storage_driver.c \ storage_backend.h storage_backend.c +# Network driver generic impl APIs +NODE_DEVICE_CONF_SOURCES = \ + node_device_conf.c node_device_conf.h + STORAGE_DRIVER_FS_SOURCES = \ storage_backend_fs.h storage_backend_fs.c @@ -171,7 +175,8 @@ $(DRIVER_SOURCES) \ $(DOMAIN_CONF_SOURCES) \ $(NETWORK_CONF_SOURCES) \ - $(STORAGE_CONF_SOURCES) + $(STORAGE_CONF_SOURCES) \ + $(NODE_DEVICE_CONF_SOURCES) if WITH_TEST if WITH_DRIVER_MODULES diff -r 28bfbe4fa22f src/node_device_conf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/node_device_conf.c Thu Nov 20 17:43:01 2008 +0000 @@ -0,0 +1,404 @@ +/* + * node_device_conf.c: config handling for node devices + * + * 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 "virterror_internal.h" +#include "memory.h" + +#include "node_device_conf.h" +#include "memory.h" +#include "xml.h" +#include "util.h" +#include "buf.h" +#include "uuid.h" + + +VIR_ENUM_IMPL(virNodeDevCap, VIR_NODE_DEV_CAP_LAST, + "system", + "pci", + "usb_device", + "usb", + "net", + "block", + "scsi_host", + "scsi", + "storage"); + +VIR_ENUM_IMPL(virNodeDevNetCap, VIR_NODE_DEV_CAP_NET_LAST, + "80203", + "80211"); + + +#define virNodeDeviceLog(msg...) fprintf(stderr, msg) + +virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs, + const char *name) +{ + unsigned int i; + + for (i = 0; i < devs->count; i++) + if (STREQ(devs->objs[i]->def->name, name)) + return devs->objs[i]; + + return NULL; +} + + +void virNodeDeviceDefFree(virNodeDeviceDefPtr def) +{ + virNodeDevCapsDefPtr caps; + + if (!def) + return; + + VIR_FREE(def->name); + VIR_FREE(def->parent); + + caps = def->caps; + while (caps) { + virNodeDevCapsDefPtr next = caps->next; + virNodeDevCapsDefFree(caps); + caps = next; + } + + VIR_FREE(def); +} + +void virNodeDeviceObjFree(virNodeDeviceObjPtr dev) +{ + if (!dev) + return; + + virNodeDeviceDefFree(dev->def); + if (dev->privateFree) + (*dev->privateFree)(dev->privateData); + + VIR_FREE(dev); +} + +void virNodeDeviceObjListFree(virNodeDeviceObjListPtr devs) +{ + unsigned int i; + for (i = 0 ; i < devs->count ; i++) + virNodeDeviceObjFree(devs->objs[i]); + VIR_FREE(devs->objs); + devs->count = 0; +} + +virNodeDeviceObjPtr virNodeDeviceAssignDef(virConnectPtr conn, + virNodeDeviceObjListPtr devs, + const virNodeDeviceDefPtr def) +{ + virNodeDeviceObjPtr device; + + if ((device = virNodeDeviceFindByName(devs, def->name))) { + virNodeDeviceDefFree(device->def); + device->def = def; + return device; + } + + if (VIR_ALLOC(device) < 0) { + virNodeDeviceReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + + device->def = def; + + if (VIR_REALLOC_N(devs->objs, devs->count+1) < 0) { + device->def = NULL; + virNodeDeviceObjFree(device); + virNodeDeviceReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + devs->objs[devs->count++] = device; + + return device; + +} + +void virNodeDeviceObjRemove(virNodeDeviceObjListPtr devs, + const virNodeDeviceObjPtr dev) +{ + unsigned int i; + + for (i = 0; i < devs->count; i++) { + if (devs->objs[i] == dev) { + virNodeDeviceObjFree(devs->objs[i]); + + if (i < (devs->count - 1)) + memmove(devs->objs + i, devs->objs + i + 1, + sizeof(*(devs->objs)) * (devs->count - (i + 1))); + + if (VIR_REALLOC_N(devs->objs, devs->count - 1) < 0) { + ; /* Failure to reduce memory allocation isn't fatal */ + } + devs->count--; + + break; + } + } +} + +char *virNodeDeviceDefFormat(virConnectPtr conn, + const virNodeDeviceDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virNodeDevCapsDefPtr caps = def->caps; + char *tmp; + + virBufferAddLit(&buf, "<device>\n"); + virBufferEscapeString(&buf, " <name>%s</name>\n", def->name); + + if (def->parent) + virBufferEscapeString(&buf, " <parent>%s</parent>\n", def->parent); + + for (caps = def->caps; caps; caps = caps->next) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + union _virNodeDevCapData *data = &caps->data; + + virBufferVSprintf(&buf, " <capability type='%s'>\n", + virNodeDevCapTypeToString(caps->type)); + switch (caps->type) { + case VIR_NODE_DEV_CAP_SYSTEM: + if (data->system.product_name) + virBufferEscapeString(&buf, " <product>%s</product>\n", + data->system.product_name); + virBufferAddLit(&buf, " <hardware>\n"); + if (data->system.hardware.vendor_name) + virBufferEscapeString(&buf, " <vendor>%s</vendor>\n", + data->system.hardware.vendor_name); + if (data->system.hardware.version) + virBufferEscapeString(&buf, " <version>%s</version>\n", + data->system.hardware.version); + if (data->system.hardware.serial) + virBufferEscapeString(&buf, " <serial>%s</serial>\n", + data->system.hardware.serial); + virUUIDFormat(data->system.hardware.uuid, uuidstr); + virBufferVSprintf(&buf, " <uuid>%s</uuid>\n", uuidstr); + virBufferAddLit(&buf, " </hardware>\n"); + virBufferAddLit(&buf, " <firmware>\n"); + if (data->system.firmware.vendor_name) + virBufferEscapeString(&buf, " <vendor>%s</vendor>\n", + data->system.firmware.vendor_name); + if (data->system.firmware.version) + virBufferEscapeString(&buf, " <version>%s</version>\n", + data->system.firmware.version); + if (data->system.firmware.release_date) + virBufferEscapeString(&buf, + " <release_date>%s</release_date>\n", + data->system.firmware.release_date); + virBufferAddLit(&buf, " </firmware>\n"); + break; + case VIR_NODE_DEV_CAP_PCI_DEV: + virBufferVSprintf(&buf, " <domain>%d</domain>\n", + data->pci_dev.domain); + virBufferVSprintf(&buf, " <bus>%d</bus>\n", data->pci_dev.bus); + virBufferVSprintf(&buf, " <slot>%d</slot>\n", + data->pci_dev.slot); + virBufferVSprintf(&buf, " <function>%d</function>\n", + data->pci_dev.function); + virBufferVSprintf(&buf, " <product id='%d'", + data->pci_dev.product); + if (data->pci_dev.product_name) + virBufferEscapeString(&buf, ">%s</product>\n", + data->pci_dev.product_name); + else + virBufferAddLit(&buf, " />\n"); + virBufferVSprintf(&buf, " <vendor id='%d'", + data->pci_dev.vendor); + if (data->pci_dev.vendor_name) + virBufferEscapeString(&buf, ">%s</vendor>\n", + data->pci_dev.vendor_name); + else + virBufferAddLit(&buf, " />\n"); + break; + case VIR_NODE_DEV_CAP_USB_DEV: + virBufferVSprintf(&buf, " <bus>%d</bus>\n", data->usb_dev.bus); + virBufferVSprintf(&buf, " <device>%d</device>\n", + data->usb_dev.device); + virBufferVSprintf(&buf, " <product id='%d'", + data->usb_dev.product); + if (data->usb_dev.product_name) + virBufferEscapeString(&buf, ">%s</product>\n", + data->usb_dev.product_name); + else + virBufferAddLit(&buf, " />\n"); + virBufferVSprintf(&buf, " <vendor id='%d'", + data->usb_dev.vendor); + if (data->usb_dev.vendor_name) + virBufferEscapeString(&buf, ">%s</vendor>\n", + data->usb_dev.vendor_name); + else + virBufferAddLit(&buf, " />\n"); + break; + case VIR_NODE_DEV_CAP_USB_INTERFACE: + virBufferVSprintf(&buf, " <number>%d</number>\n", + data->usb_if.number); + virBufferVSprintf(&buf, " <class>%d</class>\n", + data->usb_if._class); + virBufferVSprintf(&buf, " <subclass>%d</subclass>\n", + data->usb_if.subclass); + virBufferVSprintf(&buf, " <protocol>%d</protocol>\n", + data->usb_if.protocol); + if (data->usb_if.description) + virBufferVSprintf(&buf, " <description>%s</description>\n", + data->usb_if.description); + break; + case VIR_NODE_DEV_CAP_NET: + virBufferVSprintf(&buf, " <interface>%s</interface>\n", + data->net.interface); + if (data->net.address) + virBufferVSprintf(&buf, " <address>%s</address>\n", + data->net.address); + if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) { + const char *subtyp = + virNodeDevNetCapTypeToString(data->net.subtype); + virBufferVSprintf(&buf, " <capability type='%s'/>\n", subtyp); + } + break; + case VIR_NODE_DEV_CAP_BLOCK: + virBufferVSprintf(&buf, " <device>%s</device>\n", + data->block.device); + break; + case VIR_NODE_DEV_CAP_SCSI_HOST: + virBufferVSprintf(&buf, " <host>%d</host>\n", + data->scsi_host.host); + break; + case VIR_NODE_DEV_CAP_SCSI: + virBufferVSprintf(&buf, " <host>%d</host>\n", data->scsi.host); + virBufferVSprintf(&buf, " <bus>%d</bus>\n", data->scsi.bus); + virBufferVSprintf(&buf, " <target>%d</target>\n", + data->scsi.target); + virBufferVSprintf(&buf, " <lun>%d</lun>\n", data->scsi.lun); + if (data->scsi.type) + virBufferVSprintf(&buf, " <type>%s</type>\n", + data->scsi.type); + break; + case VIR_NODE_DEV_CAP_STORAGE: + if (data->storage.bus) + virBufferVSprintf(&buf, " <bus>%s</bus>\n", + data->storage.bus); + if (data->storage.drive_type) + virBufferVSprintf(&buf, " <drive_type>%s</drive_type>\n", + data->storage.drive_type); + if (data->storage.model) + virBufferVSprintf(&buf, " <model>%s</model>\n", + data->storage.model); + if (data->storage.vendor) + virBufferVSprintf(&buf, " <vendor>%s</vendor>\n", + data->storage.vendor); + if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE) { + int avl = data->storage.flags & + VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE; + virBufferAddLit(&buf, " <capability type='removable'>\n"); + virBufferVSprintf(&buf, + " <media_available>%d" + "</media_available>\n", avl ? 1 : 0); + virBufferVSprintf(&buf, " <media_size>%llu</media_size>\n", + data->storage.removable_media_size); + virBufferAddLit(&buf, " </capability>\n"); + } else { + virBufferVSprintf(&buf, " <size>%llu</size>\n", + data->storage.size); + } + if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE) + virBufferAddLit(&buf, + " <capability type='hotpluggable' />\n"); + break; + case VIR_NODE_DEV_CAP_LAST: + /* ignore special LAST value */ + break; + } + + virBufferAddLit(&buf, " </capability>\n"); + } + + virBufferAddLit(&buf, "</device>\n"); + + if (virBufferError(&buf)) + goto no_memory; + + return virBufferContentAndReset(&buf); + + no_memory: + virNodeDeviceReportError(conn, VIR_ERR_NO_MEMORY, NULL); + tmp = virBufferContentAndReset(&buf); + VIR_FREE(tmp); + return NULL; +} + +void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps) +{ + union _virNodeDevCapData *data = &caps->data; + + switch (caps->type) { + case VIR_NODE_DEV_CAP_SYSTEM: + VIR_FREE(data->system.product_name); + VIR_FREE(data->system.hardware.vendor_name); + VIR_FREE(data->system.hardware.version); + VIR_FREE(data->system.hardware.serial); + VIR_FREE(data->system.firmware.vendor_name); + VIR_FREE(data->system.firmware.version); + VIR_FREE(data->system.firmware.release_date); + break; + case VIR_NODE_DEV_CAP_PCI_DEV: + VIR_FREE(data->pci_dev.product_name); + VIR_FREE(data->pci_dev.vendor_name); + break; + case VIR_NODE_DEV_CAP_USB_DEV: + VIR_FREE(data->usb_dev.product_name); + VIR_FREE(data->usb_dev.vendor_name); + break; + case VIR_NODE_DEV_CAP_USB_INTERFACE: + VIR_FREE(data->usb_if.description); + break; + case VIR_NODE_DEV_CAP_NET: + VIR_FREE(data->net.interface); + VIR_FREE(data->net.address); + break; + case VIR_NODE_DEV_CAP_BLOCK: + VIR_FREE(data->block.device); + break; + case VIR_NODE_DEV_CAP_SCSI_HOST: + break; + case VIR_NODE_DEV_CAP_SCSI: + VIR_FREE(data->scsi.type); + break; + case VIR_NODE_DEV_CAP_STORAGE: + VIR_FREE(data->storage.bus); + VIR_FREE(data->storage.drive_type); + VIR_FREE(data->storage.model); + VIR_FREE(data->storage.vendor); + break; + case VIR_NODE_DEV_CAP_LAST: + /* This case is here to shutup the compiler */ + break; + } + + VIR_FREE(caps); +} + diff -r 28bfbe4fa22f src/node_device_conf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/node_device_conf.h Thu Nov 20 17:43:01 2008 +0000 @@ -0,0 +1,195 @@ +/* + * node_device_conf.h: config handling for node devices + * + * 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_CONF_H__ +#define __VIR_NODE_DEVICE_CONF_H__ + +#include "internal.h" +#include "util.h" + +enum virNodeDevCapType { + /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */ + VIR_NODE_DEV_CAP_SYSTEM, /* System capability */ + VIR_NODE_DEV_CAP_PCI_DEV, /* PCI device */ + VIR_NODE_DEV_CAP_USB_DEV, /* USB device */ + VIR_NODE_DEV_CAP_USB_INTERFACE, /* USB interface */ + VIR_NODE_DEV_CAP_NET, /* Network device */ + VIR_NODE_DEV_CAP_BLOCK, /* Block device */ + VIR_NODE_DEV_CAP_SCSI_HOST, /* SCSI Host Bus Adapter */ + VIR_NODE_DEV_CAP_SCSI, /* SCSI device */ + VIR_NODE_DEV_CAP_STORAGE, /* Storage device */ + VIR_NODE_DEV_CAP_LAST +}; + +enum virNodeDevNetCapType { + /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */ + VIR_NODE_DEV_CAP_NET_80203, /* 802.03 network device */ + VIR_NODE_DEV_CAP_NET_80211, /* 802.11 network device */ + VIR_NODE_DEV_CAP_NET_LAST +}; + +VIR_ENUM_DECL(virNodeDevCap); +VIR_ENUM_DECL(virNodeDevNetCap); + +enum virNodeDevStorageCapFlags { + VIR_NODE_DEV_CAP_STORAGE_REMOVABLE = (1 << 0), + VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE = (1 << 1), + VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE = (1 << 2), +}; + +typedef struct _virNodeDevCapsDef virNodeDevCapsDef; +typedef virNodeDevCapsDef *virNodeDevCapsDefPtr; +struct _virNodeDevCapsDef { + enum virNodeDevCapType type; + union _virNodeDevCapData { + struct { + char *product_name; + struct { + char *vendor_name; + char *version; + char *serial; + unsigned char uuid[VIR_UUID_BUFLEN]; + } hardware; + struct { + char *vendor_name; + char *version; + char *release_date; + } firmware; + } system; + struct { + unsigned domain; + unsigned bus; + unsigned slot; + unsigned function; + unsigned product; + unsigned vendor; + char *product_name; + char *vendor_name; + } pci_dev; + struct { + unsigned bus; + unsigned device; + unsigned product; + unsigned vendor; + char *product_name; + char *vendor_name; + } usb_dev; + struct { + unsigned number; + unsigned _class; /* "class" is reserved in C */ + unsigned subclass; + unsigned protocol; + char *description; + } usb_if; + struct { + char *address; + char *interface; + enum virNodeDevNetCapType subtype; /* LAST -> no subtype */ + } net; + struct { + char *device; + } block; + struct { + unsigned host; + } scsi_host; + struct { + unsigned host; + unsigned bus; + unsigned target; + unsigned lun; + char *type; + } scsi; + struct { + unsigned long long size; + unsigned long long removable_media_size; + char *bus; + char *drive_type; + char *model; + char *vendor; + unsigned flags; /* virNodeDevStorageCapFlags bits */ + } storage; + } data; + virNodeDevCapsDefPtr next; /* next capability */ +}; + + +typedef struct _virNodeDeviceDef virNodeDeviceDef; +typedef virNodeDeviceDef *virNodeDeviceDefPtr; +struct _virNodeDeviceDef { + char *name; /* device name (unique on node) */ + char *parent; /* optional parent device name */ + virNodeDevCapsDefPtr caps; /* optional device capabilities */ +}; + + +typedef struct _virNodeDeviceObj virNodeDeviceObj; +typedef virNodeDeviceObj *virNodeDeviceObjPtr; +struct _virNodeDeviceObj { + virNodeDeviceDefPtr def; /* device definition */ + void *privateData; /* driver-specific private data */ + void (*privateFree)(void *data); /* destructor for private data */ + +}; + +typedef struct _virNodeDeviceObjList virNodeDeviceObjList; +typedef virNodeDeviceObjList *virNodeDeviceObjListPtr; +struct _virNodeDeviceObjList { + unsigned int count; + virNodeDeviceObjPtr *objs; +}; + +typedef struct _virDeviceMonitorState virDeviceMonitorState; +typedef virDeviceMonitorState *virDeviceMonitorStatePtr; +struct _virDeviceMonitorState { + virNodeDeviceObjList devs; /* currently-known devices */ + void *privateData; /* driver-specific private data */ +}; + +#define virNodeDeviceReportError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_NODEDEV, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs, + const char *name); + +virNodeDeviceObjPtr virNodeDeviceAssignDef(virConnectPtr conn, + virNodeDeviceObjListPtr devs, + const virNodeDeviceDefPtr def); + +void virNodeDeviceObjRemove(virNodeDeviceObjListPtr devs, + const virNodeDeviceObjPtr dev); + +char *virNodeDeviceDefFormat(virConnectPtr conn, + const virNodeDeviceDefPtr def); + +// TODO: virNodeDeviceDefParseString/File/Node for virNodeDeviceCreate + +void virNodeDeviceDefFree(virNodeDeviceDefPtr def); + +void virNodeDeviceObjFree(virNodeDeviceObjPtr dev); + +void virNodeDeviceObjListFree(virNodeDeviceObjListPtr devs); + +void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps); + +#endif /* __VIR_NODE_DEVICE_CONF_H__ */ -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Nov 20, 2008 at 05:53:43PM +0000, Daniel P. Berrange wrote:
This is primarily the internal XML handling for node devices Changes since last time
- Remove the duplicate mac address information - Remove unused 'originating_device'
okay, let's push, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

The internal impl for HAL and devicekit Changes since last time - Fix broken logic in devicekit NIC interfac checks - Disable DeviceKit by default Daniel diff -r c51d78efb84a configure.in --- a/configure.in Thu Nov 20 17:43:18 2008 +0000 +++ b/configure.in Thu Nov 20 17:47:42 2008 +0000 @@ -1105,6 +1105,108 @@ 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 "$with_libvirtd" = "no" ; then + with_hal=no +fi +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=no]) + +if test "$with_libvirtd" = "no" ; then + with_devkit=no +fi + +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]) + +with_nodedev=no; +if test "$with_devkit" = "yes" -o "$with_hal" = "yes"; +then + with_nodedev=yes + AC_DEFINE_UNQUOTED([WITH_NODE_DEVICES], 1, [with node device driver]) +fi +AM_CONDITIONAL([WITH_NODE_DEVICES], [test "$with_nodedev" = "yes"]) + + # very annoying rm -f COPYING cp COPYING.LIB COPYING @@ -1196,6 +1298,16 @@ else AC_MSG_NOTICE([ xen: 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 -r c51d78efb84a po/POTFILES.in --- a/po/POTFILES.in Thu Nov 20 17:43:18 2008 +0000 +++ b/po/POTFILES.in Thu Nov 20 17:47:42 2008 +0000 @@ -13,6 +13,7 @@ src/lxc_driver.c src/network_conf.c src/network_driver.c +src/node_device.c src/openvz_conf.c src/openvz_driver.c src/proxy_internal.c diff -r c51d78efb84a qemud/Makefile.am --- a/qemud/Makefile.am Thu Nov 20 17:43:18 2008 +0000 +++ b/qemud/Makefile.am Thu Nov 20 17:47:42 2008 +0000 @@ -111,6 +111,10 @@ if WITH_NETWORK libvirtd_LDADD += ../src/libvirt_driver_network.la endif + +if WITH_NODE_DEVICES +libvirtd_LDADD += ../src/libvirt_driver_nodedev.la +endif endif libvirtd_LDADD += ../src/libvirt.la diff -r c51d78efb84a qemud/qemud.c --- a/qemud/qemud.c Thu Nov 20 17:43:18 2008 +0000 +++ b/qemud/qemud.c Thu Nov 20 17:47:42 2008 +0000 @@ -78,6 +78,9 @@ #endif #ifdef WITH_STORAGE_DIR #include "storage_driver.h" +#endif +#ifdef WITH_NODE_DEVICES +#include "node_device.h" #endif #endif @@ -763,6 +766,7 @@ virDriverLoadModule("uml"); virDriverLoadModule("network"); virDriverLoadModule("storage"); + virDriverLoadModule("nodedev"); #else #ifdef WITH_QEMU qemuRegister(); @@ -778,6 +782,9 @@ #endif #ifdef WITH_STORAGE_DIR storageRegister(); +#endif +#if defined(HAVE_HAL) || defined(HAVE_DEVKIT) + nodedevRegister(); #endif #endif diff -r c51d78efb84a src/Makefile.am --- a/src/Makefile.am Thu Nov 20 17:43:18 2008 +0000 +++ b/src/Makefile.am Thu Nov 20 17:47:42 2008 +0000 @@ -156,6 +156,14 @@ STORAGE_HELPER_DISK_SOURCES = \ parthelper.c + +NODE_DEVICE_DRIVER_SOURCES = \ + node_device.c node_device.h + +NODE_DEVICE_DRIVER_HAL_SOURCES = \ + node_device_hal.c +NODE_DEVICE_DRIVER_DEVKIT_SOURCES = \ + node_device_devkit.c ######################### @@ -327,6 +335,36 @@ libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_DISK_SOURCES) endif +if WITH_NODE_DEVICES +# Needed to keep automake quiet about conditionals +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_nodedev.la +else +noinst_LTLIBRARIES += libvirt_driver_nodedev.la +# Stateful, so linked to daemon instead +#libvirt_la_LIBADD += libvirt_driver_nodedev.la +endif +libvirt_driver_nodedev_la_SOURCES = $(NODE_DEVICE_DRIVER_SOURCES) + +libvirt_driver_nodedev_la_CFLAGS = +libvirt_driver_nodedev_la_LDFLAGS = +if HAVE_HAL +libvirt_driver_nodedev_la_SOURCES += $(NODE_DEVICE_DRIVER_HAL_SOURCES) +libvirt_driver_nodedev_la_CFLAGS += $(HAL_CFLAGS) +libvirt_driver_nodedev_la_LDFLAGS += $(HAL_LIBS) +endif +if HAVE_DEVKIT +libvirt_driver_nodedev_la_SOURCES += $(NODE_DEVICE_DRIVER_DEVKIT_SOURCES) +libvirt_driver_nodedev_la_CFLAGS += $(DEVKIT_CFLAGS) +libvirt_driver_nodedev_la_LDFLAGS += $(DEVKIT_LIBS) +endif + +if WITH_DRIVER_MODULES +libvirt_driver_nodedev_la_LDFLAGS += -module -avoid-version +endif +endif + + # Add all conditional sources just in case... EXTRA_DIST += \ $(TEST_DRIVER_SOURCES) \ @@ -341,7 +379,10 @@ $(STORAGE_DRIVER_FS_SOURCES) \ $(STORAGE_DRIVER_LVM_SOURCES) \ $(STORAGE_DRIVER_ISCSI_SOURCES) \ - $(STORAGE_DRIVER_DISK_SOURCES) + $(STORAGE_DRIVER_DISK_SOURCES) \ + $(NODE_DEVICE_DRIVER_SOURCES) \ + $(NODE_DEVICE_DRIVER_HAL_SOURCES) \ + $(NODE_DEVICE_DRIVER_DEVKIT_SOURCES) # Empty source list - it merely links a bunch of convenience libs together diff -r c51d78efb84a src/libvirt_sym.version.in --- a/src/libvirt_sym.version.in Thu Nov 20 17:43:18 2008 +0000 +++ b/src/libvirt_sym.version.in Thu Nov 20 17:47:42 2008 +0000 @@ -327,6 +327,7 @@ virGetNetwork; virGetStoragePool; virGetStorageVol; + virGetNodeDevice; virUnrefDomain; @@ -450,6 +451,7 @@ virRegisterNetworkDriver; virRegisterStateDriver; virRegisterStorageDriver; + virRegisterDeviceMonitor; /* memory.h */ @@ -478,6 +480,16 @@ /* nodeinfo.h */ virNodeInfoPopulate; + + + /* node_device_conf.h */ + virNodeDeviceObjRemove; + virNodeDevCapTypeToString; + virNodeDeviceFindByName; + virNodeDeviceObjListFree; + virNodeDeviceDefFree; + virNodeDevCapsDefFree; + virNodeDeviceDefFormat; /* qparams.h */ @@ -538,6 +550,7 @@ virStrToLong_i; virStrToLong_ll; virStrToLong_ull; + virStrToLong_ui; virFileLinkPointsTo; saferead; safewrite; @@ -557,6 +570,7 @@ virFileOpenTty; virFileReadLimFD; virFileReadPid; + virFileLinkPointsTo; virParseNumber; virRun; virSkipSpaces; diff -r c51d78efb84a src/node_device.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/node_device.c Thu Nov 20 17:47:42 2008 +0000 @@ -0,0 +1,213 @@ +/* + * 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 "virterror_internal.h" +#include "datatypes.h" +#include "memory.h" + +#include "node_device_conf.h" +#include "node_device.h" + +static int dev_has_cap(const virNodeDeviceObjPtr dev, const char *cap) +{ + virNodeDevCapsDefPtr caps = dev->def->caps; + while (caps) { + if (STREQ(cap, virNodeDevCapTypeToString(caps->type))) + return 1; + caps = caps->next; + } + return 0; +} + + +static int nodeNumOfDevices(virConnectPtr conn, + const char *cap, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virDeviceMonitorStatePtr driver = conn->devMonPrivateData; + int ndevs = 0; + unsigned int i; + + for (i = 0; i < driver->devs.count; i++) + if ((cap == NULL) || + dev_has_cap(driver->devs.objs[i], cap)) + ++ndevs; + + return ndevs; +} + +static int +nodeListDevices(virConnectPtr conn, + const char *cap, + char **const names, int maxnames, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virDeviceMonitorStatePtr driver = conn->devMonPrivateData; + int ndevs = 0; + unsigned int i; + + for (i = 0; i < driver->devs.count && ndevs < maxnames; i++) + if (cap == NULL || + dev_has_cap(driver->devs.objs[i], cap)) + if ((names[ndevs++] = strdup(driver->devs.objs[i]->def->name)) == NULL) + goto failure; + + return ndevs; + + failure: + --ndevs; + while (--ndevs >= 0) + VIR_FREE(names[ndevs]); + return -1; +} + + +static virNodeDevicePtr nodeDeviceLookupByName(virConnectPtr conn, + const char *name) +{ + virDeviceMonitorStatePtr driver = conn->devMonPrivateData; + virNodeDeviceObjPtr obj = virNodeDeviceFindByName(&driver->devs, name); + + if (!obj) { + virNodeDeviceReportError(conn, VIR_ERR_INVALID_NODE_DEVICE, + "%s", _("no node device with matching name")); + return NULL; + } + + return virGetNodeDevice(conn, name); + +} + +static char *nodeDeviceDumpXML(virNodeDevicePtr dev, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virDeviceMonitorStatePtr driver = dev->conn->devMonPrivateData; + virNodeDeviceObjPtr obj = virNodeDeviceFindByName(&driver->devs, dev->name); + + if (!obj) { + virNodeDeviceReportError(dev->conn, VIR_ERR_INVALID_NODE_DEVICE, + "%s", _("no node device with matching name")); + return NULL; + } + + return virNodeDeviceDefFormat(dev->conn, obj->def); +} + + +static char *nodeDeviceGetParent(virNodeDevicePtr dev) +{ + virDeviceMonitorStatePtr driver = dev->conn->devMonPrivateData; + virNodeDeviceObjPtr obj = virNodeDeviceFindByName(&driver->devs, dev->name); + + if (!obj) { + virNodeDeviceReportError(dev->conn, VIR_ERR_INVALID_NODE_DEVICE, + "%s", _("no node device with matching name")); + return NULL; + } + + return obj->def->parent; +} + + +static int nodeDeviceNumOfCaps(virNodeDevicePtr dev) +{ + virDeviceMonitorStatePtr driver = dev->conn->devMonPrivateData; + virNodeDeviceObjPtr obj = virNodeDeviceFindByName(&driver->devs, dev->name); + virNodeDevCapsDefPtr caps; + int ncaps = 0; + + if (!obj) { + virNodeDeviceReportError(dev->conn, VIR_ERR_INVALID_NODE_DEVICE, + "%s", _("no node device with matching name")); + return -1; + } + + for (caps = obj->def->caps; caps; caps = caps->next) + ++ncaps; + + return ncaps; +} + + +static int +nodeDeviceListCaps(virNodeDevicePtr dev, char **const names, int maxnames) +{ + virDeviceMonitorStatePtr driver = dev->conn->devMonPrivateData; + virNodeDeviceObjPtr obj = virNodeDeviceFindByName(&driver->devs, dev->name); + virNodeDevCapsDefPtr caps; + int ncaps = 0; + + if (!obj) { + virNodeDeviceReportError(dev->conn, VIR_ERR_INVALID_NODE_DEVICE, + "%s", _("no node device with matching name")); + return -1; + } + + for (caps = obj->def->caps; caps && ncaps < maxnames; caps = caps->next) { + names[ncaps] = strdup(virNodeDevCapTypeToString(caps->type)); + if (names[ncaps++] == NULL) + goto failure; + } + + return ncaps; + + failure: + --ncaps; + while (--ncaps >= 0) + VIR_FREE(names[ncaps]); + return -1; +} + + +void registerCommonNodeFuncs(virDeviceMonitorPtr driver) +{ + driver->numOfDevices = nodeNumOfDevices; + driver->listDevices = nodeListDevices; + driver->deviceLookupByName = nodeDeviceLookupByName; + driver->deviceDumpXML = nodeDeviceDumpXML; + driver->deviceGetParent = nodeDeviceGetParent; + driver->deviceNumOfCaps = nodeDeviceNumOfCaps; + driver->deviceListCaps = nodeDeviceListCaps; +} + + +int nodedevRegister(void) { +#if defined(HAVE_HAL) && defined(HAVE_DEVKIT) + /* Register only one of these two - they conflict */ + if (halNodeRegister() == -1) + return devkitNodeRegister(); + return 0; +#else +#ifdef HAVE_HAL + return halNodeRegister(); +#endif +#ifdef HAVE_DEVKIT + return devkitNodeRegister(); +#endif +#endif +} diff -r c51d78efb84a src/node_device.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/node_device.h Thu Nov 20 17:47:42 2008 +0000 @@ -0,0 +1,41 @@ +/* + * 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 "internal.h" +#include "driver.h" + +#ifdef HAVE_HAL +int halNodeRegister(void); +#endif +#ifdef HAVE_DEVKIT +int devkitNodeRegister(void); +#endif + +void registerCommonNodeFuncs(virDeviceMonitorPtr mon); + +int nodedevRegister(void); + +#endif /* __VIR_NODE_DEVICE_H__ */ diff -r c51d78efb84a src/node_device_conf.h --- a/src/node_device_conf.h Thu Nov 20 17:43:18 2008 +0000 +++ b/src/node_device_conf.h Thu Nov 20 17:47:42 2008 +0000 @@ -161,6 +161,7 @@ typedef struct _virDeviceMonitorState virDeviceMonitorState; typedef virDeviceMonitorState *virDeviceMonitorStatePtr; struct _virDeviceMonitorState { + int dbusWatch; virNodeDeviceObjList devs; /* currently-known devices */ void *privateData; /* driver-specific private data */ }; diff -r c51d78efb84a src/node_device_devkit.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/node_device_devkit.c Thu Nov 20 17:47:42 2008 +0000 @@ -0,0 +1,429 @@ +/* + * 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 "node_device_conf.h" +#include "virterror_internal.h" +#include "driver.h" +#include "datatypes.h" +#include "event.h" +#include "memory.h" +#include "uuid.h" + +#include "node_device.h" + +/* + * Host device enumeration (DeviceKit implementation) + */ + +static virDeviceMonitorStatePtr driverState; + +#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__) +#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg) + +#define CONN_DRV_STATE(conn) \ + ((virDeviceMonitorStatePtr)((conn)->devMonPrivateData)) +#define DRV_STATE_DKCLIENT(ds) ((DevkitClient *)((ds)->privateData)) +#define CONN_DKCLIENT(conn) DRV_STATE_DKCLIENT(CONN_DRV_STATE(conn)) + +#define NODE_DEV_DKDEV(obj) ((DevkitDevice *)((obj)->privateData) + +static int get_str_prop(DevkitDevice *dkdev, const char *prop, char **val_p) +{ + char *val = devkit_device_dup_property_as_str(dkdev, prop); + + if (val) { + if (*val) { + *val_p = val; + return 0; + } else { + /* Treat empty strings as NULL values */ + VIR_FREE(val); + } + } + + return -1; +} + +#if 0 +static int get_int_prop(DevkitDevice *dkdev, const char *prop, int *val_p) +{ + if (! devkit_device_has_property(dkdev, prop)) + return -1; + *val_p = devkit_device_get_property_as_int(dkdev, prop); + return 0; +} + +static int get_uint64_prop(DevkitDevice *dkdev, const char *prop, + unsigned long long *val_p) +{ + if (! devkit_device_has_property(dkdev, prop)) + return -1; + *val_p = devkit_device_get_property_as_uint64(dkdev, prop); + return 0; +} +#endif + +static int gather_pci_cap(DevkitDevice *dkdev, + union _virNodeDevCapData *d) +{ + const char *sysfs_path = devkit_device_get_native_path(dkdev); + + if (sysfs_path != NULL) { + char *p = strrchr(sysfs_path, '/'); + if (p) { + (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.domain); + (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.bus); + (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.slot); + (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.function); + } + } + return 0; +} + + +static int gather_usb_cap(DevkitDevice *dkdev, + union _virNodeDevCapData *d) +{ + (void)get_str_prop(dkdev, "ID_VENDOR", &d->usb_dev.vendor_name); + (void)get_str_prop(dkdev, "ID_MODEL", &d->usb_dev.product_name); + return 0; +} + + +static int gather_net_cap(DevkitDevice *dkdev, + union _virNodeDevCapData *d) +{ + const char *sysfs_path = devkit_device_get_native_path(dkdev); + const char *interface; + + if (sysfs_path == NULL) + return -1; + interface = strrchr(sysfs_path, '/'); + if (!interface || !*interface || !*(++interface)) + return -1; + if ((d->net.interface = strdup(interface)) == NULL) + return -1; + + d->net.subtype = VIR_NODE_DEV_CAP_NET_LAST; + + return 0; +} + + +static int gather_block_cap(DevkitDevice *dkdev, + union _virNodeDevCapData *d) +{ + const char *device = devkit_device_get_device_file(dkdev); + + if (device && ((d->block.device = strdup(device)) == NULL)) + return -1; + + return 0; +} + + +struct _caps_tbl_entry { + const char *cap_name; + enum virNodeDevCapType type; + int (*gather_fn)(DevkitDevice *dkdev, + union _virNodeDevCapData *data); +}; + +typedef struct _caps_tbl_entry caps_tbl_entry; + +static caps_tbl_entry caps_tbl[] = { + { "pci", VIR_NODE_DEV_CAP_PCI_DEV, gather_pci_cap }, + { "usb", VIR_NODE_DEV_CAP_USB_DEV, gather_usb_cap }, + { "net", VIR_NODE_DEV_CAP_NET, gather_net_cap }, + { "block", VIR_NODE_DEV_CAP_BLOCK, gather_block_cap }, + // TODO: more caps! +}; + + +/* qsort/bsearch string 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_capability(DevkitDevice *dkdev, + const char *cap_name, + virNodeDevCapsDefPtr *caps_p) +{ + size_t caps_tbl_len = sizeof(caps_tbl) / sizeof(caps_tbl[0]); + caps_tbl_entry *entry; + + entry = bsearch(&cap_name, caps_tbl, caps_tbl_len, + sizeof(caps_tbl[0]), cmpstringp); + + if (entry) { + virNodeDevCapsDefPtr caps; + if (VIR_ALLOC(caps) < 0) + return ENOMEM; + caps->type = entry->type; + if (entry->gather_fn) { + int rv = (*entry->gather_fn)(dkdev, &caps->data); + if (rv != 0) { + virNodeDevCapsDefFree(caps); + return rv; + } + } + caps->next = *caps_p; + *caps_p = caps; + } + + return 0; +} + + +static int gather_capabilities(DevkitDevice *dkdev, + virNodeDevCapsDefPtr *caps_p) +{ + const char *subsys = devkit_device_get_subsystem(dkdev); + const char *bus_name = devkit_device_get_property(dkdev, "ID_BUS"); + virNodeDevCapsDefPtr caps = NULL; + int rv; + + if (subsys) { + rv = gather_capability(dkdev, subsys, &caps); + if (rv != 0) goto failure; + } + + if (bus_name && (subsys == NULL || !STREQ(bus_name, subsys))) { + rv = gather_capability(dkdev, bus_name, &caps); + if (rv != 0) goto failure; + } + + *caps_p = caps; + return 0; + + failure: + while (caps) { + virNodeDevCapsDefPtr next = caps->next; + virNodeDevCapsDefFree(caps); + caps = next; + } + return rv; +} + +static void dev_create(void *_dkdev, void *_dkclient ATTRIBUTE_UNUSED) +{ + DevkitDevice *dkdev = _dkdev; + const char *sysfs_path = devkit_device_get_native_path(dkdev); + virNodeDeviceObjPtr dev = NULL; + const char *name; + int rv; + + if (sysfs_path == NULL) + /* Currently using basename(sysfs_path) as device name (key) */ + return; + + name = strrchr(sysfs_path, '/'); + if (name == NULL) + name = sysfs_path; + else + ++name; + + if (VIR_ALLOC(dev) < 0 || VIR_ALLOC(dev->def) < 0) + goto failure; + + dev->privateData = dkdev; + + if ((dev->def->name = strdup(name)) == NULL) + goto failure; + + // TODO: Find device parent, if any + + rv = gather_capabilities(dkdev, &dev->def->caps); + if (rv != 0) goto failure; + + if (VIR_REALLOC_N(driverState->devs.objs, driverState->devs.count + 1) < 0) + goto failure; + + driverState->devs.objs[driverState->devs.count++] = dev; + + return; + + failure: + DEBUG("FAILED TO ADD dev %s", name); + if (dev) + virNodeDeviceDefFree(dev->def); + VIR_FREE(dev); +} + + +static int devkitDeviceMonitorStartup(void) +{ + size_t caps_tbl_len = sizeof(caps_tbl) / sizeof(caps_tbl[0]); + DevkitClient *devkit_client = NULL; + GError *err = NULL; + GList *devs; + int i; + + /* Ensure caps_tbl is sorted by capability name */ + qsort(caps_tbl, caps_tbl_len, sizeof(caps_tbl[0]), cmpstringp); + + if (VIR_ALLOC(driverState) < 0) + return -1; + + // 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) { + DEBUG0("devkit_client_new returned NULL"); + goto failure; + } + if (!devkit_client_connect(devkit_client, &err)) { + DEBUG0("devkit_client_connect failed"); + goto failure; + } + + /* Populate with known devices. + * + * This really should be: + devs = devkit_client_enumerate_by_subsystem(devkit_client, NULL, &err); + if (err) { + DEBUG0("devkit_client_enumerate_by_subsystem failed"); + devs = NULL; + goto failure; + } + g_list_foreach(devs, dev_create, devkit_client); + * but devkit_client_enumerate_by_subsystem currently fails when the second + * arg is null (contrary to the API documentation). So the following code + * (from Dan B) works around this by listing devices per handled subsystem. + */ + + for (i = 0 ; i < ARRAY_CARDINALITY(caps_tbl) ; i++) { + const char *caps[] = { caps_tbl[i].cap_name, NULL }; + devs = devkit_client_enumerate_by_subsystem(devkit_client, + caps, + &err); + if (err) { + DEBUG0("devkit_client_enumerate_by_subsystem failed"); + devs = NULL; + goto failure; + } + g_list_foreach(devs, dev_create, devkit_client); + } + + driverState->privateData = devkit_client; + + // TODO: Register to get DeviceKit events on device changes and + // coordinate updates with queries and other operations. + + return 0; + + failure: + if (err) { + DEBUG("\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); + VIR_FREE(driverState); + + return -1; +} + + +static int devkitDeviceMonitorShutdown(void) +{ + if (driverState) { + DevkitClient *devkit_client = DRV_STATE_DKCLIENT(driverState); + virNodeDeviceObjListFree(&driverState->devs); + if (devkit_client) + g_object_unref(devkit_client); + VIR_FREE(driverState); + return 0; + } + return -1; +} + + +static int devkitDeviceMonitorReload(void) +{ + (void)devkitDeviceMonitorShutdown(); + return devkitDeviceMonitorStartup(); +} + + +static int devkitDeviceMonitorActive(void) +{ + /* Always ready to deal with a shutdown */ + return 0; +} + + +static virDrvOpenStatus +devkitNodeDrvOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + if (driverState == NULL) + return VIR_DRV_OPEN_DECLINED; + + conn->devMonPrivateData = driverState; + + return VIR_DRV_OPEN_SUCCESS; +} + +static int devkitNodeDrvClose(virConnectPtr conn ATTRIBUTE_UNUSED) +{ + conn->devMonPrivateData = NULL; + return 0; +} + + +static virDeviceMonitor devkitDeviceMonitor = { + .name = "devkitDeviceMonitor", + .open = devkitNodeDrvOpen, + .close = devkitNodeDrvClose, +}; + + +static virStateDriver devkitStateDriver = { + .initialize = devkitDeviceMonitorStartup, + .cleanup = devkitDeviceMonitorShutdown, + .reload = devkitDeviceMonitorReload, + .active = devkitDeviceMonitorActive, +}; + +int devkitNodeRegister(void) +{ + registerCommonNodeFuncs(&devkitDeviceMonitor); + if (virRegisterDeviceMonitor(&devkitDeviceMonitor) < 0) + return -1; + return virRegisterStateDriver(&devkitStateDriver); +} diff -r c51d78efb84a src/node_device_hal.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/node_device_hal.c Thu Nov 20 17:47:42 2008 +0000 @@ -0,0 +1,765 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> + +#include <config.h> +#include <libhal.h> + +#include "node_device_conf.h" +#include "virterror_internal.h" +#include "driver.h" +#include "datatypes.h" +#include "event.h" +#include "memory.h" +#include "uuid.h" +#include "logging.h" +#include "node_device.h" + +/* + * Host device enumeration (HAL implementation) + */ + +static virDeviceMonitorStatePtr driverState; + +#define CONN_DRV_STATE(conn) \ + ((virDeviceMonitorStatePtr)((conn)->devMonPrivateData)) +#define DRV_STATE_HAL_CTX(ds) ((LibHalContext *)((ds)->privateData)) +#define CONN_HAL_CTX(conn) DRV_STATE_HAL_CTX(CONN_DRV_STATE(conn)) + +#define NODE_DEV_UDI(obj) ((const char *)((obj)->privateData) + + +static const char *hal_name(const char *udi) +{ + const char *name = strrchr(udi, '/'); + if (name) + return name+1; + return udi; +} + + +static int get_str_prop(LibHalContext *ctxt, const char *udi, + const char *prop, char **val_p) +{ + char *val = libhal_device_get_property_string(ctxt, udi, prop, NULL); + + if (val) { + if (*val) { + *val_p = val; + return 0; + } else { + /* Treat empty strings as NULL values */ + VIR_FREE(val); + } + } + + return -1; +} + +static int get_int_prop(LibHalContext *ctxt, const char *udi, + const char *prop, int *val_p) +{ + DBusError err; + int val; + int rv; + + dbus_error_init(&err); + val = libhal_device_get_property_int(ctxt, udi, prop, &err); + rv = dbus_error_is_set(&err); + dbus_error_free(&err); + if (rv == 0) + *val_p = val; + + return rv; +} + +static int get_bool_prop(LibHalContext *ctxt, const char *udi, + const char *prop, int *val_p) +{ + DBusError err; + int val; + int rv; + + dbus_error_init(&err); + val = libhal_device_get_property_bool(ctxt, udi, prop, &err); + rv = dbus_error_is_set(&err); + dbus_error_free(&err); + if (rv == 0) + *val_p = val; + + return rv; +} + +static int get_uint64_prop(LibHalContext *ctxt, const char *udi, + const char *prop, unsigned long long *val_p) +{ + DBusError err; + unsigned long long val; + int rv; + + dbus_error_init(&err); + val = libhal_device_get_property_uint64(ctxt, udi, prop, &err); + rv = dbus_error_is_set(&err); + dbus_error_free(&err); + if (rv == 0) + *val_p = val; + + return rv; +} + +static int gather_pci_cap(LibHalContext *ctx, const char *udi, + union _virNodeDevCapData *d) +{ + char *sysfs_path; + + if (get_str_prop(ctx, udi, "pci.linux.sysfs_path", &sysfs_path) == 0) { + char *p = strrchr(sysfs_path, '/'); + if (p) { + (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.domain); + (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.bus); + (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.slot); + (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.function); + } + VIR_FREE(sysfs_path); + } + (void)get_int_prop(ctx, udi, "pci.vendor_id", (int *)&d->pci_dev.vendor); + if (get_str_prop(ctx, udi, "pci.vendor", &d->pci_dev.vendor_name) != 0) + (void)get_str_prop(ctx, udi, "info.vendor", &d->pci_dev.vendor_name); + (void)get_int_prop(ctx, udi, "pci.product_id", (int *)&d->pci_dev.product); + if (get_str_prop(ctx, udi, "pci.product", &d->pci_dev.product_name) != 0) + (void)get_str_prop(ctx, udi, "info.product", &d->pci_dev.product_name); + return 0; +} + + +static int gather_usb_cap(LibHalContext *ctx, const char *udi, + union _virNodeDevCapData *d) +{ + (void)get_int_prop(ctx, udi, "usb.interface.number", + (int *)&d->usb_if.number); + (void)get_int_prop(ctx, udi, "usb.interface.class", + (int *)&d->usb_if._class); + (void)get_int_prop(ctx, udi, "usb.interface.subclass", + (int *)&d->usb_if.subclass); + (void)get_int_prop(ctx, udi, "usb.interface.protocol", + (int *)&d->usb_if.protocol); + (void)get_str_prop(ctx, udi, "usb.interface.description", + &d->usb_if.description); + return 0; +} + + +static int gather_usb_device_cap(LibHalContext *ctx, const char *udi, + union _virNodeDevCapData *d) +{ + (void)get_int_prop(ctx, udi, "usb_device.bus_number", + (int *)&d->usb_dev.bus); + (void)get_int_prop(ctx, udi, "usb_device.linux.device_number", + (int *)&d->usb_dev.device); + (void)get_int_prop(ctx, udi, "usb_device.vendor_id", + (int *)&d->usb_dev.vendor); + if (get_str_prop(ctx, udi, "usb_device.vendor", + &d->usb_dev.vendor_name) != 0) + (void)get_str_prop(ctx, udi, "info.vendor", &d->usb_dev.vendor_name); + (void)get_int_prop(ctx, udi, "usb_device.product_id", + (int *)&d->usb_dev.product); + if (get_str_prop(ctx, udi, "usb_device.product", + &d->usb_dev.product_name) != 0) + (void)get_str_prop(ctx, udi, "info.product", &d->usb_dev.product_name); + return 0; +} + + +static int gather_net_cap(LibHalContext *ctx, const char *udi, + union _virNodeDevCapData *d) +{ + (void)get_str_prop(ctx, udi, "net.interface", &d->net.interface); + (void)get_str_prop(ctx, udi, "net.address", &d->net.address); + if (get_uint64_prop(ctx, udi, "net.80203.mac_address", + &d->net.data.ieee80203.mac_address) == 0) + d->net.subtype = VIR_NODE_DEV_CAP_NET_80203; + else if (get_uint64_prop(ctx, udi, "net.80211.mac_address", + &d->net.data.ieee80211.mac_address) == 0) + d->net.subtype = VIR_NODE_DEV_CAP_NET_80211; + else + d->net.subtype = VIR_NODE_DEV_CAP_NET_LAST; + + return 0; +} + + +static int gather_block_cap(LibHalContext *ctx, const char *udi, + union _virNodeDevCapData *d) +{ + (void)get_str_prop(ctx, udi, "block.device", &d->block.device); + return 0; +} + + +static int gather_scsi_host_cap(LibHalContext *ctx, const char *udi, + union _virNodeDevCapData *d) +{ + (void)get_int_prop(ctx, udi, "scsi_host.host", (int *)&d->scsi_host.host); + return 0; +} + + +static int gather_scsi_cap(LibHalContext *ctx, const char *udi, + union _virNodeDevCapData *d) +{ + (void)get_int_prop(ctx, udi, "scsi.host", (int *)&d->scsi.host); + (void)get_int_prop(ctx, udi, "scsi.bus", (int *)&d->scsi.bus); + (void)get_int_prop(ctx, udi, "scsi.target", (int *)&d->scsi.target); + (void)get_int_prop(ctx, udi, "scsi.lun", (int *)&d->scsi.lun); + (void)get_str_prop(ctx, udi, "scsi.type", &d->scsi.type); + return 0; +} + + +static int gather_storage_cap(LibHalContext *ctx, const char *udi, + union _virNodeDevCapData *d) +{ + int val; + (void)get_str_prop(ctx, udi, "storage.bus", &d->storage.bus); + (void)get_str_prop(ctx, udi, "storage.drive_type", &d->storage.drive_type); + (void)get_str_prop(ctx, udi, "storage.model", &d->storage.model); + (void)get_str_prop(ctx, udi, "storage.vendor", &d->storage.vendor); + if (get_bool_prop(ctx, udi, "storage.removable", &val) == 0 && val) { + d->storage.flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE; + if (get_bool_prop(ctx, udi, + "storage.removable.media_available", &val) && val) { + d->storage.flags |= + VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE; + (void)get_uint64_prop(ctx, udi, "storage.removable.media_size", + &d->storage.removable_media_size); + } + } else { + (void)get_uint64_prop(ctx, udi, "storage.size", &d->storage.size); + } + if (get_bool_prop(ctx, udi, "storage.hotpluggable", &val) == 0 && val) + d->storage.flags |= VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE; + return 0; +} + + +static int gather_system_cap(LibHalContext *ctx, const char *udi, + union _virNodeDevCapData *d) +{ + char *uuidstr; + + (void)get_str_prop(ctx, udi, "system.product", &d->system.product_name); + (void)get_str_prop(ctx, udi, "system.hardware.vendor", + &d->system.hardware.vendor_name); + (void)get_str_prop(ctx, udi, "system.hardware.version", + &d->system.hardware.version); + (void)get_str_prop(ctx, udi, "system.hardware.serial", + &d->system.hardware.serial); + if (get_str_prop(ctx, udi, "system.hardware.uuid", &uuidstr) == 0) { + (void)virUUIDParse(uuidstr, d->system.hardware.uuid); + VIR_FREE(uuidstr); + } + (void)get_str_prop(ctx, udi, "system.firmware.vendor", + &d->system.firmware.vendor_name); + (void)get_str_prop(ctx, udi, "system.firmware.version", + &d->system.firmware.version); + (void)get_str_prop(ctx, udi, "system.firmware.release_date", + &d->system.firmware.release_date); + return 0; +} + + +struct _caps_tbl_entry { + const char *cap_name; + enum virNodeDevCapType type; + int (*gather_fn)(LibHalContext *ctx, + const char *udi, + union _virNodeDevCapData *data); +}; + +typedef struct _caps_tbl_entry caps_tbl_entry; + +static caps_tbl_entry caps_tbl[] = { + { "system", VIR_NODE_DEV_CAP_SYSTEM, gather_system_cap }, + { "pci", VIR_NODE_DEV_CAP_PCI_DEV, gather_pci_cap }, + { "usb", VIR_NODE_DEV_CAP_USB_INTERFACE, gather_usb_cap }, + { "usb_device", VIR_NODE_DEV_CAP_USB_DEV, gather_usb_device_cap }, + { "net", VIR_NODE_DEV_CAP_NET, gather_net_cap }, + { "block", VIR_NODE_DEV_CAP_BLOCK, gather_block_cap }, + { "scsi_host", VIR_NODE_DEV_CAP_SCSI_HOST, gather_scsi_host_cap }, + { "scsi", VIR_NODE_DEV_CAP_SCSI, gather_scsi_cap }, + { "storage", VIR_NODE_DEV_CAP_STORAGE, gather_storage_cap }, +}; + + +/* qsort/bsearch string 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_capability(LibHalContext *ctx, const char *udi, + const char *cap_name, + virNodeDevCapsDefPtr *caps_p) +{ + caps_tbl_entry *entry; + + entry = bsearch(&cap_name, caps_tbl, ARRAY_CARDINALITY(caps_tbl), + sizeof(caps_tbl[0]), cmpstringp); + + if (entry) { + virNodeDevCapsDefPtr caps; + if (VIR_ALLOC(caps) < 0) + return ENOMEM; + caps->type = entry->type; + if (entry->gather_fn) { + int rv = (*entry->gather_fn)(ctx, udi, &caps->data); + if (rv != 0) { + virNodeDevCapsDefFree(caps); + return rv; + } + } + caps->next = *caps_p; + *caps_p = caps; + } + + return 0; +} + + +static int gather_capabilities(LibHalContext *ctx, const char *udi, + virNodeDevCapsDefPtr *caps_p) +{ + char *bus_name = NULL; + virNodeDevCapsDefPtr caps = NULL; + char **hal_cap_names; + int rv, i; + + if (STREQ(udi, "/org/freedesktop/Hal/devices/computer")) { + rv = gather_capability(ctx, udi, "system", &caps); + if (rv != 0) + goto failure; + } + + if (get_str_prop(ctx, udi, "info.subsystem", &bus_name) == 0) { + rv = gather_capability(ctx, udi, bus_name, &caps); + if (rv != 0) + goto failure; + } + + hal_cap_names = libhal_device_get_property_strlist(ctx, udi, + "info.capabilities", + NULL); + if (hal_cap_names) { + for (i = 0; hal_cap_names[i]; i++) { + if (! (bus_name && STREQ(hal_cap_names[i], bus_name))) { + rv = gather_capability(ctx, udi, hal_cap_names[i], &caps); + if (rv != 0) + goto failure; + } + } + for (i = 0; hal_cap_names[i]; i++) + VIR_FREE(hal_cap_names[i]); + VIR_FREE(hal_cap_names); + } + VIR_FREE(bus_name); + + *caps_p = caps; + return 0; + + failure: + VIR_FREE(bus_name); + if (hal_cap_names) { + for (i = 0; hal_cap_names[i]; i++) + VIR_FREE(hal_cap_names[i]); + VIR_FREE(hal_cap_names); + } + while (caps) { + virNodeDevCapsDefPtr next = caps->next; + virNodeDevCapsDefFree(caps); + caps = next; + } + return rv; +} + +static void free_udi(void *udi) +{ + VIR_FREE(udi); +} + +static void dev_create(char *udi) +{ + LibHalContext *ctx = DRV_STATE_HAL_CTX(driverState); + char *parent_key = NULL; + virNodeDeviceObjPtr dev; + const char *name = hal_name(udi); + int rv; + + if (VIR_ALLOC(dev) < 0 || VIR_ALLOC(dev->def) < 0) + goto failure; + + dev->privateData = udi; + dev->privateFree = free_udi; + + if ((dev->def->name = strdup(name)) == NULL) + goto failure; + + if (get_str_prop(ctx, udi, "info.parent", &parent_key) == 0) { + dev->def->parent = strdup(hal_name(parent_key)); + VIR_FREE(parent_key); + if (dev->def->parent == NULL) + goto failure; + } + + rv = gather_capabilities(ctx, udi, &dev->def->caps); + if (rv != 0) goto failure; + + if (VIR_REALLOC_N(driverState->devs.objs, driverState->devs.count + 1) < 0) + goto failure; + + driverState->devs.objs[driverState->devs.count++] = dev; + + return; + + failure: + DEBUG("FAILED TO ADD dev %s", name); + if (dev) + virNodeDeviceDefFree(dev->def); + VIR_FREE(dev); + VIR_FREE(udi); +} + + +static void device_added(LibHalContext *ctx ATTRIBUTE_UNUSED, + const char *udi) +{ + DEBUG0(hal_name(udi)); + dev_create(strdup(udi)); +} + + +static void device_removed(LibHalContext *ctx ATTRIBUTE_UNUSED, + const char *udi) +{ + const char *name = hal_name(udi); + virNodeDeviceObjPtr dev = virNodeDeviceFindByName(&driverState->devs,name); + DEBUG0(name); + if (dev) + virNodeDeviceObjRemove(&driverState->devs, dev); + else + DEBUG("no device named %s", name); +} + + +static void device_cap_added(LibHalContext *ctx, + const char *udi, const char *cap) +{ + const char *name = hal_name(udi); + virNodeDeviceObjPtr dev = virNodeDeviceFindByName(&driverState->devs,name); + DEBUG("%s %s", cap, name); + if (dev) + (void)gather_capability(ctx, udi, cap, &dev->def->caps); + else + DEBUG("no device named %s", name); +} + + +static void device_cap_lost(LibHalContext *ctx ATTRIBUTE_UNUSED, + const char *udi, + const char *cap) +{ + const char *name = hal_name(udi); + virNodeDeviceObjPtr dev = virNodeDeviceFindByName(&driverState->devs,name); + DEBUG("%s %s", cap, name); + if (dev) { + /* Simply "rediscover" device -- incrementally handling changes + * to sub-capabilities (like net.80203) is nasty ... so avoid it. + */ + virNodeDeviceObjRemove(&driverState->devs, dev); + dev_create(strdup(udi)); + } else + DEBUG("no device named %s", name); +} + + +static void device_prop_modified(LibHalContext *ctx ATTRIBUTE_UNUSED, + const char *udi, + const char *key, + dbus_bool_t is_removed ATTRIBUTE_UNUSED, + dbus_bool_t is_added ATTRIBUTE_UNUSED) +{ + const char *name = hal_name(udi); + virNodeDeviceObjPtr dev = virNodeDeviceFindByName(&driverState->devs,name); + DEBUG("%s %s", key, name); + if (dev) { + /* Simply "rediscover" device -- incrementally handling changes + * to properties (which are mapped into caps in very capability- + * specific ways) is nasty ... so avoid it. + */ + virNodeDeviceObjRemove(&driverState->devs, dev); + dev_create(strdup(udi)); + } else + DEBUG("no device named %s", name); +} + + +static void dbus_watch_callback(int fdatch ATTRIBUTE_UNUSED, + int fd ATTRIBUTE_UNUSED, + int events, void *opaque) +{ + DBusWatch *watch = opaque; + LibHalContext *hal_ctx = DRV_STATE_HAL_CTX(driverState); + DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx); + int dbus_flags = 0; + + if (events & VIR_EVENT_HANDLE_READABLE) + dbus_flags |= DBUS_WATCH_READABLE; + if (events & VIR_EVENT_HANDLE_WRITABLE) + dbus_flags |= DBUS_WATCH_WRITABLE; + if (events & VIR_EVENT_HANDLE_ERROR) + dbus_flags |= DBUS_WATCH_ERROR; + if (events & VIR_EVENT_HANDLE_HANGUP) + dbus_flags |= DBUS_WATCH_HANGUP; + + (void)dbus_watch_handle(watch, dbus_flags); + + while (dbus_connection_dispatch(dbus_conn) == DBUS_DISPATCH_DATA_REMAINS) + /* keep dispatching while data remains */; +} + + +static int xlate_dbus_watch_flags(int dbus_flags) +{ + unsigned int flags = 0; + if (dbus_flags & DBUS_WATCH_READABLE) + flags |= VIR_EVENT_HANDLE_READABLE; + if (dbus_flags & DBUS_WATCH_WRITABLE) + flags |= VIR_EVENT_HANDLE_WRITABLE; + if (dbus_flags & DBUS_WATCH_ERROR) + flags |= VIR_EVENT_HANDLE_ERROR; + if (dbus_flags & DBUS_WATCH_HANGUP) + flags |= VIR_EVENT_HANDLE_HANGUP; + return flags; +} + + +static dbus_bool_t add_dbus_watch(DBusWatch *watch, + void *data) +{ + int flags = 0; + virDeviceMonitorStatePtr state = data; + + if (dbus_watch_get_enabled(watch)) + flags = xlate_dbus_watch_flags(dbus_watch_get_flags(watch)); + + if ((state->dbusWatch = + virEventAddHandle(dbus_watch_get_unix_fd(watch), flags, + dbus_watch_callback, watch, NULL)) < 0) + return 0; + return 1; +} + + +static void remove_dbus_watch(DBusWatch *watch ATTRIBUTE_UNUSED, + void *data) +{ + virDeviceMonitorStatePtr state = data; + + (void)virEventRemoveHandle(state->dbusWatch); +} + + +static void toggle_dbus_watch(DBusWatch *watch, + void *data) +{ + int flags = 0; + virDeviceMonitorStatePtr state = data; + + if (dbus_watch_get_enabled(watch)) + flags = xlate_dbus_watch_flags(dbus_watch_get_flags(watch)); + + (void)virEventUpdateHandle(state->dbusWatch, flags); +} + + +static int halDeviceMonitorStartup(void) +{ + LibHalContext *hal_ctx = NULL; + DBusConnection *dbus_conn = NULL; + DBusError err; + char **udi = NULL; + int num_devs, i; + + /* Ensure caps_tbl is sorted by capability name */ + qsort(caps_tbl, ARRAY_CARDINALITY(caps_tbl), sizeof(caps_tbl[0]), + cmpstringp); + + if (VIR_ALLOC(driverState) < 0) + return -1; + + /* 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 failure; + } + dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_conn == NULL) { + fprintf(stderr, "%s: dbus_bus_get failed\n", __FUNCTION__); + goto failure; + } + if (!libhal_ctx_set_dbus_connection(hal_ctx, dbus_conn)) { + fprintf(stderr, "%s: libhal_ctx_set_dbus_connection failed\n", + __FUNCTION__); + goto failure; + } + if (!libhal_ctx_init(hal_ctx, &err)) { + fprintf(stderr, "%s: libhal_ctx_init failed\n", __FUNCTION__); + goto failure; + } + + /* Register dbus watch callbacks */ + if (!dbus_connection_set_watch_functions(dbus_conn, + add_dbus_watch, + remove_dbus_watch, + toggle_dbus_watch, + driverState, NULL)) { + fprintf(stderr, "%s: dbus_connection_set_watch_functions failed\n", + __FUNCTION__); + goto failure; + } + + /* Register HAL event callbacks */ + if (!libhal_ctx_set_device_added(hal_ctx, device_added) || + !libhal_ctx_set_device_removed(hal_ctx, device_removed) || + !libhal_ctx_set_device_new_capability(hal_ctx, device_cap_added) || + !libhal_ctx_set_device_lost_capability(hal_ctx, device_cap_lost) || + !libhal_ctx_set_device_property_modified(hal_ctx, device_prop_modified)) { + fprintf(stderr, "%s: setting up HAL callbacks failed\n", __FUNCTION__); + goto failure; + } + + /* Populate with known devices */ + driverState->privateData = hal_ctx; + udi = libhal_get_all_devices(hal_ctx, &num_devs, &err); + if (udi == NULL) { + fprintf(stderr, "%s: libhal_get_all_devices failed\n", __FUNCTION__); + goto failure; + } + for (i = 0; i < num_devs; i++) + dev_create(udi[i]); + free(udi); + + return 0; + + failure: + if (dbus_error_is_set(&err)) { + fprintf(stderr, "\t%s: %s\n", err.name, err.message); + dbus_error_free(&err); + } + virNodeDeviceObjListFree(&driverState->devs); + if (hal_ctx) + (void)libhal_ctx_free(hal_ctx); + if (udi) { + for (i = 0; i < num_devs; i++) + free(udi[i]); + free(udi); + } + VIR_FREE(driverState); + + return -1; +} + + +static int halDeviceMonitorShutdown(void) +{ + if (driverState) { + LibHalContext *hal_ctx = DRV_STATE_HAL_CTX(driverState); + virNodeDeviceObjListFree(&driverState->devs); + (void)libhal_ctx_shutdown(hal_ctx, NULL); + (void)libhal_ctx_free(hal_ctx); + VIR_FREE(driverState); + return 0; + } + return -1; +} + + +static int halDeviceMonitorReload(void) +{ + (void)halDeviceMonitorShutdown(); + return halDeviceMonitorStartup(); +} + + +static int halDeviceMonitorActive(void) +{ + /* Always ready to deal with a shutdown */ + return 0; +} + + +static virDrvOpenStatus halNodeDrvOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + if (driverState == NULL) + return VIR_DRV_OPEN_DECLINED; + + conn->devMonPrivateData = driverState; + + return VIR_DRV_OPEN_SUCCESS; +} + +static int halNodeDrvClose(virConnectPtr conn ATTRIBUTE_UNUSED) +{ + conn->devMonPrivateData = NULL; + return 0; +} + + +static virDeviceMonitor halDeviceMonitor = { + .name = "halDeviceMonitor", + .open = halNodeDrvOpen, + .close = halNodeDrvClose, +}; + + +static virStateDriver halStateDriver = { + .initialize = halDeviceMonitorStartup, + .cleanup = halDeviceMonitorShutdown, + .reload = halDeviceMonitorReload, + .active = halDeviceMonitorActive, +}; + +int halNodeRegister(void) +{ + registerCommonNodeFuncs(&halDeviceMonitor); + if (virRegisterDeviceMonitor(&halDeviceMonitor) < 0) + return -1; + return virRegisterStateDriver(&halStateDriver); +} -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Hi Dan, All looks good in general - just want to call out the couple of devkit leaks again. No problem leaving them 'til later to fix, of course. Cheers, Mark. On Thu, 2008-11-20 at 17:55 +0000, Daniel P. Berrange wrote:
+ +static void dev_create(void *_dkdev, void *_dkclient ATTRIBUTE_UNUSED) +{ + DevkitDevice *dkdev = _dkdev; + const char *sysfs_path = devkit_device_get_native_path(dkdev); + virNodeDeviceObjPtr dev = NULL; + const char *name; + int rv; + + if (sysfs_path == NULL) + /* Currently using basename(sysfs_path) as device name (key) */ + return; + + name = strrchr(sysfs_path, '/'); + if (name == NULL) + name = sysfs_path; + else + ++name; + + if (VIR_ALLOC(dev) < 0 || VIR_ALLOC(dev->def) < 0) + goto failure; + + dev->privateData = dkdev;
Still leaking the GObject ref here, I think
+static int devkitDeviceMonitorStartup(void) +{ + size_t caps_tbl_len = sizeof(caps_tbl) / sizeof(caps_tbl[0]); + DevkitClient *devkit_client = NULL; + GError *err = NULL; + GList *devs; + int i; + + /* Ensure caps_tbl is sorted by capability name */ + qsort(caps_tbl, caps_tbl_len, sizeof(caps_tbl[0]), cmpstringp); + + if (VIR_ALLOC(driverState) < 0) + return -1; + + // 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) { + DEBUG0("devkit_client_new returned NULL"); + goto failure; + } + if (!devkit_client_connect(devkit_client, &err)) { + DEBUG0("devkit_client_connect failed"); + goto failure; + } + + /* Populate with known devices. + * + * This really should be: + devs = devkit_client_enumerate_by_subsystem(devkit_client, NULL, &err); + if (err) { + DEBUG0("devkit_client_enumerate_by_subsystem failed"); + devs = NULL; + goto failure; + } + g_list_foreach(devs, dev_create, devkit_client); + * but devkit_client_enumerate_by_subsystem currently fails when the second + * arg is null (contrary to the API documentation). So the following code + * (from Dan B) works around this by listing devices per handled subsystem. + */ + + for (i = 0 ; i < ARRAY_CARDINALITY(caps_tbl) ; i++) { + const char *caps[] = { caps_tbl[i].cap_name, NULL }; + devs = devkit_client_enumerate_by_subsystem(devkit_client, + caps, + &err); + if (err) { + DEBUG0("devkit_client_enumerate_by_subsystem failed"); + devs = NULL; + goto failure; + } + g_list_foreach(devs, dev_create, devkit_client);
g_list_free()

This provides the remote driver implementation for node device APIs This is basically just fixing up to add the optional 'cap' arg to the List methods, and remove the Create/Destroy/ByCap methods Daniel diff -r acc38454f32d qemud/remote.c --- a/qemud/remote.c Thu Nov 20 16:16:37 2008 +0000 +++ b/qemud/remote.c Thu Nov 20 16:26:13 2008 +0000 @@ -67,6 +67,7 @@ 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" @@ -3723,6 +3724,213 @@ } +/*************************************************************** + * NODE INFO APIS + **************************************************************/ + +static int +remoteDispatchNodeNumOfDevices (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_node_num_of_devices_args *args, + remote_node_num_of_devices_ret *ret) +{ + CHECK_CONN(client); + + ret->num = virNodeNumOfDevices (client->conn, + args->cap ? *args->cap : NULL, + args->flags); + 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, + args->cap ? *args->cap : NULL, + ret->names.names_val, args->maxnames, args->flags); + if (ret->names.names_len == -1) { + VIR_FREE(ret->names.names_val); + return -1; + } + + 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 = virNodeDeviceLookupByName(client->conn, args->name); + 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 +remoteDispatchNodeDeviceGetParent (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_node_device_get_parent_args *args, + remote_node_device_get_parent_ret *ret) +{ + virNodeDevicePtr dev; + const char *parent; + CHECK_CONN(client); + + dev = virNodeDeviceLookupByName(client->conn, args->name); + if (dev == NULL) { + remoteDispatchError (client, req, "%s", _("node_device not found")); + return -2; + } + + parent = virNodeDeviceGetParent(dev); + + if (parent == NULL) { + ret->parent = NULL; + } else { + /* remoteDispatchClientRequest will free this. */ + char **parent_p; + if (VIR_ALLOC(parent_p) < 0) { + remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, NULL); + return -2; + } + *parent_p = strdup(parent); + if (*parent_p == NULL) { + remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, NULL); + return -2; + } + ret->parent = parent_p; + } + + virNodeDeviceFree(dev); + return 0; +} + + +static int +remoteDispatchNodeDeviceNumOfCaps (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_node_device_num_of_caps_args *args, + remote_node_device_num_of_caps_ret *ret) +{ + virNodeDevicePtr dev; + CHECK_CONN(client); + + dev = virNodeDeviceLookupByName(client->conn, args->name); + if (dev == NULL) { + remoteDispatchError (client, req, "%s", _("node_device not found")); + return -2; + } + + ret->num = virNodeDeviceNumOfCaps(dev); + + virNodeDeviceFree(dev); + return 0; +} + + +static int +remoteDispatchNodeDeviceListCaps (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_node_device_list_caps_args *args, + remote_node_device_list_caps_ret *ret) +{ + virNodeDevicePtr dev; + CHECK_CONN(client); + + dev = virNodeDeviceLookupByName(client->conn, args->name); + if (dev == NULL) { + remoteDispatchError (client, req, "%s", _("node_device not found")); + return -2; + } + + 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 = + virNodeDeviceListCaps (dev, ret->names.names_val, + args->maxnames); + if (ret->names.names_len == -1) { + VIR_FREE(ret->names.names_val); + return -1; + } + + return 0; +} + + /************************** * Async Events **************************/ @@ -3848,6 +4056,7 @@ client->bufferLength = len; client->bufferOffset = 0; } + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire @@ -3918,3 +4127,9 @@ 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) +{ + dev_dst->name = strdup(dev_src->name); +} diff -r acc38454f32d qemud/remote_dispatch_localvars.h --- a/qemud/remote_dispatch_localvars.h Thu Nov 20 16:16:37 2008 +0000 +++ b/qemud/remote_dispatch_localvars.h Thu Nov 20 16:26:13 2008 +0000 @@ -7,6 +7,8 @@ remote_storage_pool_list_volumes_args lv_remote_storage_pool_list_volumes_args; remote_storage_pool_list_volumes_ret lv_remote_storage_pool_list_volumes_ret; remote_domain_events_deregister_ret lv_remote_domain_events_deregister_ret; +remote_node_device_get_parent_args lv_remote_node_device_get_parent_args; +remote_node_device_get_parent_ret lv_remote_node_device_get_parent_ret; remote_domain_shutdown_args lv_remote_domain_shutdown_args; remote_list_defined_domains_args lv_remote_list_defined_domains_args; remote_list_defined_domains_ret lv_remote_list_defined_domains_ret; @@ -34,6 +36,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_node_device_list_caps_args lv_remote_node_device_list_caps_args; +remote_node_device_list_caps_ret lv_remote_node_device_list_caps_ret; remote_open_args lv_remote_open_args; 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; @@ -67,6 +71,8 @@ remote_storage_pool_lookup_by_volume_ret lv_remote_storage_pool_lookup_by_volume_ret; remote_domain_get_max_vcpus_args lv_remote_domain_get_max_vcpus_args; remote_domain_get_max_vcpus_ret lv_remote_domain_get_max_vcpus_ret; +remote_node_device_num_of_caps_args lv_remote_node_device_num_of_caps_args; +remote_node_device_num_of_caps_ret lv_remote_node_device_num_of_caps_ret; remote_domain_get_info_args lv_remote_domain_get_info_args; remote_domain_get_info_ret lv_remote_domain_get_info_ret; remote_storage_pool_num_of_volumes_args lv_remote_storage_pool_num_of_volumes_args; @@ -82,6 +88,8 @@ remote_domain_destroy_args lv_remote_domain_destroy_args; 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_args lv_remote_node_num_of_devices_args; +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 +99,17 @@ 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; diff -r acc38454f32d qemud/remote_dispatch_proc_switch.h --- a/qemud/remote_dispatch_proc_switch.h Thu Nov 20 16:16:37 2008 +0000 +++ b/qemud/remote_dispatch_proc_switch.h Thu Nov 20 16:26:13 2008 +0000 @@ -560,6 +560,51 @@ 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_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_GET_PARENT: + fn = (dispatch_fn) remoteDispatchNodeDeviceGetParent; + args_filter = (xdrproc_t) xdr_remote_node_device_get_parent_args; + args = (char *) &lv_remote_node_device_get_parent_args; + memset (&lv_remote_node_device_get_parent_args, 0, sizeof lv_remote_node_device_get_parent_args); + ret_filter = (xdrproc_t) xdr_remote_node_device_get_parent_ret; + ret = (char *) &lv_remote_node_device_get_parent_ret; + memset (&lv_remote_node_device_get_parent_ret, 0, sizeof lv_remote_node_device_get_parent_ret); + break; +case REMOTE_PROC_NODE_DEVICE_LIST_CAPS: + fn = (dispatch_fn) remoteDispatchNodeDeviceListCaps; + args_filter = (xdrproc_t) xdr_remote_node_device_list_caps_args; + args = (char *) &lv_remote_node_device_list_caps_args; + memset (&lv_remote_node_device_list_caps_args, 0, sizeof lv_remote_node_device_list_caps_args); + ret_filter = (xdrproc_t) xdr_remote_node_device_list_caps_ret; + ret = (char *) &lv_remote_node_device_list_caps_ret; + memset (&lv_remote_node_device_list_caps_ret, 0, sizeof lv_remote_node_device_list_caps_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_DEVICE_NUM_OF_CAPS: + fn = (dispatch_fn) remoteDispatchNodeDeviceNumOfCaps; + args_filter = (xdrproc_t) xdr_remote_node_device_num_of_caps_args; + args = (char *) &lv_remote_node_device_num_of_caps_args; + memset (&lv_remote_node_device_num_of_caps_args, 0, sizeof lv_remote_node_device_num_of_caps_args); + ret_filter = (xdrproc_t) xdr_remote_node_device_num_of_caps_ret; + ret = (char *) &lv_remote_node_device_num_of_caps_ret; + memset (&lv_remote_node_device_num_of_caps_ret, 0, sizeof lv_remote_node_device_num_of_caps_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; @@ -580,6 +625,24 @@ ret_filter = (xdrproc_t) xdr_remote_node_get_info_ret; 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_NUM_OF_DEVICES: + fn = (dispatch_fn) remoteDispatchNodeNumOfDevices; + args_filter = (xdrproc_t) xdr_remote_node_num_of_devices_args; + args = (char *) &lv_remote_node_num_of_devices_args; + memset (&lv_remote_node_num_of_devices_args, 0, sizeof lv_remote_node_num_of_devices_args); + 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_NUM_OF_DEFINED_DOMAINS: fn = (dispatch_fn) remoteDispatchNumOfDefinedDomains; diff -r acc38454f32d qemud/remote_dispatch_prototypes.h --- a/qemud/remote_dispatch_prototypes.h Thu Nov 20 16:16:37 2008 +0000 +++ b/qemud/remote_dispatch_prototypes.h Thu Nov 20 16:26:13 2008 +0000 @@ -76,9 +76,16 @@ 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 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 remoteDispatchNodeDeviceGetParent (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_device_get_parent_args *args, remote_node_device_get_parent_ret *ret); +static int remoteDispatchNodeDeviceListCaps (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_device_list_caps_args *args, remote_node_device_list_caps_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 remoteDispatchNodeDeviceNumOfCaps (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_device_num_of_caps_args *args, remote_node_device_num_of_caps_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 remoteDispatchNodeNumOfDevices (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_node_num_of_devices_args *args, remote_node_num_of_devices_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 -r acc38454f32d qemud/remote_protocol.c --- a/qemud/remote_protocol.c Thu Nov 20 16:16:37 2008 +0000 +++ b/qemud/remote_protocol.c Thu Nov 20 16:26:13 2008 +0000 @@ -84,6 +84,15 @@ } bool_t +xdr_remote_nonnull_node_device (XDR *xdrs, remote_nonnull_node_device *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_domain (XDR *xdrs, remote_domain *objp) { @@ -115,6 +124,15 @@ { if (!xdr_pointer (xdrs, (char **)objp, sizeof (remote_nonnull_storage_vol), (xdrproc_t) xdr_remote_nonnull_storage_vol)) + return FALSE; + return TRUE; +} + +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; } @@ -2008,6 +2026,146 @@ } bool_t +xdr_remote_node_num_of_devices_args (XDR *xdrs, remote_node_num_of_devices_args *objp) +{ + + if (!xdr_remote_string (xdrs, &objp->cap)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +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_remote_string (xdrs, &objp->cap)) + return FALSE; + if (!xdr_int (xdrs, &objp->maxnames)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + 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_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->name)) + 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_get_parent_args (XDR *xdrs, remote_node_device_get_parent_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_node_device_get_parent_ret (XDR *xdrs, remote_node_device_get_parent_ret *objp) +{ + + if (!xdr_remote_string (xdrs, &objp->parent)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_node_device_num_of_caps_args (XDR *xdrs, remote_node_device_num_of_caps_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_node_device_num_of_caps_ret (XDR *xdrs, remote_node_device_num_of_caps_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->num)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_node_device_list_caps_args (XDR *xdrs, remote_node_device_list_caps_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + if (!xdr_int (xdrs, &objp->maxnames)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_node_device_list_caps_ret (XDR *xdrs, remote_node_device_list_caps_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_CAPS_LIST_MAX, + sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_domain_events_register_ret (XDR *xdrs, remote_domain_events_register_ret *objp) { diff -r acc38454f32d qemud/remote_protocol.h --- a/qemud/remote_protocol.h Thu Nov 20 16:16:37 2008 +0000 +++ b/qemud/remote_protocol.h Thu Nov 20 16:26:13 2008 +0000 @@ -31,6 +31,8 @@ #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,11 @@ }; typedef struct remote_nonnull_storage_vol remote_nonnull_storage_vol; +struct remote_nonnull_node_device { + remote_nonnull_string name; +}; +typedef struct remote_nonnull_node_device remote_nonnull_node_device; + typedef remote_nonnull_domain *remote_domain; typedef remote_nonnull_network *remote_network; @@ -73,6 +80,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; @@ -1122,6 +1131,87 @@ }; typedef struct remote_storage_vol_get_path_ret remote_storage_vol_get_path_ret; +struct remote_node_num_of_devices_args { + remote_string cap; + u_int flags; +}; +typedef struct remote_node_num_of_devices_args remote_node_num_of_devices_args; + +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 { + remote_string cap; + int maxnames; + u_int flags; +}; +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_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 name; + 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_get_parent_args { + remote_nonnull_string name; +}; +typedef struct remote_node_device_get_parent_args remote_node_device_get_parent_args; + +struct remote_node_device_get_parent_ret { + remote_string parent; +}; +typedef struct remote_node_device_get_parent_ret remote_node_device_get_parent_ret; + +struct remote_node_device_num_of_caps_args { + remote_nonnull_string name; +}; +typedef struct remote_node_device_num_of_caps_args remote_node_device_num_of_caps_args; + +struct remote_node_device_num_of_caps_ret { + int num; +}; +typedef struct remote_node_device_num_of_caps_ret remote_node_device_num_of_caps_ret; + +struct remote_node_device_list_caps_args { + remote_nonnull_string name; + int maxnames; +}; +typedef struct remote_node_device_list_caps_args remote_node_device_list_caps_args; + +struct remote_node_device_list_caps_ret { + struct { + u_int names_len; + remote_nonnull_string *names_val; + } names; +}; +typedef struct remote_node_device_list_caps_ret remote_node_device_list_caps_ret; + struct remote_domain_events_register_ret { int cb_registered; }; @@ -1252,6 +1342,13 @@ REMOTE_PROC_DOMAIN_MIGRATE_PREPARE2 = 108, REMOTE_PROC_DOMAIN_MIGRATE_FINISH2 = 109, REMOTE_PROC_GET_URI = 110, + REMOTE_PROC_NODE_NUM_OF_DEVICES = 111, + REMOTE_PROC_NODE_LIST_DEVICES = 112, + REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_NAME = 113, + REMOTE_PROC_NODE_DEVICE_DUMP_XML = 114, + REMOTE_PROC_NODE_DEVICE_GET_PARENT = 115, + REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS = 116, + REMOTE_PROC_NODE_DEVICE_LIST_CAPS = 117, }; typedef enum remote_procedure remote_procedure; @@ -1289,10 +1386,12 @@ 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*); @@ -1462,6 +1561,20 @@ 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_args (XDR *, remote_node_num_of_devices_args*); +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_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_get_parent_args (XDR *, remote_node_device_get_parent_args*); +extern bool_t xdr_remote_node_device_get_parent_ret (XDR *, remote_node_device_get_parent_ret*); +extern bool_t xdr_remote_node_device_num_of_caps_args (XDR *, remote_node_device_num_of_caps_args*); +extern bool_t xdr_remote_node_device_num_of_caps_ret (XDR *, remote_node_device_num_of_caps_ret*); +extern bool_t xdr_remote_node_device_list_caps_args (XDR *, remote_node_device_list_caps_args*); +extern bool_t xdr_remote_node_device_list_caps_ret (XDR *, remote_node_device_list_caps_ret*); extern bool_t xdr_remote_domain_events_register_ret (XDR *, remote_domain_events_register_ret*); extern bool_t xdr_remote_domain_events_deregister_ret (XDR *, remote_domain_events_deregister_ret*); extern bool_t xdr_remote_domain_event_ret (XDR *, remote_domain_event_ret*); @@ -1478,10 +1591,12 @@ 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 (); @@ -1651,6 +1766,20 @@ 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_args (); +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_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_get_parent_args (); +extern bool_t xdr_remote_node_device_get_parent_ret (); +extern bool_t xdr_remote_node_device_num_of_caps_args (); +extern bool_t xdr_remote_node_device_num_of_caps_ret (); +extern bool_t xdr_remote_node_device_list_caps_args (); +extern bool_t xdr_remote_node_device_list_caps_ret (); extern bool_t xdr_remote_domain_events_register_ret (); extern bool_t xdr_remote_domain_events_deregister_ret (); extern bool_t xdr_remote_domain_event_ret (); diff -r acc38454f32d qemud/remote_protocol.x --- a/qemud/remote_protocol.x Thu Nov 20 16:16:37 2008 +0000 +++ b/qemud/remote_protocol.x Thu Nov 20 16:26:13 2008 +0000 @@ -86,6 +86,12 @@ /* 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,17 @@ remote_nonnull_string key; }; +/* A node device which may not be NULL. */ +struct remote_nonnull_node_device { + remote_nonnull_string name; +}; + /* 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. */ @@ -994,6 +1006,70 @@ remote_nonnull_string name; }; +/* Node driver calls: */ + +struct remote_node_num_of_devices_args { + remote_string cap; + unsigned flags; +}; + +struct remote_node_num_of_devices_ret { + int num; +}; + +struct remote_node_list_devices_args { + remote_string cap; + int maxnames; + unsigned flags; +}; + +struct remote_node_list_devices_ret { + remote_nonnull_string names<REMOTE_NODE_DEVICE_NAME_LIST_MAX>; +}; + +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 name; + unsigned flags; +}; + +struct remote_node_device_dump_xml_ret { + remote_nonnull_string xml; +}; + +struct remote_node_device_get_parent_args { + remote_nonnull_string name; +}; + +struct remote_node_device_get_parent_ret { + remote_string parent; +}; + +struct remote_node_device_num_of_caps_args { + remote_nonnull_string name; +}; + +struct remote_node_device_num_of_caps_ret { + int num; +}; + +struct remote_node_device_list_caps_args { + remote_nonnull_string name; + int maxnames; +}; + +struct remote_node_device_list_caps_ret { + remote_nonnull_string names<REMOTE_NODE_DEVICE_CAPS_LIST_MAX>; +}; + + /** * Events Register/Deregister: * It would seem rpcgen does not like both args, and ret @@ -1140,7 +1216,15 @@ REMOTE_PROC_DOMAIN_EVENT = 107, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE2 = 108, REMOTE_PROC_DOMAIN_MIGRATE_FINISH2 = 109, - REMOTE_PROC_GET_URI = 110 + REMOTE_PROC_GET_URI = 110, + + REMOTE_PROC_NODE_NUM_OF_DEVICES = 111, + REMOTE_PROC_NODE_LIST_DEVICES = 112, + REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_NAME = 113, + REMOTE_PROC_NODE_DEVICE_DUMP_XML = 114, + REMOTE_PROC_NODE_DEVICE_GET_PARENT = 115, + REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS = 116, + REMOTE_PROC_NODE_DEVICE_LIST_CAPS = 117 }; /* Custom RPC structure. */ diff -r acc38454f32d src/remote_internal.c --- a/src/remote_internal.c Thu Nov 20 16:16:37 2008 +0000 +++ b/src/remote_internal.c Thu Nov 20 16:26:13 2008 +0000 @@ -143,6 +143,14 @@ return (retcode); \ } +#define GET_DEVMON_PRIVATE(conn,retcode) \ + struct private_data *priv = (struct private_data *) (conn)->devMonPrivateData; \ + 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, @@ -172,6 +180,7 @@ 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); @@ -3764,6 +3773,229 @@ /*----------------------------------------------------------------------*/ +static virDrvOpenStatus +remoteDevMonOpen(virConnectPtr conn, + 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->devMonPrivateData = conn->privateData; + return VIR_DRV_OPEN_SUCCESS; + } + + /* Decline open. Will fallback to appropriate local node driver. */ + return VIR_DRV_OPEN_DECLINED; +} + +static int remoteDevMonClose(virConnectPtr conn) +{ + int ret = 0; + GET_DEVMON_PRIVATE (conn, -1); + if (priv->localUses) { + priv->localUses--; + if (!priv->localUses) { + ret = doRemoteClose(conn, priv); + VIR_FREE(priv); + conn->devMonPrivateData = NULL; + } + } + return ret; +} + +static int remoteNodeNumOfDevices(virConnectPtr conn, + const char *cap, + unsigned int flags) +{ + remote_node_num_of_devices_args args; + remote_node_num_of_devices_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + + args.cap = cap ? (char **)&cap : NULL; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NODE_NUM_OF_DEVICES, + (xdrproc_t) xdr_remote_node_num_of_devices_args, (char *) &args, + (xdrproc_t) xdr_remote_node_num_of_devices_ret, (char *) &ret) == -1) + return -1; + + return ret.num; +} + + +static int remoteNodeListDevices(virConnectPtr conn, + const char *cap, + char **const names, + int maxnames, + unsigned int flags) +{ + 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.cap = cap ? (char **)&cap : NULL; + args.maxnames = maxnames; + args.flags = flags; + + 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 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.name = dev->name; + 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 char *remoteNodeDeviceGetParent(virNodeDevicePtr dev) +{ + remote_node_device_get_parent_args args; + remote_node_device_get_parent_ret ret; + GET_STORAGE_PRIVATE (dev->conn, NULL); + + args.name = dev->name; + + memset (&ret, 0, sizeof ret); + if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_GET_PARENT, + (xdrproc_t) xdr_remote_node_device_get_parent_args, (char *) &args, + (xdrproc_t) xdr_remote_node_device_get_parent_ret, (char *) &ret) == -1) + return NULL; + + /* Caller frees. */ + return ret.parent ? *ret.parent : NULL; +} + +static int remoteNodeDeviceNumOfCaps(virNodeDevicePtr dev) +{ + remote_node_device_num_of_caps_args args; + remote_node_device_num_of_caps_ret ret; + GET_STORAGE_PRIVATE (dev->conn, -1); + + args.name = dev->name; + + memset (&ret, 0, sizeof ret); + if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS, + (xdrproc_t) xdr_remote_node_device_num_of_caps_args, (char *) &args, + (xdrproc_t) xdr_remote_node_device_num_of_caps_ret, (char *) &ret) == -1) + return -1; + + return ret.num; +} + +static int remoteNodeDeviceListCaps(virNodeDevicePtr dev, + char **const names, + int maxnames) +{ + int i; + remote_node_device_list_caps_args args; + remote_node_device_list_caps_ret ret; + GET_STORAGE_PRIVATE (dev->conn, -1); + + if (maxnames > REMOTE_NODE_DEVICE_CAPS_LIST_MAX) { + error (dev->conn, VIR_ERR_RPC, _("too many capability names requested")); + return -1; + } + args.maxnames = maxnames; + args.name = dev->name; + + memset (&ret, 0, sizeof ret); + if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_LIST_CAPS, + (xdrproc_t) xdr_remote_node_device_list_caps_args, (char *) &args, + (xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret) == -1) + return -1; + + if (ret.names.names_len > maxnames) { + error (dev->conn, VIR_ERR_RPC, _("too many capability names received")); + xdr_free ((xdrproc_t) xdr_remote_node_device_list_caps_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_device_list_caps_ret, (char *) &ret); + + return ret.names.names_len; +} + + +/*----------------------------------------------------------------------*/ + static int remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open, virConnectAuthPtr auth @@ -5018,6 +5250,12 @@ 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) +{ + return virGetNodeDevice(conn, dev.name); } /* Make remote_nonnull_domain and remote_nonnull_network. */ @@ -5175,6 +5413,20 @@ .volGetPath = remoteStorageVolGetPath, }; +static virDeviceMonitor dev_monitor = { + .name = "remote", + .open = remoteDevMonOpen, + .close = remoteDevMonClose, + .numOfDevices = remoteNodeNumOfDevices, + .listDevices = remoteNodeListDevices, + .deviceLookupByName = remoteNodeDeviceLookupByName, + .deviceDumpXML = remoteNodeDeviceDumpXML, + .deviceGetParent = remoteNodeDeviceGetParent, + .deviceNumOfCaps = remoteNodeDeviceNumOfCaps, + .deviceListCaps = remoteNodeDeviceListCaps, +}; + + #ifdef WITH_LIBVIRTD static virStateDriver state_driver = { .initialize = remoteStartup, @@ -5194,6 +5446,7 @@ if (virRegisterDriver (&driver) == -1) return -1; if (virRegisterNetworkDriver (&network_driver) == -1) return -1; if (virRegisterStorageDriver (&storage_driver) == -1) return -1; + if (virRegisterDeviceMonitor (&dev_monitor) == -1) return -1; #ifdef WITH_LIBVIRTD if (virRegisterStateDriver (&state_driver) == -1) return -1; #endif -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Nov 20, 2008 at 05:56:34PM +0000, Daniel P. Berrange wrote:
This provides the remote driver implementation for node device APIs
This is basically just fixing up to add the optional 'cap' arg to the List methods, and remove the Create/Destroy/ByCap methods
okay, +1 Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This provides the virsh binding. The main changes is to the list method to not call ByCaps anymore. And I've actually really renamed the commands this time ! Daniel diff -r 9a1d48518cac src/virsh.c --- a/src/virsh.c Thu Nov 20 16:26:13 2008 +0000 +++ b/src/virsh.c Thu Nov 20 16:43:40 2008 +0000 @@ -4415,6 +4415,94 @@ vshPrint(ctl, _("Running hypervisor: %s %d.%d.%d\n"), hvType, major, minor, rel); } + return TRUE; +} + +/* + * "nodedev-list" command + */ +static const vshCmdInfo info_node_list_devices[] = { + {"syntax", "nodedev-list [--cap <capability>]"}, + {"help", gettext_noop("enumerate devices on this host")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_node_list_devices[] = { + {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, gettext_noop("capability name")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNodeListDevices (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + char *cap; + char **devices; + int found, num_devices, i; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + cap = vshCommandOptString(cmd, "cap", &found); + if (!found) + cap = NULL; + + num_devices = virNodeNumOfDevices(ctl->conn, cap, 0); + 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, cap, devices, num_devices, 0); + 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; +} + +/* + * "nodedev-dumpxml" command + */ +static const vshCmdInfo info_node_device_dumpxml[] = { + {"syntax", "nodedev-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 *name; + virNodeDevicePtr device; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + if (!(name = vshCommandOptString(cmd, "device", NULL))) + return FALSE; + if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) { + vshError(ctl, FALSE, "%s '%s'", _("Could not find matching device"), name); + return FALSE; + } + + vshPrint(ctl, "%s\n", virNodeDeviceGetXMLDesc(device, 0)); + virNodeDeviceFree(device); return TRUE; } @@ -5570,6 +5658,9 @@ {"net-uuid", cmdNetworkUuid, opts_network_uuid, info_network_uuid}, {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo}, + {"nodedev-list", cmdNodeListDevices, opts_node_list_devices, info_node_list_devices}, + {"nodedev-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}, -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Nov 20, 2008 at 05:57:37PM +0000, Daniel P. Berrange wrote:
This provides the virsh binding. The main changes is to the list method to not call ByCaps anymore. And I've actually really renamed the commands this time !
okay :-) +1 Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This is the python API, again just changed to cope with removal of the ByCaps API calls Daniel diff -r 105e73557ef8 python/generator.py --- a/python/generator.py Thu Nov 20 16:27:06 2008 +0000 +++ b/python/generator.py Thu Nov 20 16:43:08 2008 +0000 @@ -260,6 +260,11 @@ '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 = { @@ -318,6 +323,8 @@ 'virDomainBlockPeek', 'virDomainMemoryPeek', 'virEventRegisterImpl', + 'virNodeListDevices', + 'virNodeDeviceListCaps', ) @@ -601,6 +608,8 @@ "virStoragePool *": ("._o", "virStoragePool(self, _obj=%s)", "virStoragePool"), "virStorageVolPtr": ("._o", "virStorageVol(self, _obj=%s)", "virStorageVol"), "virStorageVol *": ("._o", "virStorageVol(self, _obj=%s)", "virStorageVol"), + "virNodeDevicePtr": ("._o", "virNodeDevice(self, _obj=%s)", "virNodeDevice"), + "virNodeDevice *": ("._o", "virNodeDevice(self, _obj=%s)", "virNodeDevice"), "virConnectPtr": ("._o", "virConnect(_obj=%s)", "virConnect"), "virConnect *": ("._o", "virConnect(_obj=%s)", "virConnect"), } @@ -608,7 +617,8 @@ converter_type = { } -primary_classes = ["virDomain", "virNetwork", "virStoragePool", "virStorageVol", "virConnect"] +primary_classes = ["virDomain", "virNetwork", "virStoragePool", "virStorageVol", + "virConnect", "virNodeDevice" ] classes_ancestor = { } @@ -617,6 +627,7 @@ "virNetwork": "virNetworkFree", "virStoragePool": "virStoragePoolFree", "virStorageVol": "virStorageVolFree", + "virNodeDevice" : "virNodeDeviceFree" } functions_noexcept = { @@ -626,6 +637,8 @@ 'virStoragePoolGetName': True, 'virStorageVolGetName': True, 'virStorageVolGetkey': True, + 'virNodeDeviceGetName': True, + 'virNodeDeviceGetParent': True, } reference_keepers = { @@ -706,6 +719,13 @@ elif name[0:13] == "virStorageVol": func = name[13:] func = string.lower(func[0:1]) + func[1:] + elif name[0:13] == "virNodeDevice": + if name[13:16] == "Get": + func = string.lower(name[16]) + name[17:] + elif name[13:19] == "Lookup" or name[13:] == "Create": + func = string.lower(name[3]) + name[4:] + else: + func = string.lower(name[13]) + name[14:] elif name[0:7] == "virNode": func = name[7:] func = string.lower(func[0:1]) + func[1:] @@ -958,7 +978,7 @@ else: txt.write("Class %s()\n" % (classname)) classes.write("class %s:\n" % (classname)) - if classname in [ "virDomain", "virNetwork", "virStoragePool", "virStorageVol" ]: + if classname in [ "virDomain", "virNetwork", "virStoragePool", "virStorageVol", "virNodeDevice" ]: classes.write(" def __init__(self, conn, _obj=None):\n") else: classes.write(" def __init__(self, _obj=None):\n") @@ -966,7 +986,7 @@ list = reference_keepers[classname] for ref in list: classes.write(" self.%s = None\n" % ref[1]) - if classname in [ "virDomain", "virNetwork" ]: + if classname in [ "virDomain", "virNetwork", "virNodeDevice" ]: classes.write(" self._conn = conn\n") elif classname in [ "virStorageVol", "virStoragePool" ]: classes.write(" self._conn = conn\n" + \ diff -r 105e73557ef8 python/libvir.c --- a/python/libvir.c Thu Nov 20 16:27:06 2008 +0000 +++ b/python/libvir.c Thu Nov 20 16:43:08 2008 +0000 @@ -1466,6 +1466,90 @@ 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; + char *cap; + unsigned int flags; + + if (!PyArg_ParseTuple(args, (char *)"Ozi:virNodeListDevices", + &pyobj_conn, &cap, &flags)) + return(NULL); + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + c_retval = virNodeNumOfDevices(conn, cap, flags); + 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, cap, names, c_retval, flags); + 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_virNodeDeviceListCaps(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + PyObject *py_retval; + char **names = NULL; + int c_retval, i; + virNodeDevicePtr dev; + PyObject *pyobj_dev; + + if (!PyArg_ParseTuple(args, (char *)"O:virNodeDeviceListCaps", &pyobj_dev)) + return(NULL); + dev = (virNodeDevicePtr) PyvirNodeDevice_Get(pyobj_dev); + + c_retval = virNodeDeviceNumOfCaps(dev); + 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 = virNodeDeviceListCaps(dev, 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); +} + + /******************************************* * Helper functions to avoid importing modules * for every callback @@ -2044,6 +2128,8 @@ {(char *) "virEventRegisterImpl", libvirt_virEventRegisterImpl, METH_VARARGS, NULL}, {(char *) "virEventInvokeHandleCallback", libvirt_virEventInvokeHandleCallback, METH_VARARGS, NULL}, {(char *) "virEventInvokeTimeoutCallback", libvirt_virEventInvokeTimeoutCallback, METH_VARARGS, NULL}, + {(char *) "virNodeListDevices", libvirt_virNodeListDevices, METH_VARARGS, NULL}, + {(char *) "virNodeDeviceListCaps", libvirt_virNodeDeviceListCaps, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; diff -r 105e73557ef8 python/libvirt-python-api.xml --- a/python/libvirt-python-api.xml Thu Nov 20 16:27:06 2008 +0000 +++ b/python/libvirt-python-api.xml Thu Nov 20 16:43:08 2008 +0000 @@ -160,5 +160,17 @@ <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</info> + <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> + <arg name='cap' type='const unsigned char *' info='capability name'/> + <arg name='flags' type='unsigned int' info='flags (unused; pass 0)'/> + <return type='str *' info='the list of Names or None in case of error'/> + </function> + <function name='virNodeDeviceListCaps' file='python'> + <info>list the node device's capabilities</info> + <arg name='dev' type='virNodeDevicePtr' info='pointer to the node device'/> + <return type='str *' info='the list of Names or None in case of error'/> + </function> </symbols> </api> diff -r 105e73557ef8 python/libvirt_wrap.h --- a/python/libvirt_wrap.h Thu Nov 20 16:27:06 2008 +0000 +++ b/python/libvirt_wrap.h Thu Nov 20 16:43:08 2008 +0000 @@ -65,6 +65,16 @@ virStorageVolPtr obj; } PyvirStorageVol_Object; + +#define PyvirNodeDevice_Get(v) (((v) == Py_None) ? NULL : \ + (((PyvirNodeDevice_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + virNodeDevicePtr obj; +} PyvirNodeDevice_Object; + + #define PyvirEventHandleCallback_Get(v) (((v) == Py_None) ? NULL : \ (((PyvirEventHandleCallback_Object *)(v))->obj)) @@ -89,6 +99,7 @@ void* obj; } PyvirVoidPtr_Object; + PyObject * libvirt_intWrap(int val); PyObject * libvirt_longWrap(long val); PyObject * libvirt_ulongWrap(unsigned long val); @@ -105,6 +116,8 @@ PyObject * libvirt_virEventTimeoutCallbackWrap(virEventTimeoutCallback node); PyObject * libvirt_virFreeCallbackWrap(virFreeCallback node); PyObject * libvirt_virVoidPtrWrap(void* node); +PyObject * libvirt_virNodeDevicePtrWrap(virNodeDevicePtr node); + /* Provide simple macro statement wrappers (adapted from GLib, in turn from Perl): * LIBVIRT_STMT_START { statements; } LIBVIRT_STMT_END; diff -r 105e73557ef8 python/types.c --- a/python/types.c Thu Nov 20 16:27:06 2008 +0000 +++ b/python/types.c Thu Nov 20 16:43:08 2008 +0000 @@ -164,6 +164,21 @@ } 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); +} + +PyObject * libvirt_virEventHandleCallbackWrap(virEventHandleCallback node) { PyObject *ret; -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Nov 20, 2008 at 05:58:16PM +0000, Daniel P. Berrange wrote:
This is the python API, again just changed to cope with removal of the ByCaps API calls
looks fine, [...]
diff -r 105e73557ef8 python/libvir.c --- a/python/libvir.c Thu Nov 20 16:27:06 2008 +0000 +++ b/python/libvir.c Thu Nov 20 16:43:08 2008 +0000 [...] +static PyObject * +libvirt_virNodeDeviceListCaps(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + PyObject *py_retval; + char **names = NULL; + int c_retval, i; + virNodeDevicePtr dev; + PyObject *pyobj_dev; + + if (!PyArg_ParseTuple(args, (char *)"O:virNodeDeviceListCaps", &pyobj_dev)) + return(NULL); + dev = (virNodeDevicePtr) PyvirNodeDevice_Get(pyobj_dev); + + c_retval = virNodeDeviceNumOfCaps(dev); + 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 = virNodeDeviceListCaps(dev, 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); +}
But I don't think we need that one anymore, right ? +1 Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This patch contains a couple of misc fixes. HAL has separate 'block' and 'storage' and 'volume' capabilities. Actual devices get the 'block' and 'storage' capability, while partitions get the 'block' and 'volume' capability. We have no need to expose partitions in this API, since we already have a explicit storage pool impl for this. In addition, DeviceKit will not expose partition information. There are also a number of misc devices HAL shows that don't have any capabilities in libvirt XML yet. Wierd embeedded components from the chipset, and and sound devices. DeviceKit doesn't (currently) expose any of these, and since we have no capability info for them it is fairly pointless exposing them from the HAL impl in libvirt either. Thus, this patch does a couple of things - Filters out any devices from HAL for which we have no capabilities - Removes the 'block' capability, and list the block device name in the 'storage' capability XML - In virsh nodedev-list, alphabetically sort device names If we find we want some of these devices we're filtering, we can easily re-add them in future - I prefer to expose too little info for now, than too much. Daniel diff -r f99baad5c0da src/node_device_conf.c --- a/src/node_device_conf.c Thu Nov 20 17:48:12 2008 +0000 +++ b/src/node_device_conf.c Thu Nov 20 17:48:16 2008 +0000 @@ -43,7 +43,6 @@ "usb_device", "usb", "net", - "block", "scsi_host", "scsi", "storage"); @@ -280,10 +279,6 @@ virBufferVSprintf(&buf, " <capability type='%s'/>\n", subtyp); } break; - case VIR_NODE_DEV_CAP_BLOCK: - virBufferVSprintf(&buf, " <device>%s</device>\n", - data->block.device); - break; case VIR_NODE_DEV_CAP_SCSI_HOST: virBufferVSprintf(&buf, " <host>%d</host>\n", data->scsi_host.host); @@ -299,6 +294,8 @@ data->scsi.type); break; case VIR_NODE_DEV_CAP_STORAGE: + virBufferVSprintf(&buf, " <block>%s</blocke>\n", + data->storage.block); if (data->storage.bus) virBufferVSprintf(&buf, " <bus>%s</bus>\n", data->storage.bus); @@ -380,15 +377,13 @@ VIR_FREE(data->net.interface); VIR_FREE(data->net.address); break; - case VIR_NODE_DEV_CAP_BLOCK: - VIR_FREE(data->block.device); - break; case VIR_NODE_DEV_CAP_SCSI_HOST: break; case VIR_NODE_DEV_CAP_SCSI: VIR_FREE(data->scsi.type); break; case VIR_NODE_DEV_CAP_STORAGE: + VIR_FREE(data->storage.block); VIR_FREE(data->storage.bus); VIR_FREE(data->storage.drive_type); VIR_FREE(data->storage.model); diff -r f99baad5c0da src/node_device_conf.h --- a/src/node_device_conf.h Thu Nov 20 17:48:12 2008 +0000 +++ b/src/node_device_conf.h Thu Nov 20 17:48:16 2008 +0000 @@ -34,7 +34,6 @@ VIR_NODE_DEV_CAP_USB_DEV, /* USB device */ VIR_NODE_DEV_CAP_USB_INTERFACE, /* USB interface */ VIR_NODE_DEV_CAP_NET, /* Network device */ - VIR_NODE_DEV_CAP_BLOCK, /* Block device */ VIR_NODE_DEV_CAP_SCSI_HOST, /* SCSI Host Bus Adapter */ VIR_NODE_DEV_CAP_SCSI, /* SCSI device */ VIR_NODE_DEV_CAP_STORAGE, /* Storage device */ @@ -107,9 +106,6 @@ enum virNodeDevNetCapType subtype; /* LAST -> no subtype */ } net; struct { - char *device; - } block; - struct { unsigned host; } scsi_host; struct { @@ -122,6 +118,7 @@ struct { unsigned long long size; unsigned long long removable_media_size; + char *block; char *bus; char *drive_type; char *model; diff -r f99baad5c0da src/node_device_devkit.c --- a/src/node_device_devkit.c Thu Nov 20 17:48:12 2008 +0000 +++ b/src/node_device_devkit.c Thu Nov 20 17:48:16 2008 +0000 @@ -31,6 +31,7 @@ #include "event.h" #include "memory.h" #include "uuid.h" +#include "logging.h" #include "node_device.h" @@ -133,12 +134,12 @@ } -static int gather_block_cap(DevkitDevice *dkdev, - union _virNodeDevCapData *d) +static int gather_storage_cap(DevkitDevice *dkdev, + union _virNodeDevCapData *d) { const char *device = devkit_device_get_device_file(dkdev); - if (device && ((d->block.device = strdup(device)) == NULL)) + if (device && ((d->storage.block = strdup(device)) == NULL)) return -1; return 0; @@ -158,7 +159,7 @@ { "pci", VIR_NODE_DEV_CAP_PCI_DEV, gather_pci_cap }, { "usb", VIR_NODE_DEV_CAP_USB_DEV, gather_usb_cap }, { "net", VIR_NODE_DEV_CAP_NET, gather_net_cap }, - { "block", VIR_NODE_DEV_CAP_BLOCK, gather_block_cap }, + { "block", VIR_NODE_DEV_CAP_STORAGE, gather_storage_cap }, // TODO: more caps! }; diff -r f99baad5c0da src/node_device_hal.c --- a/src/node_device_hal.c Thu Nov 20 17:48:12 2008 +0000 +++ b/src/node_device_hal.c Thu Nov 20 17:48:16 2008 +0000 @@ -210,14 +210,6 @@ } -static int gather_block_cap(LibHalContext *ctx, const char *udi, - union _virNodeDevCapData *d) -{ - (void)get_str_prop(ctx, udi, "block.device", &d->block.device); - return 0; -} - - static int gather_scsi_host_cap(LibHalContext *ctx, const char *udi, union _virNodeDevCapData *d) { @@ -242,6 +234,7 @@ union _virNodeDevCapData *d) { int val; + (void)get_str_prop(ctx, udi, "block.device", &d->storage.block); (void)get_str_prop(ctx, udi, "storage.bus", &d->storage.bus); (void)get_str_prop(ctx, udi, "storage.drive_type", &d->storage.drive_type); (void)get_str_prop(ctx, udi, "storage.model", &d->storage.model); @@ -306,7 +299,6 @@ { "usb", VIR_NODE_DEV_CAP_USB_INTERFACE, gather_usb_cap }, { "usb_device", VIR_NODE_DEV_CAP_USB_DEV, gather_usb_device_cap }, { "net", VIR_NODE_DEV_CAP_NET, gather_net_cap }, - { "block", VIR_NODE_DEV_CAP_BLOCK, gather_block_cap }, { "scsi_host", VIR_NODE_DEV_CAP_SCSI_HOST, gather_scsi_host_cap }, { "scsi", VIR_NODE_DEV_CAP_SCSI, gather_scsi_cap }, { "storage", VIR_NODE_DEV_CAP_STORAGE, gather_storage_cap }, @@ -436,6 +428,13 @@ rv = gather_capabilities(ctx, udi, &dev->def->caps); if (rv != 0) goto failure; + + if (dev->def->caps == NULL) { + virNodeDeviceDefFree(dev->def); + VIR_FREE(dev); + VIR_FREE(udi); + return; + } if (VIR_REALLOC_N(driverState->devs.objs, driverState->devs.count + 1) < 0) goto failure; diff -r f99baad5c0da src/virsh.c --- a/src/virsh.c Thu Nov 20 17:48:12 2008 +0000 +++ b/src/virsh.c Thu Nov 20 17:48:16 2008 +0000 @@ -4462,6 +4462,7 @@ free(devices); return FALSE; } + qsort(&devices[0], num_devices, sizeof(char*), namesorter); for (i = 0; i < num_devices; i++) { vshPrint(ctl, "%s\n", devices[i]); free(devices[i]); -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Nov 20, 2008 at 06:14:53PM +0000, Mark McLoughlin wrote:
On Thu, 2008-11-20 at 18:03 +0000, Daniel P. Berrange wrote:
case VIR_NODE_DEV_CAP_STORAGE: + virBufferVSprintf(&buf, " <block>%s</blocke>\n",
^
Minore tpyo hero
Opps ! I also forgot one other thing we talked about - changing the PCI and USB vendor+product numbers into Hex. I've made that change here, and prefixed with 0x so its obvious they are Hex. Daniel diff -r 449c387f2de9 src/node_device_conf.c --- a/src/node_device_conf.c Thu Nov 20 18:08:58 2008 +0000 +++ b/src/node_device_conf.c Thu Nov 20 18:25:36 2008 +0000 @@ -43,7 +43,6 @@ "usb_device", "usb", "net", - "block", "scsi_host", "scsi", "storage"); @@ -221,14 +220,14 @@ data->pci_dev.slot); virBufferVSprintf(&buf, " <function>%d</function>\n", data->pci_dev.function); - virBufferVSprintf(&buf, " <product id='%d'", + virBufferVSprintf(&buf, " <product id='0x%04x'", data->pci_dev.product); if (data->pci_dev.product_name) virBufferEscapeString(&buf, ">%s</product>\n", data->pci_dev.product_name); else virBufferAddLit(&buf, " />\n"); - virBufferVSprintf(&buf, " <vendor id='%d'", + virBufferVSprintf(&buf, " <vendor id='0x%04x'", data->pci_dev.vendor); if (data->pci_dev.vendor_name) virBufferEscapeString(&buf, ">%s</vendor>\n", @@ -240,14 +239,14 @@ virBufferVSprintf(&buf, " <bus>%d</bus>\n", data->usb_dev.bus); virBufferVSprintf(&buf, " <device>%d</device>\n", data->usb_dev.device); - virBufferVSprintf(&buf, " <product id='%d'", + virBufferVSprintf(&buf, " <product id='0x%04x'", data->usb_dev.product); if (data->usb_dev.product_name) virBufferEscapeString(&buf, ">%s</product>\n", data->usb_dev.product_name); else virBufferAddLit(&buf, " />\n"); - virBufferVSprintf(&buf, " <vendor id='%d'", + virBufferVSprintf(&buf, " <vendor id='0x%04x'", data->usb_dev.vendor); if (data->usb_dev.vendor_name) virBufferEscapeString(&buf, ">%s</vendor>\n", @@ -280,10 +279,6 @@ virBufferVSprintf(&buf, " <capability type='%s'/>\n", subtyp); } break; - case VIR_NODE_DEV_CAP_BLOCK: - virBufferVSprintf(&buf, " <device>%s</device>\n", - data->block.device); - break; case VIR_NODE_DEV_CAP_SCSI_HOST: virBufferVSprintf(&buf, " <host>%d</host>\n", data->scsi_host.host); @@ -299,6 +294,8 @@ data->scsi.type); break; case VIR_NODE_DEV_CAP_STORAGE: + virBufferVSprintf(&buf, " <block>%s</block>\n", + data->storage.block); if (data->storage.bus) virBufferVSprintf(&buf, " <bus>%s</bus>\n", data->storage.bus); @@ -380,15 +377,13 @@ VIR_FREE(data->net.interface); VIR_FREE(data->net.address); break; - case VIR_NODE_DEV_CAP_BLOCK: - VIR_FREE(data->block.device); - break; case VIR_NODE_DEV_CAP_SCSI_HOST: break; case VIR_NODE_DEV_CAP_SCSI: VIR_FREE(data->scsi.type); break; case VIR_NODE_DEV_CAP_STORAGE: + VIR_FREE(data->storage.block); VIR_FREE(data->storage.bus); VIR_FREE(data->storage.drive_type); VIR_FREE(data->storage.model); diff -r 449c387f2de9 src/node_device_conf.h --- a/src/node_device_conf.h Thu Nov 20 18:08:58 2008 +0000 +++ b/src/node_device_conf.h Thu Nov 20 18:25:36 2008 +0000 @@ -34,7 +34,6 @@ VIR_NODE_DEV_CAP_USB_DEV, /* USB device */ VIR_NODE_DEV_CAP_USB_INTERFACE, /* USB interface */ VIR_NODE_DEV_CAP_NET, /* Network device */ - VIR_NODE_DEV_CAP_BLOCK, /* Block device */ VIR_NODE_DEV_CAP_SCSI_HOST, /* SCSI Host Bus Adapter */ VIR_NODE_DEV_CAP_SCSI, /* SCSI device */ VIR_NODE_DEV_CAP_STORAGE, /* Storage device */ @@ -107,9 +106,6 @@ enum virNodeDevNetCapType subtype; /* LAST -> no subtype */ } net; struct { - char *device; - } block; - struct { unsigned host; } scsi_host; struct { @@ -122,6 +118,7 @@ struct { unsigned long long size; unsigned long long removable_media_size; + char *block; char *bus; char *drive_type; char *model; diff -r 449c387f2de9 src/node_device_devkit.c --- a/src/node_device_devkit.c Thu Nov 20 18:08:58 2008 +0000 +++ b/src/node_device_devkit.c Thu Nov 20 18:25:36 2008 +0000 @@ -31,6 +31,7 @@ #include "event.h" #include "memory.h" #include "uuid.h" +#include "logging.h" #include "node_device.h" @@ -133,12 +134,12 @@ } -static int gather_block_cap(DevkitDevice *dkdev, - union _virNodeDevCapData *d) +static int gather_storage_cap(DevkitDevice *dkdev, + union _virNodeDevCapData *d) { const char *device = devkit_device_get_device_file(dkdev); - if (device && ((d->block.device = strdup(device)) == NULL)) + if (device && ((d->storage.block = strdup(device)) == NULL)) return -1; return 0; @@ -158,7 +159,7 @@ { "pci", VIR_NODE_DEV_CAP_PCI_DEV, gather_pci_cap }, { "usb", VIR_NODE_DEV_CAP_USB_DEV, gather_usb_cap }, { "net", VIR_NODE_DEV_CAP_NET, gather_net_cap }, - { "block", VIR_NODE_DEV_CAP_BLOCK, gather_block_cap }, + { "block", VIR_NODE_DEV_CAP_STORAGE, gather_storage_cap }, // TODO: more caps! }; diff -r 449c387f2de9 src/node_device_hal.c --- a/src/node_device_hal.c Thu Nov 20 18:08:58 2008 +0000 +++ b/src/node_device_hal.c Thu Nov 20 18:25:36 2008 +0000 @@ -211,14 +211,6 @@ } -static int gather_block_cap(LibHalContext *ctx, const char *udi, - union _virNodeDevCapData *d) -{ - (void)get_str_prop(ctx, udi, "block.device", &d->block.device); - return 0; -} - - static int gather_scsi_host_cap(LibHalContext *ctx, const char *udi, union _virNodeDevCapData *d) { @@ -243,6 +235,7 @@ union _virNodeDevCapData *d) { int val; + (void)get_str_prop(ctx, udi, "block.device", &d->storage.block); (void)get_str_prop(ctx, udi, "storage.bus", &d->storage.bus); (void)get_str_prop(ctx, udi, "storage.drive_type", &d->storage.drive_type); (void)get_str_prop(ctx, udi, "storage.model", &d->storage.model); @@ -307,7 +300,6 @@ { "usb", VIR_NODE_DEV_CAP_USB_INTERFACE, gather_usb_cap }, { "usb_device", VIR_NODE_DEV_CAP_USB_DEV, gather_usb_device_cap }, { "net", VIR_NODE_DEV_CAP_NET, gather_net_cap }, - { "block", VIR_NODE_DEV_CAP_BLOCK, gather_block_cap }, { "scsi_host", VIR_NODE_DEV_CAP_SCSI_HOST, gather_scsi_host_cap }, { "scsi", VIR_NODE_DEV_CAP_SCSI, gather_scsi_cap }, { "storage", VIR_NODE_DEV_CAP_STORAGE, gather_storage_cap }, @@ -437,6 +429,13 @@ rv = gather_capabilities(ctx, udi, &dev->def->caps); if (rv != 0) goto failure; + + if (dev->def->caps == NULL) { + virNodeDeviceDefFree(dev->def); + VIR_FREE(dev); + VIR_FREE(udi); + return; + } if (VIR_REALLOC_N(driverState->devs.objs, driverState->devs.count + 1) < 0) goto failure; diff -r 449c387f2de9 src/virsh.c --- a/src/virsh.c Thu Nov 20 18:08:58 2008 +0000 +++ b/src/virsh.c Thu Nov 20 18:25:36 2008 +0000 @@ -4462,6 +4462,7 @@ free(devices); return FALSE; } + qsort(&devices[0], num_devices, sizeof(char*), namesorter); for (i = 0; i < num_devices; i++) { vshPrint(ctl, "%s\n", devices[i]); free(devices[i]); -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Nov 20, 2008 at 05:49:50PM +0000, Daniel P. Berrange wrote:
FYI, this is a repost of the final node device patches I intend to commit tomorrow. There's a few changes based on Mark's feedback which I'll note against each patch...
As an example, here is the complete output from a box of mine with all patches applied. I'm happy with committing to this XML long term, with the expectation that we may define a formal naming scheme for devices later to replace the non-guarenteed HAL/DevKit naming. # ./virsh nodedev-list computer net_00_e0_81_b0_2d_8e net_00_e0_81_b0_2d_8f pci_1002_94c3 pci_1002_aa10 pci_1022_1200 pci_1022_1200_0 pci_1022_1201 pci_1022_1201_0 pci_1022_1202 pci_1022_1202_0 pci_1022_1203 pci_1022_1203_0 pci_1022_1204 pci_1022_1204_0 pci_1033_125 pci_1033_125_0 pci_104c_8023 pci_10de_361 pci_10de_364 pci_10de_368 pci_10de_368_0 pci_10de_369 pci_10de_369_0 pci_10de_36c pci_10de_36d pci_10de_36e pci_10de_36e_scsi_host pci_10de_36e_scsi_host_scsi_device_lun0 pci_10de_370 pci_10de_371 pci_10de_372 pci_10de_372_0 pci_10de_376 pci_10de_376_0 pci_10de_377 pci_10de_377_0 pci_10de_378 pci_10de_378_0 pci_10de_37f pci_10de_37f_0 pci_10de_37f_1 pci_10de_37f_scsi_host pci_10de_37f_scsi_host_scsi_device_lun0 platform_floppy_0_storage_platform_floppy storage_model_DVD_A__DH20A4P storage_serial_SATA_WDC_WD3200AAKS__WD_WCASE0223035 usb_device_1d6b_1_0000_00_02_0 usb_device_1d6b_1_0000_00_02_0_if0 usb_device_1d6b_2_0000_00_02_1 usb_device_1d6b_2_0000_00_02_1_if0 And for each of those the XML is: <device> <name>computer</name> <capability type='system'> <hardware> <vendor>Tyan Computer Corporation</vendor> <version>REFERENCE</version> <serial>0123456789</serial> <uuid>00000000-0000-0000-0000-000000000000</uuid> </hardware> <firmware> <vendor>Phoenix Technologies Ltd.</vendor> <version>2.05.2915</version> <release_date>04/04/2008</release_date> </firmware> </capability> </device> <device> <name>net_00_e0_81_b0_2d_8e</name> <parent>pci_10de_372</parent> <capability type='net'> <interface>eth0</interface> <address>00:e0:81:b0:2d:8e</address> <capability type='80203'/> </capability> </device> <device> <name>net_00_e0_81_b0_2d_8f</name> <parent>pci_10de_372_0</parent> <capability type='net'> <interface>eth1</interface> <address>00:e0:81:b0:2d:8f</address> <capability type='80203'/> </capability> </device> <device> <name>pci_1002_94c3</name> <parent>pci_10de_377</parent> <capability type='pci'> <domain>0</domain> <bus>6</bus> <slot>0</slot> <function>0</function> <product id='38083'>RV610 video device [Radeon HD 2400 PRO]</product> <vendor id='4098'>ATI Technologies Inc</vendor> </capability> </device> <device> <name>pci_1002_aa10</name> <parent>pci_10de_377</parent> <capability type='pci'> <domain>0</domain> <bus>6</bus> <slot>0</slot> <function>1</function> <product id='43536'>RV610 audio device [Radeon HD 2400 PRO]</product> <vendor id='4098'>ATI Technologies Inc</vendor> </capability> </device> <device> <name>pci_1022_1200</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>24</slot> <function>0</function> <product id='4608'>Family 10h [Opteron, Athlon64, Sempron] HyperTransport Configuration</product> <vendor id='4130'>Advanced Micro Devices [AMD]</vendor> </capability> </device> <device> <name>pci_1022_1200_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>25</slot> <function>0</function> <product id='4608'>Family 10h [Opteron, Athlon64, Sempron] HyperTransport Configuration</product> <vendor id='4130'>Advanced Micro Devices [AMD]</vendor> </capability> </device> <device> <name>pci_1022_1201</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>24</slot> <function>1</function> <product id='4609'>Family 10h [Opteron, Athlon64, Sempron] Address Map</product> <vendor id='4130'>Advanced Micro Devices [AMD]</vendor> </capability> </device> <device> <name>pci_1022_1201_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>25</slot> <function>1</function> <product id='4609'>Family 10h [Opteron, Athlon64, Sempron] Address Map</product> <vendor id='4130'>Advanced Micro Devices [AMD]</vendor> </capability> </device> <device> <name>pci_1022_1202</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>24</slot> <function>2</function> <product id='4610'>Family 10h [Opteron, Athlon64, Sempron] DRAM Controller</product> <vendor id='4130'>Advanced Micro Devices [AMD]</vendor> </capability> </device> <device> <name>pci_1022_1202_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>25</slot> <function>2</function> <product id='4610'>Family 10h [Opteron, Athlon64, Sempron] DRAM Controller</product> <vendor id='4130'>Advanced Micro Devices [AMD]</vendor> </capability> </device> <device> <name>pci_1022_1203</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>24</slot> <function>3</function> <product id='4611'>Family 10h [Opteron, Athlon64, Sempron] Miscellaneous Control</product> <vendor id='4130'>Advanced Micro Devices [AMD]</vendor> </capability> </device> <device> <name>pci_1022_1203_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>25</slot> <function>3</function> <product id='4611'>Family 10h [Opteron, Athlon64, Sempron] Miscellaneous Control</product> <vendor id='4130'>Advanced Micro Devices [AMD]</vendor> </capability> </device> <device> <name>pci_1022_1204</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>24</slot> <function>4</function> <product id='4612'>Family 10h [Opteron, Athlon64, Sempron] Link Control</product> <vendor id='4130'>Advanced Micro Devices [AMD]</vendor> </capability> </device> <device> <name>pci_1022_1204_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>25</slot> <function>4</function> <product id='4612'>Family 10h [Opteron, Athlon64, Sempron] Link Control</product> <vendor id='4130'>Advanced Micro Devices [AMD]</vendor> </capability> </device> <device> <name>pci_1033_125</name> <parent>pci_10de_378</parent> <capability type='pci'> <domain>0</domain> <bus>3</bus> <slot>0</slot> <function>0</function> <product id='293'>uPD720400 PCI Express - PCI/PCI-X Bridge</product> <vendor id='4147'>NEC Corporation</vendor> </capability> </device> <device> <name>pci_1033_125_0</name> <parent>pci_10de_378</parent> <capability type='pci'> <domain>0</domain> <bus>3</bus> <slot>0</slot> <function>1</function> <product id='293'>uPD720400 PCI Express - PCI/PCI-X Bridge</product> <vendor id='4147'>NEC Corporation</vendor> </capability> </device> <device> <name>pci_104c_8023</name> <parent>pci_10de_370</parent> <capability type='pci'> <domain>0</domain> <bus>1</bus> <slot>5</slot> <function>0</function> <product id='32803'>TSB43AB22/A IEEE-1394a-2000 Controller (PHY/Link)</product> <vendor id='4172'>Texas Instruments</vendor> </capability> </device> <device> <name>pci_10de_361</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>128</bus> <slot>1</slot> <function>0</function> <product id='865'>MCP55 LPC Bridge</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_364</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>1</slot> <function>0</function> <product id='868'>MCP55 LPC Bridge</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_368</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>1</slot> <function>1</function> <product id='872'>MCP55 SMBus</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_368_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>128</bus> <slot>1</slot> <function>1</function> <product id='872'>MCP55 SMBus</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_369</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>0</slot> <function>0</function> <product id='873'>MCP55 Memory Controller</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_369_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>128</bus> <slot>0</slot> <function>0</function> <product id='873'>MCP55 Memory Controller</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_36c</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>2</slot> <function>0</function> <product id='876'>MCP55 USB Controller</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_36d</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>2</slot> <function>1</function> <product id='877'>MCP55 USB Controller</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_36e</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>4</slot> <function>0</function> <product id='878'>MCP55 IDE</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_36e_scsi_host</name> <parent>pci_10de_36e</parent> <capability type='scsi_host'> <host>6</host> </capability> </device> <device> <name>pci_10de_36e_scsi_host_scsi_device_lun0</name> <parent>pci_10de_36e_scsi_host</parent> <capability type='scsi'> <host>6</host> <bus>0</bus> <target>0</target> <lun>0</lun> <type>cdrom</type> </capability> </device> <device> <name>pci_10de_370</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>6</slot> <function>0</function> <product id='880'>MCP55 PCI bridge</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_371</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>6</slot> <function>1</function> <product id='881'>MCP55 High Definition Audio</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_372</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>8</slot> <function>0</function> <product id='882'>MCP55 Ethernet</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_372_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>9</slot> <function>0</function> <product id='882'>MCP55 Ethernet</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_376</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>10</slot> <function>0</function> <product id='886'>MCP55 PCI Express bridge</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_376_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>128</bus> <slot>10</slot> <function>0</function> <product id='886'>MCP55 PCI Express bridge</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_377</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>15</slot> <function>0</function> <product id='887'>MCP55 PCI Express bridge</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_377_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>128</bus> <slot>15</slot> <function>0</function> <product id='887'>MCP55 PCI Express bridge</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_378</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>13</slot> <function>0</function> <product id='888'>MCP55 PCI Express bridge</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_378_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>128</bus> <slot>13</slot> <function>0</function> <product id='888'>MCP55 PCI Express bridge</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_37f</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>5</slot> <function>0</function> <product id='895'>MCP55 SATA Controller</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_37f_0</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>5</slot> <function>1</function> <product id='895'>MCP55 SATA Controller</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_37f_1</name> <parent>computer</parent> <capability type='pci'> <domain>0</domain> <bus>0</bus> <slot>5</slot> <function>2</function> <product id='895'>MCP55 SATA Controller</product> <vendor id='4318'>nVidia Corporation</vendor> </capability> </device> <device> <name>pci_10de_37f_scsi_host</name> <parent>pci_10de_37f</parent> <capability type='scsi_host'> <host>0</host> </capability> </device> <device> <name>pci_10de_37f_scsi_host_scsi_device_lun0</name> <parent>pci_10de_37f_scsi_host</parent> <capability type='scsi'> <host>0</host> <bus>0</bus> <target>0</target> <lun>0</lun> <type>disk</type> </capability> </device> <device> <name>platform_floppy_0_storage_platform_floppy</name> <parent>platform_floppy_0</parent> <capability type='storage'> <block>/dev/fd0</blocke> <bus>platform</bus> <drive_type>floppy</drive_type> <vendor>PC Floppy Drive</vendor> <capability type='removable'> <media_available>0</media_available> <media_size>0</media_size> </capability> </capability> </device> <device> <name>storage_model_DVD_A__DH20A4P</name> <parent>pci_10de_36e_scsi_host_scsi_device_lun0</parent> <capability type='storage'> <block>/dev/sr0</blocke> <bus>scsi</bus> <drive_type>cdrom</drive_type> <model>DVD A DH20A4P</model> <vendor>ATAPI</vendor> <capability type='removable'> <media_available>0</media_available> <media_size>0</media_size> </capability> </capability> </device> <device> <name>storage_serial_SATA_WDC_WD3200AAKS__WD_WCASE0223035</name> <parent>pci_10de_37f_scsi_host_scsi_device_lun0</parent> <capability type='storage'> <block>/dev/sda</blocke> <bus>scsi</bus> <drive_type>disk</drive_type> <model>WDC WD3200AAKS-0</model> <vendor>ATA</vendor> <size>320072933376</size> </capability> </device> <device> <name>usb_device_1d6b_1_0000_00_02_0</name> <parent>pci_10de_36c</parent> <capability type='usb_device'> <bus>2</bus> <device>1</device> <product id='1'>1.1 root hub</product> <vendor id='7531'>Linux Foundation</vendor> </capability> </device> <device> <name>usb_device_1d6b_1_0000_00_02_0_if0</name> <parent>usb_device_1d6b_1_0000_00_02_0</parent> <capability type='usb'> <number>0</number> <class>9</class> <subclass>0</subclass> <protocol>0</protocol> </capability> </device> <device> <name>usb_device_1d6b_2_0000_00_02_1</name> <parent>pci_10de_36d</parent> <capability type='usb_device'> <bus>1</bus> <device>1</device> <product id='2'>2.0 root hub</product> <vendor id='7531'>Linux Foundation</vendor> </capability> </device> <device> <name>usb_device_1d6b_2_0000_00_02_1_if0</name> <parent>usb_device_1d6b_2_0000_00_02_1</parent> <capability type='usb'> <number>0</number> <class>9</class> <subclass>0</subclass> <protocol>0</protocol> </capability> </device> -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Nov 20, 2008 at 05:49:50PM +0000, Daniel P. Berrange wrote:
FYI, this is a repost of the final node device patches I intend to commit tomorrow. There's a few changes based on Mark's feedback which I'll note against each patch...
This series is now fully committed to CVS. Many thanks to David Lively for doing all the hard work on this patch series. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Mark McLoughlin