[Libvir] PATCH: 0/16: Storage management APIs

The patch series that follows provides storage management APIs for dealing with local directories, local filesystems, remote filesystems, logical volumes (LVM), disk partitioning, and iSCSI. It will also soon support SCSI host adapters (including NPIV). I'll include comments inline with each patch. The final patch in the series also provides a 'storage.html' page for the website giving a fairly friendly overview of the storage pool types and their associated XML examples. Although included here, the one API i really don't like is the one for discovery virConnectDiscoverStoragePools, since its API is not flexible enough in terms of metadata it accepts. I will probably just leave this out next time around, since its not critical for the core functionality and can thus be added later. Also TBD is a way to format a filesystem on a volume, and how to clone an existing volume, and take a snapshot. These things can all be added at a later date. Finally it does not include the async job support. This is also best added once the core code is merged. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This defines the public API for the storage pool and volume support. The naming of the APIs follows the example set by the network and domain APIs whereever the logical functions match. The main change since previous iterations is the addition of an explicit API for virStoragePoolBuild and virStoragePoolDelete, to allow formatting of the underlying storage, and permanent deletion. Many of the APIs also now have an 'unsigned int flags' param. It is also possible to lookup a volume directly based on its path or key without having the associated pool object ahead of time. include/libvirt/libvirt.h | 202 +++++++++++++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h.in | 202 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_sym.version | 45 +++++++++ 3 files changed, 449 insertions(+) diff -r d674e201bd98 include/libvirt/libvirt.h --- a/include/libvirt/libvirt.h Tue Feb 05 12:24:51 2008 -0500 +++ b/include/libvirt/libvirt.h Tue Feb 05 13:28:02 2008 -0500 @@ -859,6 +859,208 @@ int virNetworkSetAutostart (virNetwork int virNetworkSetAutostart (virNetworkPtr network, int autostart); + +/** + * virStoragePool: + * + * a virStoragePool is a private structure representing a storage pool + */ +typedef struct _virStoragePool virStoragePool; + +/** + * virStoragePoolPtr: + * + * a virStoragePoolPtr is pointer to a virStoragePool private structure, this is the + * type used to reference a storage pool in the API. + */ +typedef virStoragePool *virStoragePoolPtr; + + +typedef enum { + VIR_STORAGE_POOL_INACTIVE = 0, /* Not running */ + VIR_STORAGE_POOL_BUILDING = 1, /* Initializing pool, not available */ + VIR_STORAGE_POOL_RUNNING = 2, /* Running normally */ + VIR_STORAGE_POOL_DEGRADED = 3, /* Running degraded */ +} virStoragePoolState; + + +typedef enum { + VIR_STORAGE_POOL_BUILD_NEW = 0, /* Regular build from scratch */ + VIR_STORAGE_POOL_BUILD_REPAIR = 1, /* Repair / reinitialize */ + VIR_STORAGE_POOL_BUILD_EXTEND = 2 /* Extend existing pool */ +} virStoragePoolBuildFlags; + +typedef enum { + VIR_STORAGE_POOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_POOL_DELETE_CLEAR = 1, /* Clear all data to zeros (slow) */ +} virStoragePoolDeleteFlags; + +typedef struct _virStoragePoolInfo virStoragePoolInfo; + +struct _virStoragePoolInfo { + int state; /* virStoragePoolState flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ + unsigned long long available; /* Remaining free space bytes */ +}; + +typedef virStoragePoolInfo *virStoragePoolInfoPtr; + + +/** + * virStorageVol: + * + * a virStorageVol is a private structure representing a storage volume + */ +typedef struct _virStorageVol virStorageVol; + +/** + * virStorageVolPtr: + * + * a virStorageVolPtr is pointer to a virStorageVol private structure, this is the + * type used to reference a storage volume in the API. + */ +typedef virStorageVol *virStorageVolPtr; + + +typedef enum { + VIR_STORAGE_VOL_FILE = 0, /* Regular file based volumes */ + VIR_STORAGE_VOL_BLOCK = 1, /* Block based volumes */ + VIR_STORAGE_VOL_VIRTUAL = 2, /* Volumes which aren't assignable to guests */ +} virStorageVolType; + +typedef enum { + VIR_STORAGE_VOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_VOL_DELETE_CLEAR = 1, /* Clear all data to zeros (slow) */ +} virStorageVolDeleteFlags; + +typedef struct _virStorageVolInfo virStorageVolInfo; + +struct _virStorageVolInfo { + int type; /* virStorageVolType flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ +}; + +typedef virStorageVolInfo *virStorageVolInfoPtr; + +/* + * Get connection from pool. + */ +virConnectPtr virStoragePoolGetConnect (virStoragePoolPtr pool); + +/* + * List active storage pools + */ +int virConnectNumOfStoragePools (virConnectPtr conn); +int virConnectListStoragePools (virConnectPtr conn, + char **const names, + int maxnames); + +/* + * List inactive storage pools + */ +int virConnectNumOfDefinedStoragePools(virConnectPtr conn); +int virConnectListDefinedStoragePools(virConnectPtr conn, + char **const names, + int maxnames); + +/* + * Query a host for storage pools of a particular type + */ +int virConnectDiscoverStoragePools(virConnectPtr conn, + const char *hostname, + const char *type, + unsigned int flags, + char ***xmlDesc); + +/* + * Lookup pool by name or uuid + */ +virStoragePoolPtr virStoragePoolLookupByName (virConnectPtr conn, + const char *name); +virStoragePoolPtr virStoragePoolLookupByUUID (virConnectPtr conn, + const unsigned char *uuid); +virStoragePoolPtr virStoragePoolLookupByUUIDString(virConnectPtr conn, + const char *uuid); +virStoragePoolPtr virStoragePoolLookupByVolume (virStorageVolPtr vol); + +/* + * Creating/destroying pools + */ +virStoragePoolPtr virStoragePoolCreateXML (virConnectPtr conn, + const char *xmlDesc); +virStoragePoolPtr virStoragePoolDefineXML (virConnectPtr conn, + const char *xmlDesc); +int virStoragePoolBuild (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolUndefine (virStoragePoolPtr pool); +int virStoragePoolCreate (virStoragePoolPtr pool); +int virStoragePoolDestroy (virStoragePoolPtr pool); +int virStoragePoolDelete (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolFree (virStoragePoolPtr pool); +int virStoragePoolRefresh (virStoragePoolPtr pool, + unsigned int flags); + +/* + * StoragePool information + */ +const char* virStoragePoolGetName (virStoragePoolPtr pool); +int virStoragePoolGetUUID (virStoragePoolPtr pool, + unsigned char *uuid); +int virStoragePoolGetUUIDString (virStoragePoolPtr pool, + char *buf); + +int virStoragePoolGetInfo (virStoragePoolPtr vol, + virStoragePoolInfoPtr info); + +char * virStoragePoolGetXMLDesc (virStoragePoolPtr pool, + unsigned int flags); + +int virStoragePoolGetAutostart (virStoragePoolPtr pool, + int *autostart); +int virStoragePoolSetAutostart (virStoragePoolPtr pool, + int autostart); + +/* + * List/lookup storage volumes within a pool + */ +int virStoragePoolNumOfVolumes (virStoragePoolPtr pool); +int virStoragePoolListVolumes (virStoragePoolPtr pool, + char **const names, + int maxnames); + +virConnectPtr virStorageVolGetConnect (virStorageVolPtr vol); + +/* + * Lookup volumes based on various attributes + */ +virStorageVolPtr virStorageVolLookupByName (virStoragePoolPtr pool, + const char *name); +virStorageVolPtr virStorageVolLookupByKey (virConnectPtr conn, + const char *key); +virStorageVolPtr virStorageVolLookupByPath (virConnectPtr conn, + const char *path); + + +const char* virStorageVolGetName (virStorageVolPtr vol); +const char* virStorageVolGetKey (virStorageVolPtr vol); + +virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags); +int virStorageVolDelete (virStorageVolPtr vol, + unsigned int flags); +int virStorageVolFree (virStorageVolPtr vol); + +int virStorageVolGetInfo (virStorageVolPtr vol, + virStorageVolInfoPtr info); +char * virStorageVolGetXMLDesc (virStorageVolPtr pool, + unsigned int flags); + +char * virStorageVolGetPath (virStorageVolPtr vol); + #ifdef __cplusplus } #endif diff -r d674e201bd98 include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in Tue Feb 05 12:24:51 2008 -0500 +++ b/include/libvirt/libvirt.h.in Tue Feb 05 13:28:02 2008 -0500 @@ -859,6 +859,208 @@ int virNetworkSetAutostart (virNetwork int virNetworkSetAutostart (virNetworkPtr network, int autostart); + +/** + * virStoragePool: + * + * a virStoragePool is a private structure representing a storage pool + */ +typedef struct _virStoragePool virStoragePool; + +/** + * virStoragePoolPtr: + * + * a virStoragePoolPtr is pointer to a virStoragePool private structure, this is the + * type used to reference a storage pool in the API. + */ +typedef virStoragePool *virStoragePoolPtr; + + +typedef enum { + VIR_STORAGE_POOL_INACTIVE = 0, /* Not running */ + VIR_STORAGE_POOL_BUILDING = 1, /* Initializing pool, not available */ + VIR_STORAGE_POOL_RUNNING = 2, /* Running normally */ + VIR_STORAGE_POOL_DEGRADED = 3, /* Running degraded */ +} virStoragePoolState; + + +typedef enum { + VIR_STORAGE_POOL_BUILD_NEW = 0, /* Regular build from scratch */ + VIR_STORAGE_POOL_BUILD_REPAIR = 1, /* Repair / reinitialize */ + VIR_STORAGE_POOL_BUILD_EXTEND = 2 /* Extend existing pool */ +} virStoragePoolBuildFlags; + +typedef enum { + VIR_STORAGE_POOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_POOL_DELETE_CLEAR = 1, /* Clear all data to zeros (slow) */ +} virStoragePoolDeleteFlags; + +typedef struct _virStoragePoolInfo virStoragePoolInfo; + +struct _virStoragePoolInfo { + int state; /* virStoragePoolState flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ + unsigned long long available; /* Remaining free space bytes */ +}; + +typedef virStoragePoolInfo *virStoragePoolInfoPtr; + + +/** + * virStorageVol: + * + * a virStorageVol is a private structure representing a storage volume + */ +typedef struct _virStorageVol virStorageVol; + +/** + * virStorageVolPtr: + * + * a virStorageVolPtr is pointer to a virStorageVol private structure, this is the + * type used to reference a storage volume in the API. + */ +typedef virStorageVol *virStorageVolPtr; + + +typedef enum { + VIR_STORAGE_VOL_FILE = 0, /* Regular file based volumes */ + VIR_STORAGE_VOL_BLOCK = 1, /* Block based volumes */ + VIR_STORAGE_VOL_VIRTUAL = 2, /* Volumes which aren't assignable to guests */ +} virStorageVolType; + +typedef enum { + VIR_STORAGE_VOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_VOL_DELETE_CLEAR = 1, /* Clear all data to zeros (slow) */ +} virStorageVolDeleteFlags; + +typedef struct _virStorageVolInfo virStorageVolInfo; + +struct _virStorageVolInfo { + int type; /* virStorageVolType flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ +}; + +typedef virStorageVolInfo *virStorageVolInfoPtr; + +/* + * Get connection from pool. + */ +virConnectPtr virStoragePoolGetConnect (virStoragePoolPtr pool); + +/* + * List active storage pools + */ +int virConnectNumOfStoragePools (virConnectPtr conn); +int virConnectListStoragePools (virConnectPtr conn, + char **const names, + int maxnames); + +/* + * List inactive storage pools + */ +int virConnectNumOfDefinedStoragePools(virConnectPtr conn); +int virConnectListDefinedStoragePools(virConnectPtr conn, + char **const names, + int maxnames); + +/* + * Query a host for storage pools of a particular type + */ +int virConnectDiscoverStoragePools(virConnectPtr conn, + const char *hostname, + const char *type, + unsigned int flags, + char ***xmlDesc); + +/* + * Lookup pool by name or uuid + */ +virStoragePoolPtr virStoragePoolLookupByName (virConnectPtr conn, + const char *name); +virStoragePoolPtr virStoragePoolLookupByUUID (virConnectPtr conn, + const unsigned char *uuid); +virStoragePoolPtr virStoragePoolLookupByUUIDString(virConnectPtr conn, + const char *uuid); +virStoragePoolPtr virStoragePoolLookupByVolume (virStorageVolPtr vol); + +/* + * Creating/destroying pools + */ +virStoragePoolPtr virStoragePoolCreateXML (virConnectPtr conn, + const char *xmlDesc); +virStoragePoolPtr virStoragePoolDefineXML (virConnectPtr conn, + const char *xmlDesc); +int virStoragePoolBuild (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolUndefine (virStoragePoolPtr pool); +int virStoragePoolCreate (virStoragePoolPtr pool); +int virStoragePoolDestroy (virStoragePoolPtr pool); +int virStoragePoolDelete (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolFree (virStoragePoolPtr pool); +int virStoragePoolRefresh (virStoragePoolPtr pool, + unsigned int flags); + +/* + * StoragePool information + */ +const char* virStoragePoolGetName (virStoragePoolPtr pool); +int virStoragePoolGetUUID (virStoragePoolPtr pool, + unsigned char *uuid); +int virStoragePoolGetUUIDString (virStoragePoolPtr pool, + char *buf); + +int virStoragePoolGetInfo (virStoragePoolPtr vol, + virStoragePoolInfoPtr info); + +char * virStoragePoolGetXMLDesc (virStoragePoolPtr pool, + unsigned int flags); + +int virStoragePoolGetAutostart (virStoragePoolPtr pool, + int *autostart); +int virStoragePoolSetAutostart (virStoragePoolPtr pool, + int autostart); + +/* + * List/lookup storage volumes within a pool + */ +int virStoragePoolNumOfVolumes (virStoragePoolPtr pool); +int virStoragePoolListVolumes (virStoragePoolPtr pool, + char **const names, + int maxnames); + +virConnectPtr virStorageVolGetConnect (virStorageVolPtr vol); + +/* + * Lookup volumes based on various attributes + */ +virStorageVolPtr virStorageVolLookupByName (virStoragePoolPtr pool, + const char *name); +virStorageVolPtr virStorageVolLookupByKey (virConnectPtr conn, + const char *key); +virStorageVolPtr virStorageVolLookupByPath (virConnectPtr conn, + const char *path); + + +const char* virStorageVolGetName (virStorageVolPtr vol); +const char* virStorageVolGetKey (virStorageVolPtr vol); + +virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags); +int virStorageVolDelete (virStorageVolPtr vol, + unsigned int flags); +int virStorageVolFree (virStorageVolPtr vol); + +int virStorageVolGetInfo (virStorageVolPtr vol, + virStorageVolInfoPtr info); +char * virStorageVolGetXMLDesc (virStorageVolPtr pool, + unsigned int flags); + +char * virStorageVolGetPath (virStorageVolPtr vol); + #ifdef __cplusplus } #endif diff -r d674e201bd98 src/libvirt_sym.version --- a/src/libvirt_sym.version Tue Feb 05 12:24:51 2008 -0500 +++ b/src/libvirt_sym.version Tue Feb 05 13:28:02 2008 -0500 @@ -116,6 +116,49 @@ virNetworkGetAutostart; virNetworkSetAutostart; + virStoragePoolGetConnect; + virConnectNumOfStoragePools; + virConnectNumOfDefinedStoragePools; + virConnectListStoragePools; + virConnectListDefinedStoragePools; + virConnectDiscoverStoragePools; + virStoragePoolLookupByName; + virStoragePoolLookupByUUID; + virStoragePoolLookupByUUIDString; + virStoragePoolLookupByVolume; + virStoragePoolCreateXML; + virStoragePoolDefineXML; + virStoragePoolUndefine; + virStoragePoolCreate; + virStoragePoolBuild; + virStoragePoolDestroy; + virStoragePoolDelete; + virStoragePoolRefresh; + virStoragePoolFree; + virStoragePoolGetName; + virStoragePoolGetUUID; + virStoragePoolGetUUIDString; + virStoragePoolGetInfo; + virStoragePoolGetXMLDesc; + virStoragePoolSetAutostart; + virStoragePoolGetAutostart; + virStoragePoolNumOfVolumes; + virStoragePoolListVolumes; + + virConnectNumOfStorageVolumes; + virConnectListStorageVolumes; + virStorageVolLookupByName; + virStorageVolLookupByKey; + virStorageVolLookupByPath; + virStorageVolCreateXML; + virStorageVolDelete; + virStorageVolFree; + virStorageVolGetName; + virStorageVolGetKey; + virStorageVolGetInfo; + virStorageVolGetXMLDesc; + virStorageVolGetPath; + /* Symbols with __ are private only for use by the libvirtd daemon. They are not part of stable ABI @@ -133,6 +176,8 @@ __virGetDomain; __virGetNetwork; + __virGetStoragePool; + __virGetStorageVol; __virEventRegisterImpl; -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:30:07AM +0000, Daniel P. Berrange wrote:
This defines the public API for the storage pool and volume support. The naming of the APIs follows the example set by the network and domain APIs whereever the logical functions match. The main change since previous iterations is the addition of an explicit API for virStoragePoolBuild and virStoragePoolDelete, to allow formatting of the underlying storage, and permanent deletion. Many of the APIs also now have an 'unsigned int flags' param. It is also possible to lookup a volume directly based on its path or key without having the associated pool object ahead of time.
include/libvirt/libvirt.h | 202 +++++++++++++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h.in | 202 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_sym.version | 45 +++++++++ 3 files changed, 449 insertions(+)
diff -r d674e201bd98 include/libvirt/libvirt.h --- a/include/libvirt/libvirt.h Tue Feb 05 12:24:51 2008 -0500 +++ b/include/libvirt/libvirt.h Tue Feb 05 13:28:02 2008 -0500 @@ -859,6 +859,208 @@ int virNetworkSetAutostart (virNetwork int virNetworkSetAutostart (virNetworkPtr network, int autostart);
+ +/** + * virStoragePool: + * + * a virStoragePool is a private structure representing a storage pool + */ +typedef struct _virStoragePool virStoragePool; + +/** + * virStoragePoolPtr: + * + * a virStoragePoolPtr is pointer to a virStoragePool private structure, this is the + * type used to reference a storage pool in the API. + */ +typedef virStoragePool *virStoragePoolPtr; + + +typedef enum { + VIR_STORAGE_POOL_INACTIVE = 0, /* Not running */ + VIR_STORAGE_POOL_BUILDING = 1, /* Initializing pool, not available */ + VIR_STORAGE_POOL_RUNNING = 2, /* Running normally */ + VIR_STORAGE_POOL_DEGRADED = 3, /* Running degraded */ +} virStoragePoolState; + + +typedef enum { + VIR_STORAGE_POOL_BUILD_NEW = 0, /* Regular build from scratch */ + VIR_STORAGE_POOL_BUILD_REPAIR = 1, /* Repair / reinitialize */ + VIR_STORAGE_POOL_BUILD_EXTEND = 2 /* Extend existing pool */
Is shrinking not possible ? things like compressing to fewer volumes
+} virStoragePoolBuildFlags; + +typedef enum { + VIR_STORAGE_POOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_POOL_DELETE_CLEAR = 1, /* Clear all data to zeros (slow) */
I would name it VIR_STORAGE_POOL_DELETE_ZEROED , it's more obvious what the operstion does.
+} virStoragePoolDeleteFlags; + +typedef struct _virStoragePoolInfo virStoragePoolInfo; + +struct _virStoragePoolInfo { + int state; /* virStoragePoolState flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ + unsigned long long available; /* Remaining free space bytes */ +}; + +typedef virStoragePoolInfo *virStoragePoolInfoPtr; + + +/** + * virStorageVol: + * + * a virStorageVol is a private structure representing a storage volume + */ +typedef struct _virStorageVol virStorageVol; + +/** + * virStorageVolPtr: + * + * a virStorageVolPtr is pointer to a virStorageVol private structure, this is the + * type used to reference a storage volume in the API. + */ +typedef virStorageVol *virStorageVolPtr; + + +typedef enum { + VIR_STORAGE_VOL_FILE = 0, /* Regular file based volumes */ + VIR_STORAGE_VOL_BLOCK = 1, /* Block based volumes */ + VIR_STORAGE_VOL_VIRTUAL = 2, /* Volumes which aren't assignable to guests */ +} virStorageVolType;
I wonder if it's worth making the distinction between virtual storage with local state and those without, the second having really good properties for example when deciding to do a migration. But current enum is fine already.
+typedef enum { + VIR_STORAGE_VOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_VOL_DELETE_CLEAR = 1, /* Clear all data to zeros (slow) */ +} virStorageVolDeleteFlags;
I still prefer ZEROED as CLEAr but not a big deal.
+typedef struct _virStorageVolInfo virStorageVolInfo; + +struct _virStorageVolInfo { + int type; /* virStorageVolType flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ +}; + +typedef virStorageVolInfo *virStorageVolInfoPtr; + +/* + * Get connection from pool. + */ +virConnectPtr virStoragePoolGetConnect (virStoragePoolPtr pool); + +/* + * List active storage pools + */ +int virConnectNumOfStoragePools (virConnectPtr conn); +int virConnectListStoragePools (virConnectPtr conn, + char **const names, + int maxnames); + +/* + * List inactive storage pools + */ +int virConnectNumOfDefinedStoragePools(virConnectPtr conn); +int virConnectListDefinedStoragePools(virConnectPtr conn, + char **const names, + int maxnames); + +/* + * Query a host for storage pools of a particular type + */ +int virConnectDiscoverStoragePools(virConnectPtr conn, + const char *hostname, + const char *type, + unsigned int flags, + char ***xmlDesc); + +/* + * Lookup pool by name or uuid + */ +virStoragePoolPtr virStoragePoolLookupByName (virConnectPtr conn, + const char *name); +virStoragePoolPtr virStoragePoolLookupByUUID (virConnectPtr conn, + const unsigned char *uuid); +virStoragePoolPtr virStoragePoolLookupByUUIDString(virConnectPtr conn, + const char *uuid); +virStoragePoolPtr virStoragePoolLookupByVolume (virStorageVolPtr vol); + +/* + * Creating/destroying pools + */ +virStoragePoolPtr virStoragePoolCreateXML (virConnectPtr conn, + const char *xmlDesc); +virStoragePoolPtr virStoragePoolDefineXML (virConnectPtr conn, + const char *xmlDesc); +int virStoragePoolBuild (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolUndefine (virStoragePoolPtr pool); +int virStoragePoolCreate (virStoragePoolPtr pool);
Can we use a flag here ? Is the operation synchronous or not ? Or is that an instant operation and only Build() is likely to take some time. I would add an unused flag for safety here.
+int virStoragePoolDestroy (virStoragePoolPtr pool); +int virStoragePoolDelete (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolFree (virStoragePoolPtr pool); +int virStoragePoolRefresh (virStoragePoolPtr pool, + unsigned int flags); + +/* + * StoragePool information + */ +const char* virStoragePoolGetName (virStoragePoolPtr pool); +int virStoragePoolGetUUID (virStoragePoolPtr pool, + unsigned char *uuid); +int virStoragePoolGetUUIDString (virStoragePoolPtr pool, + char *buf); + +int virStoragePoolGetInfo (virStoragePoolPtr vol, + virStoragePoolInfoPtr info); + +char * virStoragePoolGetXMLDesc (virStoragePoolPtr pool, + unsigned int flags); + +int virStoragePoolGetAutostart (virStoragePoolPtr pool, + int *autostart); +int virStoragePoolSetAutostart (virStoragePoolPtr pool, + int autostart); + +/* + * List/lookup storage volumes within a pool + */ +int virStoragePoolNumOfVolumes (virStoragePoolPtr pool); +int virStoragePoolListVolumes (virStoragePoolPtr pool, + char **const names, + int maxnames); + +virConnectPtr virStorageVolGetConnect (virStorageVolPtr vol); + +/* + * Lookup volumes based on various attributes + */ +virStorageVolPtr virStorageVolLookupByName (virStoragePoolPtr pool, + const char *name); +virStorageVolPtr virStorageVolLookupByKey (virConnectPtr conn, + const char *key); +virStorageVolPtr virStorageVolLookupByPath (virConnectPtr conn, + const char *path); + + +const char* virStorageVolGetName (virStorageVolPtr vol); +const char* virStorageVolGetKey (virStorageVolPtr vol); + +virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags); +int virStorageVolDelete (virStorageVolPtr vol, + unsigned int flags); +int virStorageVolFree (virStorageVolPtr vol); + +int virStorageVolGetInfo (virStorageVolPtr vol, + virStorageVolInfoPtr info); +char * virStorageVolGetXMLDesc (virStorageVolPtr pool, + unsigned int flags); + +char * virStorageVolGetPath (virStorageVolPtr vol); + #ifdef __cplusplus } #endif
Looks fine to me in general.
--- a/src/libvirt_sym.version Tue Feb 05 12:24:51 2008 -0500 +++ b/src/libvirt_sym.version Tue Feb 05 13:28:02 2008 -0500 @@ -116,6 +116,49 @@ virNetworkGetAutostart; virNetworkSetAutostart;
+ virStoragePoolGetConnect; + virConnectNumOfStoragePools; + virConnectNumOfDefinedStoragePools; + virConnectListStoragePools; + virConnectListDefinedStoragePools; + virConnectDiscoverStoragePools; + virStoragePoolLookupByName; + virStoragePoolLookupByUUID; + virStoragePoolLookupByUUIDString; + virStoragePoolLookupByVolume; + virStoragePoolCreateXML; + virStoragePoolDefineXML; + virStoragePoolUndefine; + virStoragePoolCreate; + virStoragePoolBuild; + virStoragePoolDestroy; + virStoragePoolDelete; + virStoragePoolRefresh; + virStoragePoolFree; + virStoragePoolGetName; + virStoragePoolGetUUID; + virStoragePoolGetUUIDString; + virStoragePoolGetInfo; + virStoragePoolGetXMLDesc; + virStoragePoolSetAutostart; + virStoragePoolGetAutostart; + virStoragePoolNumOfVolumes; + virStoragePoolListVolumes; + + virConnectNumOfStorageVolumes; + virConnectListStorageVolumes; + virStorageVolLookupByName; + virStorageVolLookupByKey; + virStorageVolLookupByPath; + virStorageVolCreateXML; + virStorageVolDelete; + virStorageVolFree; + virStorageVolGetName; + virStorageVolGetKey; + virStorageVolGetInfo; + virStorageVolGetXMLDesc; + virStorageVolGetPath; + /* Symbols with __ are private only for use by the libvirtd daemon. They are not part of stable ABI @@ -133,6 +176,8 @@
__virGetDomain; __virGetNetwork; + __virGetStoragePool; + __virGetStorageVol;
Okay, +1, I would love to see this or part of it commited before the next round, even if all entry points are present, as a temporary way to decrease patches and separate the agreed upon from what would need to be refined. I guess it's not a problem until we make a new release. Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Thu, Feb 14, 2008 at 06:06:08AM -0500, Daniel Veillard wrote:
On Tue, Feb 12, 2008 at 04:30:07AM +0000, Daniel P. Berrange wrote:
+ + +typedef enum { + VIR_STORAGE_POOL_BUILD_NEW = 0, /* Regular build from scratch */ + VIR_STORAGE_POOL_BUILD_REPAIR = 1, /* Repair / reinitialize */ + VIR_STORAGE_POOL_BUILD_EXTEND = 2 /* Extend existing pool */
Is shrinking not possible ? things like compressing to fewer volumes
I guess it could conceivably be supported. I should rename this to be VIR_STORAGE_POOL_BUILD_RESIZE since the distinction between extending and shrinking is not really relevant from the context of this API.
+} virStoragePoolBuildFlags; + +typedef enum { + VIR_STORAGE_POOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_POOL_DELETE_CLEAR = 1, /* Clear all data to zeros (slow) */
I would name it VIR_STORAGE_POOL_DELETE_ZEROED , it's more obvious what the operstion does.
Good idea.
+typedef enum { + VIR_STORAGE_VOL_FILE = 0, /* Regular file based volumes */ + VIR_STORAGE_VOL_BLOCK = 1, /* Block based volumes */ + VIR_STORAGE_VOL_VIRTUAL = 2, /* Volumes which aren't assignable to guests */ +} virStorageVolType;
I wonder if it's worth making the distinction between virtual storage with local state and those without, the second having really good properties for example when deciding to do a migration. But current enum is fine already.
Sorry, there's a mistake here - the VIR_STORAGE_VOL_VIRTUAL should not be there anymore. Since I removed the concept of 'singleton pools' it is no longer neccessary - previously it would be used for a volume which pointed to a LVM volume group - but this was just stupid, so i killed it.
+typedef enum { + VIR_STORAGE_VOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_VOL_DELETE_CLEAR = 1, /* Clear all data to zeros (slow) */ +} virStorageVolDeleteFlags;
I still prefer ZEROED as CLEAr but not a big deal.
Yep, I like it too
+virStoragePoolPtr virStoragePoolCreateXML (virConnectPtr conn, + const char *xmlDesc); +virStoragePoolPtr virStoragePoolDefineXML (virConnectPtr conn, + const char *xmlDesc); +int virStoragePoolBuild (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolUndefine (virStoragePoolPtr pool); +int virStoragePoolCreate (virStoragePoolPtr pool);
Can we use a flag here ? Is the operation synchronous or not ? Or is that an instant operation and only Build() is likely to take some time. I would add an unused flag for safety here.
Definitely. I should have a flags paramater on all 'Create' methods since they can take non-trivial time - eg to login to the remote server, and we might wish to control the behaviour via flags.
Okay, +1, I would love to see this or part of it commited before the next round, even if all entry points are present, as a temporary way to decrease patches and separate the agreed upon from what would need to be refined. I guess it's not a problem until we make a new release.
That works with me - it is non-triival work keeping them in sync, even with excellant quilt/mq tools. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:30:07AM +0000, Daniel P. Berrange wrote:
This defines the public API for the storage pool and volume support. The naming of the APIs follows the example set by the network and domain APIs whereever the logical functions match. The main change since previous iterations is the addition of an explicit API for virStoragePoolBuild and virStoragePoolDelete, to allow formatting of the underlying storage, and permanent deletion. Many of the APIs also now have an 'unsigned int flags' param. It is also possible to lookup a volume directly based on its path or key without having the associated pool object ahead of time.
include/libvirt/libvirt.h | 195 +++++++++++++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h.in | 195 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_sym.version | 45 +++++++++ 3 files changed, 435 insertions(+) diff -r 9106fd5b87fb include/libvirt/libvirt.h --- a/include/libvirt/libvirt.h Thu Feb 14 15:40:52 2008 -0500 +++ b/include/libvirt/libvirt.h Thu Feb 14 15:58:47 2008 -0500 @@ -760,6 +760,201 @@ int virNetworkSetAutostart (virNetwork int virNetworkSetAutostart (virNetworkPtr network, int autostart); + +/** + * virStoragePool: + * + * a virStoragePool is a private structure representing a storage pool + */ +typedef struct _virStoragePool virStoragePool; + +/** + * virStoragePoolPtr: + * + * a virStoragePoolPtr is pointer to a virStoragePool private structure, this is the + * type used to reference a storage pool in the API. + */ +typedef virStoragePool *virStoragePoolPtr; + + +typedef enum { + VIR_STORAGE_POOL_INACTIVE = 0, /* Not running */ + VIR_STORAGE_POOL_BUILDING = 1, /* Initializing pool, not available */ + VIR_STORAGE_POOL_RUNNING = 2, /* Running normally */ + VIR_STORAGE_POOL_DEGRADED = 3, /* Running degraded */ +} virStoragePoolState; + + +typedef enum { + VIR_STORAGE_POOL_BUILD_NEW = 0, /* Regular build from scratch */ + VIR_STORAGE_POOL_BUILD_REPAIR = 1, /* Repair / reinitialize */ + VIR_STORAGE_POOL_BUILD_RESIZE = 2 /* Extend existing pool */ +} virStoragePoolBuildFlags; + +typedef enum { + VIR_STORAGE_POOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_POOL_DELETE_ZEROED = 1, /* Clear all data to zeros (slow) */ +} virStoragePoolDeleteFlags; + +typedef struct _virStoragePoolInfo virStoragePoolInfo; + +struct _virStoragePoolInfo { + int state; /* virStoragePoolState flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ + unsigned long long available; /* Remaining free space bytes */ +}; + +typedef virStoragePoolInfo *virStoragePoolInfoPtr; + + +/** + * virStorageVol: + * + * a virStorageVol is a private structure representing a storage volume + */ +typedef struct _virStorageVol virStorageVol; + +/** + * virStorageVolPtr: + * + * a virStorageVolPtr is pointer to a virStorageVol private structure, this is the + * type used to reference a storage volume in the API. + */ +typedef virStorageVol *virStorageVolPtr; + + +typedef enum { + VIR_STORAGE_VOL_FILE = 0, /* Regular file based volumes */ + VIR_STORAGE_VOL_BLOCK = 1, /* Block based volumes */ +} virStorageVolType; + +typedef enum { + VIR_STORAGE_VOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_VOL_DELETE_ZEROED = 1, /* Clear all data to zeros (slow) */ +} virStorageVolDeleteFlags; + +typedef struct _virStorageVolInfo virStorageVolInfo; + +struct _virStorageVolInfo { + int type; /* virStorageVolType flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ +}; + +typedef virStorageVolInfo *virStorageVolInfoPtr; + +/* + * Get connection from pool. + */ +virConnectPtr virStoragePoolGetConnect (virStoragePoolPtr pool); + +/* + * List active storage pools + */ +int virConnectNumOfStoragePools (virConnectPtr conn); +int virConnectListStoragePools (virConnectPtr conn, + char **const names, + int maxnames); + +/* + * List inactive storage pools + */ +int virConnectNumOfDefinedStoragePools(virConnectPtr conn); +int virConnectListDefinedStoragePools(virConnectPtr conn, + char **const names, + int maxnames); + +/* + * Lookup pool by name or uuid + */ +virStoragePoolPtr virStoragePoolLookupByName (virConnectPtr conn, + const char *name); +virStoragePoolPtr virStoragePoolLookupByUUID (virConnectPtr conn, + const unsigned char *uuid); +virStoragePoolPtr virStoragePoolLookupByUUIDString(virConnectPtr conn, + const char *uuid); +virStoragePoolPtr virStoragePoolLookupByVolume (virStorageVolPtr vol); + +/* + * Creating/destroying pools + */ +virStoragePoolPtr virStoragePoolCreateXML (virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); +virStoragePoolPtr virStoragePoolDefineXML (virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); +int virStoragePoolBuild (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolUndefine (virStoragePoolPtr pool); +int virStoragePoolCreate (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolDestroy (virStoragePoolPtr pool); +int virStoragePoolDelete (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolFree (virStoragePoolPtr pool); +int virStoragePoolRefresh (virStoragePoolPtr pool, + unsigned int flags); + +/* + * StoragePool information + */ +const char* virStoragePoolGetName (virStoragePoolPtr pool); +int virStoragePoolGetUUID (virStoragePoolPtr pool, + unsigned char *uuid); +int virStoragePoolGetUUIDString (virStoragePoolPtr pool, + char *buf); + +int virStoragePoolGetInfo (virStoragePoolPtr vol, + virStoragePoolInfoPtr info); + +char * virStoragePoolGetXMLDesc (virStoragePoolPtr pool, + unsigned int flags); + +int virStoragePoolGetAutostart (virStoragePoolPtr pool, + int *autostart); +int virStoragePoolSetAutostart (virStoragePoolPtr pool, + int autostart); + +/* + * List/lookup storage volumes within a pool + */ +int virStoragePoolNumOfVolumes (virStoragePoolPtr pool); +int virStoragePoolListVolumes (virStoragePoolPtr pool, + char **const names, + int maxnames); + +virConnectPtr virStorageVolGetConnect (virStorageVolPtr vol); + +/* + * Lookup volumes based on various attributes + */ +virStorageVolPtr virStorageVolLookupByName (virStoragePoolPtr pool, + const char *name); +virStorageVolPtr virStorageVolLookupByKey (virConnectPtr conn, + const char *key); +virStorageVolPtr virStorageVolLookupByPath (virConnectPtr conn, + const char *path); + + +const char* virStorageVolGetName (virStorageVolPtr vol); +const char* virStorageVolGetKey (virStorageVolPtr vol); + +virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags); +int virStorageVolDelete (virStorageVolPtr vol, + unsigned int flags); +int virStorageVolFree (virStorageVolPtr vol); + +int virStorageVolGetInfo (virStorageVolPtr vol, + virStorageVolInfoPtr info); +char * virStorageVolGetXMLDesc (virStorageVolPtr pool, + unsigned int flags); + +char * virStorageVolGetPath (virStorageVolPtr vol); + #ifdef __cplusplus } #endif diff -r 9106fd5b87fb include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in Thu Feb 14 15:40:52 2008 -0500 +++ b/include/libvirt/libvirt.h.in Thu Feb 14 15:58:47 2008 -0500 @@ -760,6 +760,201 @@ int virNetworkSetAutostart (virNetwork int virNetworkSetAutostart (virNetworkPtr network, int autostart); + +/** + * virStoragePool: + * + * a virStoragePool is a private structure representing a storage pool + */ +typedef struct _virStoragePool virStoragePool; + +/** + * virStoragePoolPtr: + * + * a virStoragePoolPtr is pointer to a virStoragePool private structure, this is the + * type used to reference a storage pool in the API. + */ +typedef virStoragePool *virStoragePoolPtr; + + +typedef enum { + VIR_STORAGE_POOL_INACTIVE = 0, /* Not running */ + VIR_STORAGE_POOL_BUILDING = 1, /* Initializing pool, not available */ + VIR_STORAGE_POOL_RUNNING = 2, /* Running normally */ + VIR_STORAGE_POOL_DEGRADED = 3, /* Running degraded */ +} virStoragePoolState; + + +typedef enum { + VIR_STORAGE_POOL_BUILD_NEW = 0, /* Regular build from scratch */ + VIR_STORAGE_POOL_BUILD_REPAIR = 1, /* Repair / reinitialize */ + VIR_STORAGE_POOL_BUILD_RESIZE = 2 /* Extend existing pool */ +} virStoragePoolBuildFlags; + +typedef enum { + VIR_STORAGE_POOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_POOL_DELETE_ZEROED = 1, /* Clear all data to zeros (slow) */ +} virStoragePoolDeleteFlags; + +typedef struct _virStoragePoolInfo virStoragePoolInfo; + +struct _virStoragePoolInfo { + int state; /* virStoragePoolState flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ + unsigned long long available; /* Remaining free space bytes */ +}; + +typedef virStoragePoolInfo *virStoragePoolInfoPtr; + + +/** + * virStorageVol: + * + * a virStorageVol is a private structure representing a storage volume + */ +typedef struct _virStorageVol virStorageVol; + +/** + * virStorageVolPtr: + * + * a virStorageVolPtr is pointer to a virStorageVol private structure, this is the + * type used to reference a storage volume in the API. + */ +typedef virStorageVol *virStorageVolPtr; + + +typedef enum { + VIR_STORAGE_VOL_FILE = 0, /* Regular file based volumes */ + VIR_STORAGE_VOL_BLOCK = 1, /* Block based volumes */ +} virStorageVolType; + +typedef enum { + VIR_STORAGE_VOL_DELETE_NORMAL = 0, /* Delete metadata only (fast) */ + VIR_STORAGE_VOL_DELETE_ZEROED = 1, /* Clear all data to zeros (slow) */ +} virStorageVolDeleteFlags; + +typedef struct _virStorageVolInfo virStorageVolInfo; + +struct _virStorageVolInfo { + int type; /* virStorageVolType flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ +}; + +typedef virStorageVolInfo *virStorageVolInfoPtr; + +/* + * Get connection from pool. + */ +virConnectPtr virStoragePoolGetConnect (virStoragePoolPtr pool); + +/* + * List active storage pools + */ +int virConnectNumOfStoragePools (virConnectPtr conn); +int virConnectListStoragePools (virConnectPtr conn, + char **const names, + int maxnames); + +/* + * List inactive storage pools + */ +int virConnectNumOfDefinedStoragePools(virConnectPtr conn); +int virConnectListDefinedStoragePools(virConnectPtr conn, + char **const names, + int maxnames); + +/* + * Lookup pool by name or uuid + */ +virStoragePoolPtr virStoragePoolLookupByName (virConnectPtr conn, + const char *name); +virStoragePoolPtr virStoragePoolLookupByUUID (virConnectPtr conn, + const unsigned char *uuid); +virStoragePoolPtr virStoragePoolLookupByUUIDString(virConnectPtr conn, + const char *uuid); +virStoragePoolPtr virStoragePoolLookupByVolume (virStorageVolPtr vol); + +/* + * Creating/destroying pools + */ +virStoragePoolPtr virStoragePoolCreateXML (virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); +virStoragePoolPtr virStoragePoolDefineXML (virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); +int virStoragePoolBuild (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolUndefine (virStoragePoolPtr pool); +int virStoragePoolCreate (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolDestroy (virStoragePoolPtr pool); +int virStoragePoolDelete (virStoragePoolPtr pool, + unsigned int flags); +int virStoragePoolFree (virStoragePoolPtr pool); +int virStoragePoolRefresh (virStoragePoolPtr pool, + unsigned int flags); + +/* + * StoragePool information + */ +const char* virStoragePoolGetName (virStoragePoolPtr pool); +int virStoragePoolGetUUID (virStoragePoolPtr pool, + unsigned char *uuid); +int virStoragePoolGetUUIDString (virStoragePoolPtr pool, + char *buf); + +int virStoragePoolGetInfo (virStoragePoolPtr vol, + virStoragePoolInfoPtr info); + +char * virStoragePoolGetXMLDesc (virStoragePoolPtr pool, + unsigned int flags); + +int virStoragePoolGetAutostart (virStoragePoolPtr pool, + int *autostart); +int virStoragePoolSetAutostart (virStoragePoolPtr pool, + int autostart); + +/* + * List/lookup storage volumes within a pool + */ +int virStoragePoolNumOfVolumes (virStoragePoolPtr pool); +int virStoragePoolListVolumes (virStoragePoolPtr pool, + char **const names, + int maxnames); + +virConnectPtr virStorageVolGetConnect (virStorageVolPtr vol); + +/* + * Lookup volumes based on various attributes + */ +virStorageVolPtr virStorageVolLookupByName (virStoragePoolPtr pool, + const char *name); +virStorageVolPtr virStorageVolLookupByKey (virConnectPtr conn, + const char *key); +virStorageVolPtr virStorageVolLookupByPath (virConnectPtr conn, + const char *path); + + +const char* virStorageVolGetName (virStorageVolPtr vol); +const char* virStorageVolGetKey (virStorageVolPtr vol); + +virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags); +int virStorageVolDelete (virStorageVolPtr vol, + unsigned int flags); +int virStorageVolFree (virStorageVolPtr vol); + +int virStorageVolGetInfo (virStorageVolPtr vol, + virStorageVolInfoPtr info); +char * virStorageVolGetXMLDesc (virStorageVolPtr pool, + unsigned int flags); + +char * virStorageVolGetPath (virStorageVolPtr vol); + #ifdef __cplusplus } #endif diff -r 9106fd5b87fb src/libvirt_sym.version --- a/src/libvirt_sym.version Thu Feb 14 15:40:52 2008 -0500 +++ b/src/libvirt_sym.version Thu Feb 14 15:58:47 2008 -0500 @@ -100,6 +100,49 @@ virNetworkGetAutostart; virNetworkSetAutostart; + virStoragePoolGetConnect; + virConnectNumOfStoragePools; + virConnectNumOfDefinedStoragePools; + virConnectListStoragePools; + virConnectListDefinedStoragePools; + virConnectDiscoverStoragePools; + virStoragePoolLookupByName; + virStoragePoolLookupByUUID; + virStoragePoolLookupByUUIDString; + virStoragePoolLookupByVolume; + virStoragePoolCreateXML; + virStoragePoolDefineXML; + virStoragePoolUndefine; + virStoragePoolCreate; + virStoragePoolBuild; + virStoragePoolDestroy; + virStoragePoolDelete; + virStoragePoolRefresh; + virStoragePoolFree; + virStoragePoolGetName; + virStoragePoolGetUUID; + virStoragePoolGetUUIDString; + virStoragePoolGetInfo; + virStoragePoolGetXMLDesc; + virStoragePoolSetAutostart; + virStoragePoolGetAutostart; + virStoragePoolNumOfVolumes; + virStoragePoolListVolumes; + + virConnectNumOfStorageVolumes; + virConnectListStorageVolumes; + virStorageVolLookupByName; + virStorageVolLookupByKey; + virStorageVolLookupByPath; + virStorageVolCreateXML; + virStorageVolDelete; + virStorageVolFree; + virStorageVolGetName; + virStorageVolGetKey; + virStorageVolGetInfo; + virStorageVolGetXMLDesc; + virStorageVolGetPath; + /* Symbols with __ are private only for use by the libvirtd daemon. They are not part of stable ABI @@ -117,6 +160,8 @@ __virGetDomain; __virGetNetwork; + __virGetStoragePool; + __virGetStorageVol; __virEventRegisterImpl; -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Wed, Feb 20, 2008 at 04:00:39AM +0000, Daniel P. Berrange wrote:
On Tue, Feb 12, 2008 at 04:30:07AM +0000, Daniel P. Berrange wrote:
This defines the public API for the storage pool and volume support. The naming of the APIs follows the example set by the network and domain APIs whereever the logical functions match. The main change since previous iterations is the addition of an explicit API for virStoragePoolBuild and virStoragePoolDelete, to allow formatting of the underlying storage, and permanent deletion. Many of the APIs also now have an 'unsigned int flags' param. It is also possible to lookup a volume directly based on its path or key without having the associated pool object ahead of time.
include/libvirt/libvirt.h | 195 +++++++++++++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h.in | 195 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_sym.version | 45 +++++++++ 3 files changed, 435 insertions(+)
API looks fine to me, I guess all parts have been reviewed, at this point the best we can do is push it to CVS with the proposed changes, and start to get people to use it. If needed we can state that that the storage APIs are in beta in the next release(s) and will be consideered frozen in 0.5.0 within a couple of months. So let's push this ! What do you think ? Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Wed, Feb 20, 2008 at 02:52:54AM -0500, Daniel Veillard wrote:
On Wed, Feb 20, 2008 at 04:00:39AM +0000, Daniel P. Berrange wrote:
On Tue, Feb 12, 2008 at 04:30:07AM +0000, Daniel P. Berrange wrote:
This defines the public API for the storage pool and volume support. The naming of the APIs follows the example set by the network and domain APIs whereever the logical functions match. The main change since previous iterations is the addition of an explicit API for virStoragePoolBuild and virStoragePoolDelete, to allow formatting of the underlying storage, and permanent deletion. Many of the APIs also now have an 'unsigned int flags' param. It is also possible to lookup a volume directly based on its path or key without having the associated pool object ahead of time.
include/libvirt/libvirt.h | 195 +++++++++++++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h.in | 195 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_sym.version | 45 +++++++++ 3 files changed, 435 insertions(+)
API looks fine to me, I guess all parts have been reviewed, at this point the best we can do is push it to CVS with the proposed changes, and start to get people to use it. If needed we can state that that the storage APIs are in beta in the next release(s) and will be consideered frozen in 0.5.0 within a couple of months.
So let's push this !
All committed. Now to find out what I broke :-) It passes an automated build on Fedora 8, including syntax-check. We may need tweaks for other platforms as we test it... I also re-generated all the PO files Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Wed, Feb 20, 2008 at 04:10:30PM +0000, Daniel P. Berrange wrote:
On Wed, Feb 20, 2008 at 02:52:54AM -0500, Daniel Veillard wrote:
On Wed, Feb 20, 2008 at 04:00:39AM +0000, Daniel P. Berrange wrote:
On Tue, Feb 12, 2008 at 04:30:07AM +0000, Daniel P. Berrange wrote:
This defines the public API for the storage pool and volume support. The naming of the APIs follows the example set by the network and domain APIs whereever the logical functions match. The main change since previous iterations is the addition of an explicit API for virStoragePoolBuild and virStoragePoolDelete, to allow formatting of the underlying storage, and permanent deletion. Many of the APIs also now have an 'unsigned int flags' param. It is also possible to lookup a volume directly based on its path or key without having the associated pool object ahead of time.
include/libvirt/libvirt.h | 195 +++++++++++++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h.in | 195 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_sym.version | 45 +++++++++ 3 files changed, 435 insertions(+)
API looks fine to me, I guess all parts have been reviewed, at this point the best we can do is push it to CVS with the proposed changes, and start to get people to use it. If needed we can state that that the storage APIs are in beta in the next release(s) and will be consideered frozen in 0.5.0 within a couple of months.
So let's push this !
All committed. Now to find out what I broke :-) It passes an automated build on Fedora 8, including syntax-check. We may need tweaks for other platforms as we test it...
I've tested it also at least builds correctly on RHEL-5 and Fedora 9 too Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Wed, Feb 20, 2008 at 04:39:31PM +0000, Daniel P. Berrange wrote:
On Wed, Feb 20, 2008 at 04:10:30PM +0000, Daniel P. Berrange wrote:
On Wed, Feb 20, 2008 at 02:52:54AM -0500, Daniel Veillard wrote:
So let's push this !
All committed. Now to find out what I broke :-) It passes an automated build on Fedora 8, including syntax-check. We may need tweaks for other platforms as we test it...
I've tested it also at least builds correctly on RHEL-5 and Fedora 9 too
OCaml bindings also rebuilt against the full Storage API. Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

This defines the internal driver API for the storage APIs. The pattern follows that used for the existing APIs. NB, both the storage pool and storage volume objects are now top level objects. Previous iterations of this code have the volume as a child of the pool. This unneccessarily complicated the reference counting and forced you to always have a pool available first. include/libvirt/virterror.h | 4 src/driver.h | 150 ++++ src/hash.c | 317 +++++++++ src/internal.h | 68 ++ src/libvirt.c | 1403 ++++++++++++++++++++++++++++++++++++++++++++ src/virterror.c | 21 6 files changed, 1961 insertions(+), 5 deletions(-) diff -r 42e6f49e4e69 include/libvirt/virterror.h --- a/include/libvirt/virterror.h Thu Feb 07 12:33:13 2008 -0500 +++ b/include/libvirt/virterror.h Thu Feb 07 16:52:34 2008 -0500 @@ -54,6 +54,7 @@ typedef enum { VIR_FROM_OPENVZ, /* Error from OpenVZ driver */ VIR_FROM_XENXM, /* Error at Xen XM layer */ VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */ + VIR_FROM_STORAGE, /* Error from storage driver */ } virErrorDomain; @@ -132,6 +133,9 @@ typedef enum { VIR_ERR_NO_NETWORK, /* network not found */ VIR_ERR_INVALID_MAC, /* invalid MAC adress */ VIR_ERR_AUTH_FAILED, /* authentication failed */ + VIR_ERR_INVALID_STORAGE_POOL, /* invalid storage pool object */ + VIR_ERR_INVALID_STORAGE_VOL, /* invalid storage vol object */ + VIR_WAR_NO_STORAGE, /* failed to start storage */ } virErrorNumber; /** diff -r 42e6f49e4e69 src/driver.h --- a/src/driver.h Thu Feb 07 12:33:13 2008 -0500 +++ b/src/driver.h Thu Feb 07 16:52:34 2008 -0500 @@ -473,6 +473,155 @@ struct _virNetworkDriver { virDrvNetworkSetAutostart networkSetAutostart; }; + +typedef int + (*virDrvConnectNumOfStoragePools) (virConnectPtr conn); +typedef int + (*virDrvConnectListStoragePools) (virConnectPtr conn, + char **const names, + int maxnames); +typedef int + (*virDrvConnectNumOfDefinedStoragePools) (virConnectPtr conn); +typedef int + (*virDrvConnectListDefinedStoragePools) (virConnectPtr conn, + char **const names, + int maxnames); +typedef int + (*virDrvConnectDiscoverStoragePools) (virConnectPtr conn, + const char *hostname, + const char *type, + unsigned int flags, + char ***xmlDesc); +typedef virStoragePoolPtr + (*virDrvStoragePoolLookupByName) (virConnectPtr conn, + const char *name); +typedef virStoragePoolPtr + (*virDrvStoragePoolLookupByUUID) (virConnectPtr conn, + const unsigned char *uuid); +typedef virStoragePoolPtr + (*virDrvStoragePoolLookupByVolume) (virStorageVolPtr vol); +typedef virStoragePoolPtr + (*virDrvStoragePoolCreateXML) (virConnectPtr conn, + const char *xmlDesc); +typedef virStoragePoolPtr + (*virDrvStoragePoolDefineXML) (virConnectPtr conn, + const char *xmlDesc); +typedef int + (*virDrvStoragePoolUndefine) (virStoragePoolPtr pool); +typedef int + (*virDrvStoragePoolBuild) (virStoragePoolPtr pool, + unsigned int flags); +typedef int + (*virDrvStoragePoolCreate) (virStoragePoolPtr pool); +typedef int + (*virDrvStoragePoolDestroy) (virStoragePoolPtr pool); +typedef int + (*virDrvStoragePoolDelete) (virStoragePoolPtr pool, + unsigned int flags); +typedef int + (*virDrvStoragePoolRefresh) (virStoragePoolPtr pool, + unsigned int flags); +typedef int + (*virDrvStoragePoolGetInfo) (virStoragePoolPtr vol, + virStoragePoolInfoPtr info); +typedef char * + (*virDrvStoragePoolGetXMLDesc) (virStoragePoolPtr pool, + unsigned int flags); +typedef int + (*virDrvStoragePoolGetAutostart) (virStoragePoolPtr pool, + int *autostart); +typedef int + (*virDrvStoragePoolSetAutostart) (virStoragePoolPtr pool, + int autostart); +typedef int + (*virDrvStoragePoolNumOfVolumes) (virStoragePoolPtr pool); +typedef int + (*virDrvStoragePoolListVolumes) (virStoragePoolPtr pool, + char **const names, + int maxnames); + + +typedef virStorageVolPtr + (*virDrvStorageVolLookupByName) (virStoragePoolPtr pool, + const char *name); +typedef virStorageVolPtr + (*virDrvStorageVolLookupByKey) (virConnectPtr pool, + const char *key); +typedef virStorageVolPtr + (*virDrvStorageVolLookupByPath) (virConnectPtr pool, + const char *path); + + +typedef virStorageVolPtr + (*virDrvStorageVolCreateXML) (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags); +typedef int + (*virDrvStorageVolDelete) (virStorageVolPtr vol, + unsigned int flags); + +typedef int + (*virDrvStorageVolGetInfo) (virStorageVolPtr vol, + virStorageVolInfoPtr info); +typedef char * + (*virDrvStorageVolGetXMLDesc) (virStorageVolPtr pool, + unsigned int flags); +typedef char * + (*virDrvStorageVolGetPath) (virStorageVolPtr vol); + + + +typedef struct _virStorageDriver virStorageDriver; +typedef virStorageDriver *virStorageDriverPtr; + +/** + * _virStorageDriver: + * + * Structure associated to a network virtualization driver, defining the various + * entry points for it. + * + * All drivers must support the following fields/methods: + * - open + * - close + */ +struct _virStorageDriver { + const char * name; /* the name of the driver */ + virDrvOpen open; + virDrvClose close; + + virDrvConnectNumOfStoragePools numOfPools; + virDrvConnectListStoragePools listPools; + virDrvConnectNumOfDefinedStoragePools numOfDefinedPools; + virDrvConnectListDefinedStoragePools listDefinedPools; + virDrvConnectDiscoverStoragePools discoverPools; + virDrvStoragePoolLookupByName poolLookupByName; + virDrvStoragePoolLookupByUUID poolLookupByUUID; + virDrvStoragePoolLookupByVolume poolLookupByVolume; + virDrvStoragePoolCreateXML poolCreateXML; + virDrvStoragePoolDefineXML poolDefineXML; + virDrvStoragePoolBuild poolBuild; + virDrvStoragePoolUndefine poolUndefine; + virDrvStoragePoolCreate poolCreate; + virDrvStoragePoolDestroy poolDestroy; + virDrvStoragePoolDelete poolDelete; + virDrvStoragePoolRefresh poolRefresh; + virDrvStoragePoolGetInfo poolGetInfo; + virDrvStoragePoolGetXMLDesc poolGetXMLDesc; + virDrvStoragePoolGetAutostart poolGetAutostart; + virDrvStoragePoolSetAutostart poolSetAutostart; + virDrvStoragePoolNumOfVolumes poolNumOfVolumes; + virDrvStoragePoolListVolumes poolListVolumes; + + virDrvStorageVolLookupByName volLookupByName; + virDrvStorageVolLookupByKey volLookupByKey; + virDrvStorageVolLookupByPath volLookupByPath; + virDrvStorageVolCreateXML volCreateXML; + virDrvStorageVolDelete volDelete; + virDrvStorageVolGetInfo volGetInfo; + virDrvStorageVolGetXMLDesc volGetXMLDesc; + virDrvStorageVolGetPath volGetPath; +}; + typedef int (*virDrvStateInitialize) (void); typedef int (*virDrvStateCleanup) (void); typedef int (*virDrvStateReload) (void); @@ -495,6 +644,7 @@ struct _virStateDriver { */ int virRegisterDriver(virDriverPtr); int virRegisterNetworkDriver(virNetworkDriverPtr); +int virRegisterStorageDriver(virStorageDriverPtr); int virRegisterStateDriver(virStateDriverPtr); #ifdef __cplusplus diff -r 42e6f49e4e69 src/hash.c --- a/src/hash.c Thu Feb 07 12:33:13 2008 -0500 +++ b/src/hash.c Thu Feb 07 16:52:34 2008 -0500 @@ -652,6 +652,34 @@ virNetworkFreeName(virNetworkPtr network } /** + * virStoragePoolFreeName: + * @pool: a pool object + * + * Destroy the pool object, this is just used by the pool hash callback. + * + * Returns 0 in case of success and -1 in case of failure. + */ +static int +virStoragePoolFreeName(virStoragePoolPtr pool, const char *name ATTRIBUTE_UNUSED) +{ + return (virStoragePoolFree(pool)); +} + +/** + * virStorageVolFreeName: + * @vol: a vol object + * + * Destroy the vol object, this is just used by the vol hash callback. + * + * Returns 0 in case of success and -1 in case of failure. + */ +static int +virStorageVolFreeName(virStorageVolPtr vol, const char *name ATTRIBUTE_UNUSED) +{ + return (virStorageVolFree(vol)); +} + +/** * virGetConnect: * * Allocates a new hypervisor connection structure @@ -678,6 +706,12 @@ virGetConnect(void) { ret->networks = virHashCreate(20); if (ret->networks == NULL) goto failed; + ret->storagePools = virHashCreate(20); + if (ret->storagePools == NULL) + goto failed; + ret->storageVols = virHashCreate(20); + if (ret->storageVols == NULL) + goto failed; pthread_mutex_init(&ret->lock, NULL); @@ -690,6 +724,10 @@ failed: virHashFree(ret->domains, (virHashDeallocator) virDomainFreeName); if (ret->networks != NULL) virHashFree(ret->networks, (virHashDeallocator) virNetworkFreeName); + if (ret->storagePools != NULL) + virHashFree(ret->storagePools, (virHashDeallocator) virStoragePoolFreeName); + if (ret->storageVols != NULL) + virHashFree(ret->storageVols, (virHashDeallocator) virStorageVolFreeName); pthread_mutex_destroy(&ret->lock); free(ret); @@ -713,6 +751,11 @@ virReleaseConnect(virConnectPtr conn) { virHashFree(conn->domains, (virHashDeallocator) virDomainFreeName); if (conn->networks != NULL) virHashFree(conn->networks, (virHashDeallocator) virNetworkFreeName); + if (conn->storagePools != NULL) + virHashFree(conn->storagePools, (virHashDeallocator) virStoragePoolFreeName); + if (conn->storageVols != NULL) + virHashFree(conn->storageVols, (virHashDeallocator) virStorageVolFreeName); + virResetError(&conn->err); free(conn->name); @@ -1017,6 +1060,280 @@ virUnrefNetwork(virNetworkPtr network) { return (refs); } + +/** + * virGetStoragePool: + * @conn: the hypervisor connection + * @name: pointer to the storage pool name + * @uuid: pointer to the uuid + * + * Lookup if the storage pool 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 + * virFreeStoragePool() is needed to not leak data. + * + * Returns a pointer to the network, or NULL in case of failure + */ +virStoragePoolPtr +__virGetStoragePool(virConnectPtr conn, const char *name, const unsigned char *uuid) { + virStoragePoolPtr ret = NULL; + + if ((!VIR_IS_CONNECT(conn)) || (name == NULL) || (uuid == NULL)) { + virHashError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(NULL); + } + pthread_mutex_lock(&conn->lock); + + /* TODO search by UUID first as they are better differenciators */ + + ret = (virStoragePoolPtr) virHashLookup(conn->storagePools, name); + /* TODO check the UUID */ + if (ret == NULL) { + ret = (virStoragePoolPtr) calloc(1, sizeof(*ret)); + if (ret == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage pool")); + goto error; + } + ret->name = strdup(name); + if (ret->name == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage pool")); + goto error; + } + ret->magic = VIR_STORAGE_POOL_MAGIC; + ret->conn = conn; + if (uuid != NULL) + memcpy(&(ret->uuid[0]), uuid, VIR_UUID_BUFLEN); + + if (virHashAddEntry(conn->storagePools, name, ret) < 0) { + virHashError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to add storage pool to connection hash table")); + goto error; + } + conn->refs++; + } + ret->refs++; + pthread_mutex_unlock(&conn->lock); + return(ret); + +error: + pthread_mutex_unlock(&conn->lock); + if (ret != NULL) { + free(ret->name); + free(ret); + } + return(NULL); +} + + +/** + * virReleaseStoragePool: + * @pool: the pool to release + * + * Unconditionally release all memory associated with a pool. + * The conn.lock mutex must be held prior to calling this, and will + * be released prior to this returning. The pool 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 +virReleaseStoragePool(virStoragePoolPtr pool) { + virConnectPtr conn = pool->conn; + DEBUG("release pool %p %s", pool, pool->name); + + /* TODO search by UUID first as they are better differenciators */ + if (virHashRemoveEntry(conn->storagePools, pool->name, NULL) < 0) + virHashError(conn, VIR_ERR_INTERNAL_ERROR, + _("pool missing from connection hash table")); + + pool->magic = -1; + free(pool->name); + free(pool); + + DEBUG("unref connection %p %s %d", conn, conn->name, conn->refs); + conn->refs--; + if (conn->refs == 0) { + virReleaseConnect(conn); + /* Already unlocked mutex */ + return; + } + + pthread_mutex_unlock(&conn->lock); +} + + +/** + * virUnrefStoragePool: + * @pool: the pool to unreference + * + * Unreference the pool. If the use count drops to zero, the structure is + * actually freed. + * + * Returns the reference count or -1 in case of failure. + */ +int +virUnrefStoragePool(virStoragePoolPtr pool) { + int refs; + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virHashError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + pthread_mutex_lock(&pool->conn->lock); + DEBUG("unref pool %p %s %d", pool, pool->name, pool->refs); + pool->refs--; + refs = pool->refs; + if (refs == 0) { + virReleaseStoragePool(pool); + /* Already unlocked mutex */ + return (0); + } + + pthread_mutex_unlock(&pool->conn->lock); + return (refs); +} + + +/** + * virGetStorageVol: + * @conn: the hypervisor connection + * @pool: pool owning the volume + * @name: pointer to the storage vol name + * @uuid: pointer to the uuid + * + * Lookup if the storage vol 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 + * virFreeStorageVol() is needed to not leak data. + * + * Returns a pointer to the storage vol, or NULL in case of failure + */ +virStorageVolPtr +__virGetStorageVol(virConnectPtr conn, const char *pool, const char *name, const char *key) { + virStorageVolPtr ret = NULL; + + if ((!VIR_IS_CONNECT(conn)) || (name == NULL) || (key == NULL)) { + virHashError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(NULL); + } + pthread_mutex_lock(&conn->lock); + + ret = (virStorageVolPtr) virHashLookup(conn->storageVols, key); + if (ret == NULL) { + ret = (virStorageVolPtr) calloc(1, sizeof(*ret)); + if (ret == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage vol")); + goto error; + } + ret->pool = strdup(pool); + if (ret->pool == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage vol")); + goto error; + } + ret->name = strdup(name); + if (ret->name == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage vol")); + goto error; + } + strncpy(ret->key, key, sizeof(ret->key)-1); + ret->key[sizeof(ret->key)-1] = '\0'; + ret->magic = VIR_STORAGE_VOL_MAGIC; + ret->conn = conn; + + if (virHashAddEntry(conn->storageVols, key, ret) < 0) { + virHashError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to add storage vol to connection hash table")); + goto error; + } + conn->refs++; + } + ret->refs++; + pthread_mutex_unlock(&conn->lock); + return(ret); + +error: + pthread_mutex_unlock(&conn->lock); + if (ret != NULL) { + free(ret->name); + free(ret->pool); + free(ret); + } + return(NULL); +} + + +/** + * virReleaseStorageVol: + * @vol: the vol to release + * + * Unconditionally release all memory associated with a vol. + * The conn.lock mutex must be held prior to calling this, and will + * be released prior to this returning. The vol 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 +virReleaseStorageVol(virStorageVolPtr vol) { + virConnectPtr conn = vol->conn; + DEBUG("release vol %p %s", vol, vol->name); + + /* TODO search by UUID first as they are better differenciators */ + if (virHashRemoveEntry(conn->storageVols, vol->key, NULL) < 0) + virHashError(conn, VIR_ERR_INTERNAL_ERROR, + _("vol missing from connection hash table")); + + vol->magic = -1; + free(vol->name); + free(vol->pool); + free(vol); + + DEBUG("unref connection %p %s %d", conn, conn->name, conn->refs); + conn->refs--; + if (conn->refs == 0) { + virReleaseConnect(conn); + /* Already unlocked mutex */ + return; + } + + pthread_mutex_unlock(&conn->lock); +} + + +/** + * virUnrefStorageVol: + * @vol: the vol to unreference + * + * Unreference the vol. If the use count drops to zero, the structure is + * actually freed. + * + * Returns the reference count or -1 in case of failure. + */ +int +virUnrefStorageVol(virStorageVolPtr vol) { + int refs; + + if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { + virHashError(vol->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + pthread_mutex_lock(&vol->conn->lock); + DEBUG("unref vol %p %s %d", vol, vol->name, vol->refs); + vol->refs--; + refs = vol->refs; + if (refs == 0) { + virReleaseStorageVol(vol); + /* Already unlocked mutex */ + return (0); + } + + pthread_mutex_unlock(&vol->conn->lock); + return (refs); +} + + /* * vim: set tabstop=4: * vim: set shiftwidth=4: diff -r 42e6f49e4e69 src/internal.h --- a/src/internal.h Thu Feb 07 12:33:13 2008 -0500 +++ b/src/internal.h Thu Feb 07 16:52:34 2008 -0500 @@ -130,7 +130,7 @@ extern int debugFlag; * VIR_DOMAIN_MAGIC: * * magic value used to protect the API when pointers to domain structures - * are passed down by the uers. + * are passed down by the users. */ #define VIR_DOMAIN_MAGIC 0xDEAD4321 #define VIR_IS_DOMAIN(obj) ((obj) && (obj)->magic==VIR_DOMAIN_MAGIC) @@ -140,11 +140,31 @@ extern int debugFlag; * VIR_NETWORK_MAGIC: * * magic value used to protect the API when pointers to network structures - * are passed down by the uers. + * are passed down by the users. */ #define VIR_NETWORK_MAGIC 0xDEAD1234 #define VIR_IS_NETWORK(obj) ((obj) && (obj)->magic==VIR_NETWORK_MAGIC) #define VIR_IS_CONNECTED_NETWORK(obj) (VIR_IS_NETWORK(obj) && VIR_IS_CONNECT((obj)->conn)) + +/** + * VIR_STORAGE_POOL_MAGIC: + * + * magic value used to protect the API when pointers to storage pool structures + * are passed down by the users. + */ +#define VIR_STORAGE_POOL_MAGIC 0xDEAD5678 +#define VIR_IS_STORAGE_POOL(obj) ((obj) && (obj)->magic==VIR_STORAGE_POOL_MAGIC) +#define VIR_IS_CONNECTED_STORAGE_POOL(obj) (VIR_IS_STORAGE_POOL(obj) && VIR_IS_CONNECT((obj)->conn)) + +/** + * VIR_STORAGE_VOL_MAGIC: + * + * magic value used to protect the API when pointers to storage vol structures + * are passed down by the users. + */ +#define VIR_STORAGE_VOL_MAGIC 0xDEAD8765 +#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)) /* * arbitrary limitations @@ -165,6 +185,7 @@ struct _virConnect { /* The underlying hypervisor driver and network driver. */ virDriverPtr driver; virNetworkDriverPtr networkDriver; + virStorageDriverPtr storageDriver; /* Private data pointer which can be used by driver and * network driver as they wish. @@ -172,6 +193,7 @@ struct _virConnect { */ void * privateData; void * networkPrivateData; + void * storagePrivateData; /* Per-connection error. */ virError err; /* the last error */ @@ -187,6 +209,8 @@ struct _virConnect { pthread_mutex_t lock; virHashTablePtr domains; /* hash table for known domains */ virHashTablePtr networks; /* hash table for known domains */ + virHashTablePtr storagePools;/* hash table for known storage pools */ + virHashTablePtr storageVols;/* hash table for known storage vols */ virJobPtr jobs; /* list of active background jobs */ int refs; /* reference count */ }; @@ -216,6 +240,34 @@ struct _virNetwork { virConnectPtr conn; /* pointer back to the connection */ char *name; /* the network external name */ unsigned char uuid[VIR_UUID_BUFLEN]; /* the network unique identifier */ +}; + +/** +* _virNetwork: +* +* Internal structure associated to a storage pool +*/ +struct _virStoragePool { + unsigned int magic; /* specific value to check */ + int refs; /* reference count */ + virConnectPtr conn; /* pointer back to the connection */ + char *name; /* the storage pool external name */ + unsigned char uuid[VIR_UUID_BUFLEN]; /* the storage pool unique identifier */ +}; + +/** +* _virNetwork: +* +* Internal structure associated to a storage volume +*/ +struct _virStorageVol { + unsigned int magic; /* specific value to check */ + int refs; /* reference count */ + virConnectPtr conn; /* pointer back to the connection */ + char *pool; /* Pool name of owner */ + char *name; /* the storage vol external name */ + /* XXX currently abusing path for this. Ought not to be so evil */ + char key[PATH_MAX]; /* unique key for storage vol */ }; @@ -265,8 +317,20 @@ virNetworkPtr __virGetNetwork (virConne const unsigned char *uuid); int virUnrefNetwork (virNetworkPtr network); +virStoragePoolPtr __virGetStoragePool (virConnectPtr conn, + const char *name, + const unsigned char *uuid); +int virUnrefStoragePool (virStoragePoolPtr pool); +virStorageVolPtr __virGetStorageVol (virConnectPtr conn, + const char *pool, + const char *name, + const char *key); +int virUnrefStorageVol (virStorageVolPtr vol); + #define virGetDomain(c,n,u) __virGetDomain((c),(n),(u)) #define virGetNetwork(c,n,u) __virGetNetwork((c),(n),(u)) +#define virGetStoragePool(c,n,u) __virGetStoragePool((c),(n),(u)) +#define virGetStorageVol(c,p,n,u) __virGetStorageVol((c),(p),(n),(u)) int __virStateInitialize(void); int __virStateCleanup(void); diff -r 42e6f49e4e69 src/libvirt.c --- a/src/libvirt.c Thu Feb 07 12:33:13 2008 -0500 +++ b/src/libvirt.c Thu Feb 07 16:52:34 2008 -0500 @@ -52,6 +52,8 @@ static int virDriverTabCount = 0; static int virDriverTabCount = 0; static virNetworkDriverPtr virNetworkDriverTab[MAX_DRIVERS]; static int virNetworkDriverTabCount = 0; +static virStorageDriverPtr virStorageDriverTab[MAX_DRIVERS]; +static int virStorageDriverTabCount = 0; static virStateDriverPtr virStateDriverTab[MAX_DRIVERS]; static int virStateDriverTabCount = 0; static int initialized = 0; @@ -318,6 +320,58 @@ virLibNetworkError(virNetworkPtr network } /** + * virLibStoragePoolError: + * @conn: the connection if available + * @error: the error noumber + * @info: extra information string + * + * Handle an error at the connection level + */ +static void +virLibStoragePoolError(virStoragePoolPtr pool, 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_STORAGE_POOL) + conn = pool->conn; + + __virRaiseError(conn, NULL, NULL, VIR_FROM_STORAGE, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +/** + * virLibStorageVolError: + * @conn: the connection if available + * @error: the error noumber + * @info: extra information string + * + * Handle an error at the connection level + */ +static void +virLibStorageVolError(virStorageVolPtr vol, 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_STORAGE_VOL) + conn = vol->conn; + + __virRaiseError(conn, NULL, NULL, VIR_FROM_STORAGE, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +/** * virRegisterNetworkDriver: * @driver: pointer to a network driver block * @@ -343,6 +397,34 @@ virRegisterNetworkDriver(virNetworkDrive virNetworkDriverTab[virNetworkDriverTabCount] = driver; return virNetworkDriverTabCount++; +} + +/** + * virRegisterStorageDriver: + * @driver: pointer to a storage driver block + * + * Register a storage virtualization driver + * + * Returns the driver priority or -1 in case of error. + */ +int +virRegisterStorageDriver(virStorageDriverPtr driver) +{ + if (virInitialize() < 0) + return -1; + + if (driver == NULL) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + if (virStorageDriverTabCount >= MAX_DRIVERS) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + virStorageDriverTab[virStorageDriverTabCount] = driver; + return virStorageDriverTabCount++; } /** @@ -613,6 +695,29 @@ do_open (const char *name, } } + + /* Secondary driver for storage. Optional */ + for (i = 0; i < virStorageDriverTabCount; i++) { + res = virStorageDriverTab[i]->open (ret, uri, auth, flags); +#ifdef ENABLE_DEBUG + DEBUG("storage driver %d %s returned %s", + i, virStorageDriverTab[i]->name, + res == VIR_DRV_OPEN_SUCCESS ? "SUCCESS" : + (res == VIR_DRV_OPEN_DECLINED ? "DECLINED" : + (res == VIR_DRV_OPEN_ERROR ? "ERROR" : "unknown status"))); +#endif + if (res == VIR_DRV_OPEN_ERROR) { + if (0 && STREQ(virStorageDriverTab[i]->name, "remote")) { + virLibConnWarning (NULL, VIR_WAR_NO_STORAGE, + "Is the daemon running ?"); + } + break; + } else if (res == VIR_DRV_OPEN_SUCCESS) { + ret->storageDriver = virStorageDriverTab[i]; + break; + } + } + /* Cleansing flags */ ret->flags = flags & VIR_CONNECT_RO; @@ -708,6 +813,8 @@ virConnectClose(virConnectPtr conn) if (conn->networkDriver) conn->networkDriver->close (conn); + if (conn->storageDriver) + conn->storageDriver->close (conn); conn->driver->close (conn); if (virUnrefConnect(conn) < 0) @@ -4052,6 +4159,1302 @@ virNetworkSetAutostart(virNetworkPtr net return -1; } + +/** + * virStoragePoolGetConnect: + * @pool: pointer to a poool + * + * Provides the connection pointer associated with a storage poolk. The + * reference counter on the connection is not increased by this + * call. + * + * WARNING: When writing libvirt bindings in other languages, do + * not use this function. Instead, store the connection and + * the pool object together. + * + * Returns the virConnectPtr or NULL in case of failure. + */ +virConnectPtr +virStoragePoolGetConnect (virStoragePoolPtr pool) +{ + DEBUG("pool=%p", pool); + + if (!VIR_IS_STORAGE_POOL (pool)) { + virLibStoragePoolError (NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return NULL; + } + return pool->conn; +} + +/** + * virConnectNumOfStoragePools: + * @conn: pointer to hypervisor connection + * + * Provides the number of active storage pools + * + * Returns the number of pools found, or -1 on error + */ +int +virConnectNumOfStoragePools (virConnectPtr conn) +{ + DEBUG("conn=%p", conn); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->numOfPools) + return conn->storageDriver->numOfPools (conn); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +/** + * virConnectListStoragePools: + * @conn: pointer to hypervisor connection + * @names: array of char * to fill with pool names (allocated by caller) + * @maxnames: size of the names array + * + * Provides the list of names of active storage pools + * upto maxnames. If there are more than maxnames, the + * remaining names will be silently ignored. + * + * Returns 0 on success, -1 on error + */ +int +virConnectListStoragePools (virConnectPtr conn, + char **const names, + int maxnames) +{ + DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if ((names == NULL) || (maxnames < 0)) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->listPools) + return conn->storageDriver->listPools (conn, names, maxnames); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +} + + +/** + * virConnectNumOfDefinedStoragePools: + * @conn: pointer to hypervisor connection + * + * Provides the number of inactive storage pools + * + * Returns the number of pools found, or -1 on error + */ +int +virConnectNumOfDefinedStoragePools(virConnectPtr conn) +{ + DEBUG("conn=%p", conn); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->numOfDefinedPools) + return conn->storageDriver->numOfDefinedPools (conn); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virConnectListDefinedStoragePools: + * @conn: pointer to hypervisor connection + * @names: array of char * to fill with pool names (allocated by caller) + * @maxnames: size of the names array + * + * Provides the list of names of inactive storage pools + * upto maxnames. If there are more than maxnames, the + * remaining names will be silently ignored. + * + * Returns 0 on success, -1 on error + */ +int +virConnectListDefinedStoragePools(virConnectPtr conn, + char **const names, + int maxnames) +{ + DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if ((names == NULL) || (maxnames < 0)) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->listDefinedPools) + return conn->storageDriver->listDefinedPools (conn, names, maxnames); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virConnectDiscoverStoragePools: + * @conn: pointer to hypervisor connection + * @hostname: host to discover pools on + * @type: type of storge pool to discover + * @flags: flags for discovery (unused, pass 0) + * @xmlDesc: return array of of XML documents, one per pool + * + * Talks to a host and attempt to auto-discover a set + * of exported storage pools available. eg For iSCSI this + * would be a set of iSCSI targets. For NFS this would be + * a list of exported paths. + * + * Each discovered pool is returned as an XML document + * suitable for feeding into virStoragePoolCreateXML + * + * returns number of discovered pools, or -1 on error + */ +int +virConnectDiscoverStoragePools(virConnectPtr conn, + const char *hostname, + const char *type, + unsigned int flags, + char ***xmlDesc) +{ + DEBUG("conn=%p, host=%s, type=%s, flags=%d, xml=%p", conn, hostname, type, flags, xmlDesc); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + if (hostname == NULL || type == NULL || xmlDesc == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->discoverPools) + return conn->storageDriver->discoverPools (conn, hostname, type, flags, xmlDesc); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolLookupByName: + * @conn: pointer to hypervisor connection + * @name: name of pool to fetch + * + * Fetch a storage pool based on its unique name + * + * Returns a virStoragePoolPtr object, or NULL if no matching pool is found + */ +virStoragePoolPtr +virStoragePoolLookupByName(virConnectPtr conn, + const char *name) +{ + DEBUG("conn=%p, name=%s", 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->storageDriver && conn->storageDriver->poolLookupByName) + return conn->storageDriver->poolLookupByName (conn, name); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + +/** + * virStoragePoolLookupByUUID: + * @conn: pointer to hypervisor connection + * @uuid: globally unique id of pool to fetch + * + * Fetch a storage pool based on its globally unique id + * + * Returns a virStoragePoolPtr object, or NULL if no matching pool is found + */ +virStoragePoolPtr +virStoragePoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + DEBUG("conn=%p, uuid=%s", conn, uuid); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (uuid == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (conn->storageDriver && conn->storageDriver->poolLookupByUUID) + return conn->storageDriver->poolLookupByUUID (conn, uuid); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + +} + + +/** + * virStoragePoolLookupByUUIDString: + * @conn: pointer to hypervisor connection + * @uuidstr: globally unique id of pool to fetch + * + * Fetch a storage pool based on its globally unique id + * + * Returns a virStoragePoolPtr object, or NULL if no matching pool is found + */ +virStoragePoolPtr +virStoragePoolLookupByUUIDString(virConnectPtr conn, + const char *uuidstr) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + DEBUG("conn=%p, uuidstr=%s", conn, uuidstr); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (uuidstr == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (virUUIDParse(uuidstr, uuid) < 0) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + return virStoragePoolLookupByUUID(conn, uuid); +} + + +/** + * virStoragePoolLookupByVolume: + * @vol: pointer to storage volume + * + * Fetch a storage pool which contains a particular volume + * + * Returns a virStoragePoolPtr object, or NULL if no matching pool is found + */ +virStoragePoolPtr +virStoragePoolLookupByVolume(virStorageVolPtr vol) +{ + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + + if (vol->conn->storageDriver && vol->conn->storageDriver->poolLookupByVolume) + return vol->conn->storageDriver->poolLookupByVolume (vol); + + virLibConnError (vol->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + +} + +/** + * virStoragePoolCreateXML: + * @conn: pointer to hypervisor connection + * @xmlDesc: XML description for new pool + * + * Create a new storage based on its XML description. The + * pool is not persitent, so its definition will disappear + * when it is destroyed, or if the host is restarted + * + * Returns a virStoragePoolPtr object, or NULL if creation failed + */ +virStoragePoolPtr +virStoragePoolCreateXML(virConnectPtr conn, + const char *xmlDesc) +{ + DEBUG("conn=%p, xmlDesc=%s", conn, xmlDesc); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (xmlDesc == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (NULL); + } + + if (conn->storageDriver && conn->storageDriver->poolCreateXML) + return conn->storageDriver->poolCreateXML (conn, xmlDesc); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + +/** + * virStoragePoolDefineXML: + * @conn: pointer to hypervisor connection + * @xml: XML description for new pool + * + * Define a new inactive storage pool based on its XML description. The + * pool is persitent, until explicitly undefined. + * + * Returns a virStoragePoolPtr object, or NULL if creation failed + */ +virStoragePoolPtr +virStoragePoolDefineXML(virConnectPtr conn, + const char *xml) +{ + DEBUG("conn=%p, xml=%s", conn, xml); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (NULL); + } + if (xml == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (conn->storageDriver && conn->storageDriver->poolDefineXML) + return conn->storageDriver->poolDefineXML (conn, xml); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + +} + +/** + * virStoragePoolBuild: + * @pool: pointer to storage pool + * + * Build the underlying storage pool + * + * Returns 0 on success, or -1 upon failure + */ +int +virStoragePoolBuild(virStoragePoolPtr pool, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("pool=%p, flags=%u", pool, flags); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (-1); + } + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolBuild) + return conn->storageDriver->poolBuild (pool, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +} + + +/** + * virStoragePoolUndefine: + * @pool: pointer to storage pool + * + * Undefine an inactive storage pool + * + * Returns a virStoragePoolPtr object, or NULL if creation failed + */ +int +virStoragePoolUndefine(virStoragePoolPtr pool) +{ + virConnectPtr conn; + DEBUG("pool=%p", pool); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (-1); + } + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolUndefine) + return conn->storageDriver->poolUndefine (pool); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +} + + +/** + * virStoragePoolCreate: + * @pool: pointer to storage pool + * + * Starts an inactive storage pool + * + * Returns 0 on success, or -1 if it could not be started + */ +int +virStoragePoolCreate(virStoragePoolPtr pool) +{ + virConnectPtr conn; + DEBUG("pool=%p", pool); + + if (pool == NULL) { + TODO; + return (-1); + } + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolCreate) + return conn->storageDriver->poolCreate (pool); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +} + + +/** + * virStoragePoolDestroy: + * @pool: pointer to storage pool + * + * Destroy an active storage pool. The virStoragePoolPtr + * object should not be used after this method returns + * successfully as it has been free'd + * + * Returns 0 on success, or -1 if it could not be destroyed + */ +int +virStoragePoolDestroy(virStoragePoolPtr pool) +{ + virConnectPtr conn; + DEBUG("pool=%p", pool); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolDestroy) + return conn->storageDriver->poolDestroy (pool); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +/** + * virStoragePoolDelete: + * @pool: pointer to storage pool + * @flags: flags for obliteration process + * + * Delete the underlying pool resources. This is + * a non-recoverable operation. + * + * Returns 0 on success, or -1 if it could not be obliterate + */ +int +virStoragePoolDelete(virStoragePoolPtr pool, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("pool=%p, flags=%u", pool, flags); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolDelete) + return conn->storageDriver->poolDelete (pool, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolFree: + * @pool: pointer to storage pool + * + * Free a storage pool object + * + * Returns 0 on success, or -1 if it could not be free'd. + */ +int +virStoragePoolFree(virStoragePoolPtr pool) +{ + DEBUG("pool=%p", pool); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + if (virUnrefStoragePool(pool) < 0) + return (-1); + return(0); + +} + + +/** + * virStoragePoolRefresh: + * @pool: pointer to storage pool + * @flags: flags to control refresh behaviour (currently unused, use 0) + * + * Request that the pool refresh its list of volumes. This may + * involve communicating with a remote server, and/or initializing + * new devices at the OS layer + * + * Return 0 if the volume list was refreshed, -1 on failure + */ +int +virStoragePoolRefresh(virStoragePoolPtr pool, unsigned int flags) +{ + virConnectPtr conn; + DEBUG("pool=%p flags=%u", pool, flags); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolRefresh) + return conn->storageDriver->poolRefresh (pool, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolGetName: + * @pool: pointer to storage pool + * + * Fetch the locally unique name of the storage pool + * + * Return the name of the pool, or NULL on error + */ +const char* +virStoragePoolGetName(virStoragePoolPtr pool) +{ + DEBUG("pool=%p", pool); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (NULL); + } + return (pool->name); + +} + + +/** + * virStoragePoolGetUUID: + * @pool: pointer to storage pool + * @uuid: buffer of VIR_UUID_BUFLEN bytes in size + * + * Fetch the globally unique ID of the storage pool + * + * Return 0 on success, or -1 on error; + */ +int +virStoragePoolGetUUID(virStoragePoolPtr pool, + unsigned char *uuid) +{ + DEBUG("pool=%p, uuid=%p", pool, uuid); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + if (uuid == NULL) { + virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + memcpy(uuid, &pool->uuid[0], VIR_UUID_BUFLEN); + + return (0); + +} + +/** + * virStoragePoolGetUUIDString: + * @pool: pointer to storage pool + * @buf: buffer of VIR_UUID_STRING_BUFLEN bytes in size + * + * Fetch the globally unique ID of the storage pool as a string + * + * Return 0 on success, or -1 on error; + */ +int +virStoragePoolGetUUIDString(virStoragePoolPtr pool, + char *buf) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + DEBUG("pool=%p, buf=%p", pool, buf); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + if (buf == NULL) { + virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (virStoragePoolGetUUID(pool, &uuid[0])) + return (-1); + + virUUIDFormat(uuid, buf); + return (0); + +} + + +/** + * virStoragePoolGetInfo: + * @pool: pointer to storage pool + * @info: pointer at which to store info + * + * Get volatile information about the storage pool + * such as free space / usage summary + * + * returns 0 on success, or -1 on failure. + */ +int +virStoragePoolGetInfo(virStoragePoolPtr pool, + virStoragePoolInfoPtr info) +{ + virConnectPtr conn; + DEBUG("pool=%p, info=%p", pool, info); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + if (info == NULL) { + virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + memset(info, 0, sizeof(virStoragePoolInfo)); + + conn = pool->conn; + + if (conn->storageDriver->poolGetInfo) + return conn->storageDriver->poolGetInfo (pool, info); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +} + + +/** + * virStoragePoolGetXMLDesc: + * @pool: pointer to storage pool + * @flags: flags for XML format options (unused, pass 0) + * + * Fetch an XML document describing all aspects of the + * storage pool. This is suitable for later feeding back + * into the virStoragePoolCreateXML method. + * + * returns a XML document, or NULL on error + */ +char * +virStoragePoolGetXMLDesc(virStoragePoolPtr pool, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("pool=%p, flags=%u", pool, flags); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (NULL); + } + if (flags != 0) { + virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + conn = pool->conn; + + if (conn->storageDriver && conn->storageDriver->poolGetXMLDesc) + return conn->storageDriver->poolGetXMLDesc (pool, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + +} + + +/** + * virStoragePoolGetAutostart: + * @pool: pointer to storage pool + * @autostart: location in which to store autostart flag + * + * Fetches the value of the autostart flag, which determines + * whether the pool is automatically started at boot time + * + * return 0 on success, -1 on failure + */ +int +virStoragePoolGetAutostart(virStoragePoolPtr pool, + int *autostart) +{ + virConnectPtr conn; + DEBUG("pool=%p, autostart=%p", pool, autostart); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + if (!autostart) { + virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + conn = pool->conn; + + if (conn->storageDriver && conn->storageDriver->poolGetAutostart) + return conn->storageDriver->poolGetAutostart (pool, autostart); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolSetAutostart: + * @pool: pointer to storage pool + * @autostart: new flag setting + * + * Sets the autostart flag + * + * returns 0 on success, -1 on failure + */ +int +virStoragePoolSetAutostart(virStoragePoolPtr pool, + int autostart) +{ + virConnectPtr conn; + DEBUG("pool=%p, autostart=%d", pool, autostart); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + conn = pool->conn; + + if (conn->storageDriver && conn->storageDriver->poolSetAutostart) + return conn->storageDriver->poolSetAutostart (pool, autostart); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolNumOfVolumes: + * @pool: pointer to storage pool + * + * Fetch the number of storage volumes within a pool + * + * Returns the number of storage pools, or -1 on failure + */ +int +virStoragePoolNumOfVolumes(virStoragePoolPtr pool) +{ + DEBUG("pool=%p", pool); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->poolNumOfVolumes) + return pool->conn->storageDriver->poolNumOfVolumes (pool); + + virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolListVolumes: + * @pool: pointer to storage pool + * @names: array in which to storage volume names + * @maxnames: size of names array + * + * Fetch list of storage volume names, limiting to + * at most maxnames. + * + * Returns the number of names fetched, or -1 on error + */ +int +virStoragePoolListVolumes(virStoragePoolPtr pool, + char **const names, + int maxnames) +{ + DEBUG("pool=%p, names=%p, maxnames=%d", pool, names, maxnames); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + if ((names == NULL) || (maxnames < 0)) { + virLibConnError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->poolListVolumes) + return pool->conn->storageDriver->poolListVolumes (pool, names, maxnames); + + virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStorageVolGetConnect: + * @vol: pointer to a poool + * + * Provides the connection pointer associated with a storage poolk. The + * reference counter on the connection is not increased by this + * call. + * + * WARNING: When writing libvirt bindings in other languages, do + * not use this function. Instead, store the connection and + * the pool object together. + * + * Returns the virConnectPtr or NULL in case of failure. + */ +virConnectPtr +virStorageVolGetConnect (virStorageVolPtr vol) +{ + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL (vol)) { + virLibStoragePoolError (NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return NULL; + } + return vol->conn; +} + + +/** + * virStorageVolLookupByName: + * @pool: pointer to storage pool + * @name: name of storage volume + * + * Fetch a pointer to a storage volume based on its name + * within a pool + * + * return a storage volume, or NULL if not found / error + */ +virStorageVolPtr +virStorageVolLookupByName(virStoragePoolPtr pool, + const char *name) +{ + DEBUG("pool=%p, name=%s", pool, name); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (name == NULL) { + virLibConnError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->volLookupByName) + return pool->conn->storageDriver->volLookupByName (pool, name); + + virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + + +/** + * virStorageVolLookupByKey: + * @conn: pointer to hypervisor connection + * @key: globally unique key + * + * Fetch a pointer to a storage volume based on its + * globally unique key + * + * return a storage volume, or NULL if not found / error + */ +virStorageVolPtr +virStorageVolLookupByKey(virConnectPtr conn, + const char *key) +{ + DEBUG("conn=%p, key=%s", conn, key); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (key == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (conn->storageDriver && conn->storageDriver->volLookupByKey) + return conn->storageDriver->volLookupByKey (conn, key); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + +/** + * virStorageVolLookupByPath: + * @conn: pointer to hypervisor connection + * @path: locally unique path + * + * Fetch a pointer to a storage volume based on its + * locally (host) unique path + * + * return a storage volume, or NULL if not found / error + */ +virStorageVolPtr +virStorageVolLookupByPath(virConnectPtr conn, + const char *path) +{ + DEBUG("conn=%p, path=%s", conn, path); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (path == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (conn->storageDriver && conn->storageDriver->volLookupByPath) + return conn->storageDriver->volLookupByPath (conn, path); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + +/** + * virStorageVolGetName: + * @vol: pointer to storage volume + * + * Fetch the storage volume name. This is unique + * within the scope of a pool + * + * return the volume name, or NULL on error + */ +const char* +virStorageVolGetName(virStorageVolPtr vol) +{ + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + return (vol->name); +} + + +/** + * virStorageVolGetKey: + * @vol: pointer to storage volume + * + * Fetch the storage volume key. This is globally + * unique, so the same volume will hve the same + * key no matter what host it is accessed from + * + * return the volume key, or NULL on error + */ +const char* +virStorageVolGetKey(virStorageVolPtr vol) +{ + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + return (vol->key); +} + + +/** + * virStorageVolCreateXML: + * @pool: pointer to storage pool + * @xmldesc: description of volume to create + * @flags: flags for creation (unused, pass 0) + * + * Create a storage volume within a pool based + * on an XML description. Not all pools support + * creation of volumes + * + * return the storage volume, or NULL on error + */ +virStorageVolPtr +virStorageVolCreateXML(virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags) +{ + DEBUG("pool=%p, flags=%u", pool, flags); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + + if (pool->conn->flags & VIR_CONNECT_RO) { + virLibConnError(pool->conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (NULL); + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->volCreateXML) + return pool->conn->storageDriver->volCreateXML (pool, xmldesc, flags); + + virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + +/** + * virStorageVolDelete: + * @vol: pointer to storage volume + * + * Delete the storage volume from the pool + * + * Return 0 on success, or -1 on error + */ +int +virStorageVolDelete(virStorageVolPtr vol, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("vol=%p, flags=%u", vol, flags); + + if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (-1); + } + + conn = vol->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStorageVolError(vol, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->volDelete) + return conn->storageDriver->volDelete (vol, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStorageVolFree: + * @vol: pointer to storage volume + * + * Release the storage volume handle. The underlying + * storage volume contains to exist + * + * Return 0 on success, or -1 on error + */ +int +virStorageVolFree(virStorageVolPtr vol) +{ + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (-1); + } + if (virUnrefStorageVol(vol) < 0) + return (-1); + return(0); +} + + +/** + * virStorageVolGetInfo: + * @vol: pointer to storage volume + * @info: pointer at which to store info + * + * Fetches volatile information about the storage + * volume such as its current allocation + * + * Return 0 on success, or -1 on failure + */ +int +virStorageVolGetInfo(virStorageVolPtr vol, + virStorageVolInfoPtr info) +{ + virConnectPtr conn; + DEBUG("vol=%p, info=%p", vol, info); + + if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (-1); + } + if (info == NULL) { + virLibStorageVolError(vol, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + memset(info, 0, sizeof(virStorageVolInfo)); + + conn = vol->conn; + + if (conn->storageDriver->volGetInfo) + return conn->storageDriver->volGetInfo (vol, info); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStorageVolGetXMLDesc: + * @vol: pointer to storage volume + * @flags: flags for XML generation (unused, pass 0) + * + * Fetch an XML document describing all aspects of + * the storage volume + * + * Return the XML document, or NULL on error + */ +char * +virStorageVolGetXMLDesc(virStorageVolPtr vol, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("vol=%p, flags=%u", vol, flags); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + if (flags != 0) { + virLibStorageVolError(vol, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + conn = vol->conn; + + if (conn->storageDriver && conn->storageDriver->volGetXMLDesc) + return conn->storageDriver->volGetXMLDesc (vol, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + +} + + +/** + * virStorageVolGetPath: + * @vol: pointer to storage volume + * + * Fetch the storage volume path. Depending on the pool + * configuration this is either persistent across hosts, + * or dynamically assigned at pool startup. Consult + * pool documentation for information on getting the + * persistent naming + * + * Returns the storage volume path, or NULL on error + */ +char * +virStorageVolGetPath(virStorageVolPtr vol) +{ + virConnectPtr conn; + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + + conn = vol->conn; + + if (conn->storageDriver && conn->storageDriver->volGetPath) + return conn->storageDriver->volGetPath (vol); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + /* * vim: set tabstop=4: * vim: set shiftwidth=4: diff -r 42e6f49e4e69 src/virterror.c --- a/src/virterror.c Thu Feb 07 12:33:13 2008 -0500 +++ b/src/virterror.c Thu Feb 07 16:52:34 2008 -0500 @@ -258,6 +258,9 @@ virDefaultErrorFunc(virErrorPtr err) break; case VIR_FROM_STATS_LINUX: dom = "Linux Stats "; + break; + case VIR_FROM_STORAGE: + dom = "Storage "; break; } @@ -665,6 +668,24 @@ __virErrorMsg(virErrorNumber error, cons else errmsg = _("authentication failed: %s"); break; + case VIR_ERR_INVALID_STORAGE_POOL: + if (info == NULL) + errmsg = _("invalid storage pool pointer in"); + else + errmsg = _("invalid storage pool pointer in %s"); + break; + case VIR_ERR_INVALID_STORAGE_VOL: + if (info == NULL) + errmsg = _("invalid storage volume pointer in"); + else + errmsg = _("invalid storage volume pointer in %s"); + break; + case VIR_WAR_NO_STORAGE: + if (info == NULL) + errmsg = _("Failed to find a storage driver"); + else + errmsg = _("Failed to find a storage driver: %s"); + break; } return (errmsg); } -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

DB> +/** DB> +* _virNetwork: DB> +* DB> +* Internal structure associated to a storage pool DB> +*/ This looks like a copy-and-paste casualty. DB> +struct _virStoragePool { DB> + unsigned int magic; /* specific value to check */ DB> + int refs; /* reference count */ DB> + virConnectPtr conn; /* pointer back to the connection */ DB> + char *name; /* the storage pool external name */ DB> + unsigned char uuid[VIR_UUID_BUFLEN]; /* the storage pool unique identifier */ DB> +}; DB> + DB> +/** DB> +* _virNetwork: DB> +* DB> +* Internal structure associated to a storage volume DB> +*/ ...as does this. DB> + DB> +/** DB> + * virStoragePoolGetConnect: DB> + * @pool: pointer to a poool DB> + * DB> + * Provides the connection pointer associated with a storage poolk. The ^^^^^ Typo. Don't you just hate completely superficial responses such as this one? :) -- Dan Smith IBM Linux Technology Center Open Hypervisor Team email: danms@us.ibm.com

On Tue, Feb 12, 2008 at 10:41:54AM -0800, Dan Smith wrote:
DB> +/** DB> +* _virNetwork: DB> +* DB> +* Internal structure associated to a storage pool DB> +*/
This looks like a copy-and-paste casualty.
DB> +struct _virStoragePool { DB> + unsigned int magic; /* specific value to check */ DB> + int refs; /* reference count */ DB> + virConnectPtr conn; /* pointer back to the connection */ DB> + char *name; /* the storage pool external name */ DB> + unsigned char uuid[VIR_UUID_BUFLEN]; /* the storage pool unique identifier */ DB> +}; DB> + DB> +/** DB> +* _virNetwork: DB> +* DB> +* Internal structure associated to a storage volume DB> +*/
...as does this.
DB> + DB> +/** DB> + * virStoragePoolGetConnect: DB> + * @pool: pointer to a poool DB> + * DB> + * Provides the connection pointer associated with a storage poolk. The ^^^^^ Typo.
Thanks I'll fix all these.
Don't you just hate completely superficial responses such as this one? :)
Right, you're going on my list ;-P Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This defines the internal driver API for the storage APIs. The pattern follows that used for the existing APIs. NB, both the storage pool and storage volume objects are now top level objects. Previous iterations of this code have the volume as a child of the pool. This unneccessarily complicated the reference counting and forced you to always have a pool available first.
include/libvirt/virterror.h | 4 src/driver.h | 150 ++++ src/hash.c | 317 +++++++++ src/internal.h | 68 ++ src/libvirt.c | 1403 ++++++++++++++++++++++++++++++++++++++++++++ src/virterror.c | 21 6 files changed, 1961 insertions(+), 5 deletions(-)
...
diff -r 42e6f49e4e69 src/internal.h --- a/src/internal.h Thu Feb 07 12:33:13 2008 -0500 +++ b/src/internal.h Thu Feb 07 16:52:34 2008 -0500 ... @@ -187,6 +209,8 @@ struct _virConnect { pthread_mutex_t lock; virHashTablePtr domains; /* hash table for known domains */ virHashTablePtr networks; /* hash table for known domains */ + virHashTablePtr storagePools;/* hash table for known storage pools */ + virHashTablePtr storageVols;/* hash table for known storage vols */ virJobPtr jobs; /* list of active background jobs */ int refs; /* reference count */ };
Hi Dan, I tried to apply all of these patches, and ended up with a bunch of failed hunks. For example, the one above fails due to the fact that there is no "jobs" member in checked-in sources, yet the patch requires that it be there in the existing context. I inserted the two new members manually. Here's the list: 8 out of 15 hunks FAILED -- saving rejects to file src/virsh.c.rej 5 out of 15 hunks FAILED -- saving rejects to file python/generator.py.rej 29 out of 183 hunks FAILED -- saving rejects to file docs/libvirt-refs.xml.rej 7 out of 21 hunks FAILED -- saving rejects to file docs/libvirt-api.xml.rej 1 out of 7 hunks FAILED -- saving rejects to file src/internal.h.rej I'm not too worried about the .xml or even .py conflicts, for now. The failed hunks in virsh.c all involve VIR_JOB_CANCELLED, so I suspect that the failures are all ignorable. Bottom line: looked problematic, but no big deal

On Thu, Feb 14, 2008 at 12:15:26PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
I tried to apply all of these patches, and ended up with a bunch of failed hunks. For example, the one above fails due to the fact that there is no "jobs" member in checked-in sources, yet the patch requires that it be there in the existing context.
Urgh, yes. I forgot to mention that these were diffed wrt to the jobs patches. For convenience just apply the patches from the patch queue http://hg.berrange.com/libraries/libvirt--storage Where the 'series' file gives the canonical order.
I'm not too worried about the .xml or even .py conflicts, for now. The failed hunks in virsh.c all involve VIR_JOB_CANCELLED, so I suspect that the failures are all ignorable.
Bottom line: looked problematic, but no big deal
I'd like to add the storage API before the job stuff, because there's a couple of tweaks to the job stuff needed to make it work more easily with the storage APIs. So I'll re-diff the patches so the storage APIs apply first. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:30:45AM +0000, Daniel P. Berrange wrote:
This defines the internal driver API for the storage APIs. The pattern follows that used for the existing APIs. NB, both the storage pool and storage volume objects are now top level objects. Previous iterations of this code have the volume as a child of the pool. This unneccessarily complicated the reference counting and forced you to always have a pool available first. [...] + unsigned int flags); +typedef int + (*virDrvStoragePoolCreate) (virStoragePoolPtr pool);
as mentioned previously, a flags here is a safety IMHO
+typedef int + (*virDrvStoragePoolDestroy) (virStoragePoolPtr pool); [...] +/** +* _virNetwork: +* +* Internal structure associated to a storage volume +*/ +struct _virStorageVol { + unsigned int magic; /* specific value to check */ + int refs; /* reference count */ + virConnectPtr conn; /* pointer back to the connection */ + char *pool; /* Pool name of owner */ + char *name; /* the storage vol external name */ + /* XXX currently abusing path for this. Ought not to be so evil */ + char key[PATH_MAX]; /* unique key for storage vol */ };
I'm just a bit surprized by the static allocation of the key. Even if we are passing _virStorageVol data around, I guess the string is zero terminated and we can probably avoid the static size. Or is that too much of a burden ? [...]
+/** + * virStoragePoolCreate: + * @pool: pointer to storage pool + * + * Starts an inactive storage pool + * + * Returns 0 on success, or -1 if it could not be started + */ +int +virStoragePoolCreate(virStoragePoolPtr pool) +{ + virConnectPtr conn; + DEBUG("pool=%p", pool); + + if (pool == NULL) { + TODO; + return (-1); + } + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolCreate) + return conn->storageDriver->poolCreate (pool); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +}
Would just like to see a flags parameter here too.
+/** + * virStoragePoolDestroy: + * @pool: pointer to storage pool + * + * Destroy an active storage pool. The virStoragePoolPtr + * object should not be used after this method returns + * successfully as it has been free'd
maybe indicating the difference between Free/Destroy and Delete would be good here. people might think it's used to free the on-disk resources (I believe it's not the case, but in the context of storage maybe a bit more details are needed it's not like domains for which the runtime state can be recreated from the defined state, for storage it's a bit different I think). [...]
+/** + * virStoragePoolDelete: + * @pool: pointer to storage pool + * @flags: flags for obliteration process + * + * Delete the underlying pool resources. This is + * a non-recoverable operation.
Same as before more details are needed I guess. [...]
diff -r 42e6f49e4e69 src/virterror.c --- a/src/virterror.c Thu Feb 07 12:33:13 2008 -0500 +++ b/src/virterror.c Thu Feb 07 16:52:34 2008 -0500 @@ -258,6 +258,9 @@ virDefaultErrorFunc(virErrorPtr err) break; case VIR_FROM_STATS_LINUX: dom = "Linux Stats "; + break; + case VIR_FROM_STORAGE: + dom = "Storage "; break;
} @@ -665,6 +668,24 @@ __virErrorMsg(virErrorNumber error, cons else errmsg = _("authentication failed: %s"); break; + case VIR_ERR_INVALID_STORAGE_POOL: + if (info == NULL) + errmsg = _("invalid storage pool pointer in"); + else + errmsg = _("invalid storage pool pointer in %s"); + break; + case VIR_ERR_INVALID_STORAGE_VOL: + if (info == NULL) + errmsg = _("invalid storage volume pointer in"); + else + errmsg = _("invalid storage volume pointer in %s"); + break; + case VIR_WAR_NO_STORAGE: + if (info == NULL) + errmsg = _("Failed to find a storage driver"); + else + errmsg = _("Failed to find a storage driver: %s"); + break; } return (errmsg); }
Okay, looks fine to me, +1, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Thu, Feb 14, 2008 at 06:24:09AM -0500, Daniel Veillard wrote:
On Tue, Feb 12, 2008 at 04:30:45AM +0000, Daniel P. Berrange wrote:
This defines the internal driver API for the storage APIs. The pattern follows that used for the existing APIs. NB, both the storage pool and storage volume objects are now top level objects. Previous iterations of this code have the volume as a child of the pool. This unneccessarily complicated the reference counting and forced you to always have a pool available first. [...] + unsigned int flags); +typedef int + (*virDrvStoragePoolCreate) (virStoragePoolPtr pool);
as mentioned previously, a flags here is a safety IMHO
+typedef int + (*virDrvStoragePoolDestroy) (virStoragePoolPtr pool); [...] +/** +* _virNetwork: +* +* Internal structure associated to a storage volume +*/ +struct _virStorageVol { + unsigned int magic; /* specific value to check */ + int refs; /* reference count */ + virConnectPtr conn; /* pointer back to the connection */ + char *pool; /* Pool name of owner */ + char *name; /* the storage vol external name */ + /* XXX currently abusing path for this. Ought not to be so evil */ + char key[PATH_MAX]; /* unique key for storage vol */ };
I'm just a bit surprized by the static allocation of the key. Even if we are passing _virStorageVol data around, I guess the string is zero terminated and we can probably avoid the static size. Or is that too much of a burden ?
Yes, I'll switch this to be allocated on demand - I just happened to have it pre-allocated because I have simply renamed the 'uuid' field which was also pre-allocated.
+int +virStoragePoolCreate(virStoragePoolPtr pool) +{ + virConnectPtr conn; + DEBUG("pool=%p", pool); + + if (pool == NULL) { + TODO; + return (-1); + } + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolCreate) + return conn->storageDriver->poolCreate (pool); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +}
Would just like to see a flags parameter here too.
Yes, all Create methods need flags.
+/** + * virStoragePoolDestroy: + * @pool: pointer to storage pool + * + * Destroy an active storage pool. The virStoragePoolPtr + * object should not be used after this method returns + * successfully as it has been free'd
maybe indicating the difference between Free/Destroy and Delete would be good here. people might think it's used to free the on-disk resources (I believe it's not the case, but in the context of storage maybe a bit more details are needed it's not like domains for which the runtime state can be recreated from the defined state, for storage it's a bit different I think).
This comment is actually bogus - the Destroy method does not actually free the virStoragePoolPtr object itself - you still need to call the explicit virStoragePoolFree method. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:30:45AM +0000, Daniel P. Berrange wrote:
This defines the internal driver API for the storage APIs. The pattern follows that used for the existing APIs. NB, both the storage pool and storage volume objects are now top level objects. Previous iterations of this code have the volume as a child of the pool. This unneccessarily complicated the reference counting and forced you to always have a pool available first.
include/libvirt/virterror.h | 4 src/driver.h | 146 ++++ src/hash.c | 317 ++++++++++ src/internal.h | 68 ++ src/libvirt.c | 1367 ++++++++++++++++++++++++++++++++++++++++++++ src/virterror.c | 21 6 files changed, 1921 insertions(+), 2 deletions(-) diff -r ebc0562abcea include/libvirt/virterror.h --- a/include/libvirt/virterror.h Thu Feb 14 15:58:47 2008 -0500 +++ b/include/libvirt/virterror.h Thu Feb 14 16:04:16 2008 -0500 @@ -54,6 +54,7 @@ typedef enum { VIR_FROM_OPENVZ, /* Error from OpenVZ driver */ VIR_FROM_XENXM, /* Error at Xen XM layer */ VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */ + VIR_FROM_STORAGE, /* Error from storage driver */ } virErrorDomain; @@ -132,6 +133,9 @@ typedef enum { VIR_ERR_NO_NETWORK, /* network not found */ VIR_ERR_INVALID_MAC, /* invalid MAC adress */ VIR_ERR_AUTH_FAILED, /* authentication failed */ + VIR_ERR_INVALID_STORAGE_POOL, /* invalid storage pool object */ + VIR_ERR_INVALID_STORAGE_VOL, /* invalid storage vol object */ + VIR_WAR_NO_STORAGE, /* failed to start storage */ } virErrorNumber; /** diff -r ebc0562abcea src/driver.h --- a/src/driver.h Thu Feb 14 15:58:47 2008 -0500 +++ b/src/driver.h Thu Feb 14 16:04:16 2008 -0500 @@ -412,6 +412,151 @@ struct _virNetworkDriver { virDrvNetworkSetAutostart networkSetAutostart; }; + +typedef int + (*virDrvConnectNumOfStoragePools) (virConnectPtr conn); +typedef int + (*virDrvConnectListStoragePools) (virConnectPtr conn, + char **const names, + int maxnames); +typedef int + (*virDrvConnectNumOfDefinedStoragePools) (virConnectPtr conn); +typedef int + (*virDrvConnectListDefinedStoragePools) (virConnectPtr conn, + char **const names, + int maxnames); +typedef virStoragePoolPtr + (*virDrvStoragePoolLookupByName) (virConnectPtr conn, + const char *name); +typedef virStoragePoolPtr + (*virDrvStoragePoolLookupByUUID) (virConnectPtr conn, + const unsigned char *uuid); +typedef virStoragePoolPtr + (*virDrvStoragePoolLookupByVolume) (virStorageVolPtr vol); +typedef virStoragePoolPtr + (*virDrvStoragePoolCreateXML) (virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); +typedef virStoragePoolPtr + (*virDrvStoragePoolDefineXML) (virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); +typedef int + (*virDrvStoragePoolUndefine) (virStoragePoolPtr pool); +typedef int + (*virDrvStoragePoolBuild) (virStoragePoolPtr pool, + unsigned int flags); +typedef int + (*virDrvStoragePoolCreate) (virStoragePoolPtr pool, + unsigned int flags); +typedef int + (*virDrvStoragePoolDestroy) (virStoragePoolPtr pool); +typedef int + (*virDrvStoragePoolDelete) (virStoragePoolPtr pool, + unsigned int flags); +typedef int + (*virDrvStoragePoolRefresh) (virStoragePoolPtr pool, + unsigned int flags); +typedef int + (*virDrvStoragePoolGetInfo) (virStoragePoolPtr vol, + virStoragePoolInfoPtr info); +typedef char * + (*virDrvStoragePoolGetXMLDesc) (virStoragePoolPtr pool, + unsigned int flags); +typedef int + (*virDrvStoragePoolGetAutostart) (virStoragePoolPtr pool, + int *autostart); +typedef int + (*virDrvStoragePoolSetAutostart) (virStoragePoolPtr pool, + int autostart); +typedef int + (*virDrvStoragePoolNumOfVolumes) (virStoragePoolPtr pool); +typedef int + (*virDrvStoragePoolListVolumes) (virStoragePoolPtr pool, + char **const names, + int maxnames); + + +typedef virStorageVolPtr + (*virDrvStorageVolLookupByName) (virStoragePoolPtr pool, + const char *name); +typedef virStorageVolPtr + (*virDrvStorageVolLookupByKey) (virConnectPtr pool, + const char *key); +typedef virStorageVolPtr + (*virDrvStorageVolLookupByPath) (virConnectPtr pool, + const char *path); + + +typedef virStorageVolPtr + (*virDrvStorageVolCreateXML) (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags); +typedef int + (*virDrvStorageVolDelete) (virStorageVolPtr vol, + unsigned int flags); + +typedef int + (*virDrvStorageVolGetInfo) (virStorageVolPtr vol, + virStorageVolInfoPtr info); +typedef char * + (*virDrvStorageVolGetXMLDesc) (virStorageVolPtr pool, + unsigned int flags); +typedef char * + (*virDrvStorageVolGetPath) (virStorageVolPtr vol); + + + +typedef struct _virStorageDriver virStorageDriver; +typedef virStorageDriver *virStorageDriverPtr; + +/** + * _virStorageDriver: + * + * Structure associated to a network virtualization driver, defining the various + * entry points for it. + * + * All drivers must support the following fields/methods: + * - open + * - close + */ +struct _virStorageDriver { + const char * name; /* the name of the driver */ + virDrvOpen open; + virDrvClose close; + + virDrvConnectNumOfStoragePools numOfPools; + virDrvConnectListStoragePools listPools; + virDrvConnectNumOfDefinedStoragePools numOfDefinedPools; + virDrvConnectListDefinedStoragePools listDefinedPools; + virDrvStoragePoolLookupByName poolLookupByName; + virDrvStoragePoolLookupByUUID poolLookupByUUID; + virDrvStoragePoolLookupByVolume poolLookupByVolume; + virDrvStoragePoolCreateXML poolCreateXML; + virDrvStoragePoolDefineXML poolDefineXML; + virDrvStoragePoolBuild poolBuild; + virDrvStoragePoolUndefine poolUndefine; + virDrvStoragePoolCreate poolCreate; + virDrvStoragePoolDestroy poolDestroy; + virDrvStoragePoolDelete poolDelete; + virDrvStoragePoolRefresh poolRefresh; + virDrvStoragePoolGetInfo poolGetInfo; + virDrvStoragePoolGetXMLDesc poolGetXMLDesc; + virDrvStoragePoolGetAutostart poolGetAutostart; + virDrvStoragePoolSetAutostart poolSetAutostart; + virDrvStoragePoolNumOfVolumes poolNumOfVolumes; + virDrvStoragePoolListVolumes poolListVolumes; + + virDrvStorageVolLookupByName volLookupByName; + virDrvStorageVolLookupByKey volLookupByKey; + virDrvStorageVolLookupByPath volLookupByPath; + virDrvStorageVolCreateXML volCreateXML; + virDrvStorageVolDelete volDelete; + virDrvStorageVolGetInfo volGetInfo; + virDrvStorageVolGetXMLDesc volGetXMLDesc; + virDrvStorageVolGetPath volGetPath; +}; + typedef int (*virDrvStateInitialize) (void); typedef int (*virDrvStateCleanup) (void); typedef int (*virDrvStateReload) (void); @@ -434,6 +579,7 @@ struct _virStateDriver { */ int virRegisterDriver(virDriverPtr); int virRegisterNetworkDriver(virNetworkDriverPtr); +int virRegisterStorageDriver(virStorageDriverPtr); int virRegisterStateDriver(virStateDriverPtr); #ifdef __cplusplus diff -r ebc0562abcea src/hash.c --- a/src/hash.c Thu Feb 14 15:58:47 2008 -0500 +++ b/src/hash.c Thu Feb 14 16:04:16 2008 -0500 @@ -652,6 +652,34 @@ virNetworkFreeName(virNetworkPtr network } /** + * virStoragePoolFreeName: + * @pool: a pool object + * + * Destroy the pool object, this is just used by the pool hash callback. + * + * Returns 0 in case of success and -1 in case of failure. + */ +static int +virStoragePoolFreeName(virStoragePoolPtr pool, const char *name ATTRIBUTE_UNUSED) +{ + return (virStoragePoolFree(pool)); +} + +/** + * virStorageVolFreeName: + * @vol: a vol object + * + * Destroy the vol object, this is just used by the vol hash callback. + * + * Returns 0 in case of success and -1 in case of failure. + */ +static int +virStorageVolFreeName(virStorageVolPtr vol, const char *name ATTRIBUTE_UNUSED) +{ + return (virStorageVolFree(vol)); +} + +/** * virGetConnect: * * Allocates a new hypervisor connection structure @@ -678,6 +706,12 @@ virGetConnect(void) { ret->networks = virHashCreate(20); if (ret->networks == NULL) goto failed; + ret->storagePools = virHashCreate(20); + if (ret->storagePools == NULL) + goto failed; + ret->storageVols = virHashCreate(20); + if (ret->storageVols == NULL) + goto failed; pthread_mutex_init(&ret->lock, NULL); @@ -690,6 +724,10 @@ failed: virHashFree(ret->domains, (virHashDeallocator) virDomainFreeName); if (ret->networks != NULL) virHashFree(ret->networks, (virHashDeallocator) virNetworkFreeName); + if (ret->storagePools != NULL) + virHashFree(ret->storagePools, (virHashDeallocator) virStoragePoolFreeName); + if (ret->storageVols != NULL) + virHashFree(ret->storageVols, (virHashDeallocator) virStorageVolFreeName); pthread_mutex_destroy(&ret->lock); free(ret); @@ -713,6 +751,11 @@ virReleaseConnect(virConnectPtr conn) { virHashFree(conn->domains, (virHashDeallocator) virDomainFreeName); if (conn->networks != NULL) virHashFree(conn->networks, (virHashDeallocator) virNetworkFreeName); + if (conn->storagePools != NULL) + virHashFree(conn->storagePools, (virHashDeallocator) virStoragePoolFreeName); + if (conn->storageVols != NULL) + virHashFree(conn->storageVols, (virHashDeallocator) virStorageVolFreeName); + virResetError(&conn->err); free(conn->name); @@ -1017,6 +1060,280 @@ virUnrefNetwork(virNetworkPtr network) { return (refs); } + +/** + * virGetStoragePool: + * @conn: the hypervisor connection + * @name: pointer to the storage pool name + * @uuid: pointer to the uuid + * + * Lookup if the storage pool 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 + * virFreeStoragePool() is needed to not leak data. + * + * Returns a pointer to the network, or NULL in case of failure + */ +virStoragePoolPtr +__virGetStoragePool(virConnectPtr conn, const char *name, const unsigned char *uuid) { + virStoragePoolPtr ret = NULL; + + if ((!VIR_IS_CONNECT(conn)) || (name == NULL) || (uuid == NULL)) { + virHashError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(NULL); + } + pthread_mutex_lock(&conn->lock); + + /* TODO search by UUID first as they are better differenciators */ + + ret = (virStoragePoolPtr) virHashLookup(conn->storagePools, name); + /* TODO check the UUID */ + if (ret == NULL) { + ret = (virStoragePoolPtr) calloc(1, sizeof(*ret)); + if (ret == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage pool")); + goto error; + } + ret->name = strdup(name); + if (ret->name == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage pool")); + goto error; + } + ret->magic = VIR_STORAGE_POOL_MAGIC; + ret->conn = conn; + if (uuid != NULL) + memcpy(&(ret->uuid[0]), uuid, VIR_UUID_BUFLEN); + + if (virHashAddEntry(conn->storagePools, name, ret) < 0) { + virHashError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to add storage pool to connection hash table")); + goto error; + } + conn->refs++; + } + ret->refs++; + pthread_mutex_unlock(&conn->lock); + return(ret); + +error: + pthread_mutex_unlock(&conn->lock); + if (ret != NULL) { + free(ret->name); + free(ret); + } + return(NULL); +} + + +/** + * virReleaseStoragePool: + * @pool: the pool to release + * + * Unconditionally release all memory associated with a pool. + * The conn.lock mutex must be held prior to calling this, and will + * be released prior to this returning. The pool 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 +virReleaseStoragePool(virStoragePoolPtr pool) { + virConnectPtr conn = pool->conn; + DEBUG("release pool %p %s", pool, pool->name); + + /* TODO search by UUID first as they are better differenciators */ + if (virHashRemoveEntry(conn->storagePools, pool->name, NULL) < 0) + virHashError(conn, VIR_ERR_INTERNAL_ERROR, + _("pool missing from connection hash table")); + + pool->magic = -1; + free(pool->name); + free(pool); + + DEBUG("unref connection %p %s %d", conn, conn->name, conn->refs); + conn->refs--; + if (conn->refs == 0) { + virReleaseConnect(conn); + /* Already unlocked mutex */ + return; + } + + pthread_mutex_unlock(&conn->lock); +} + + +/** + * virUnrefStoragePool: + * @pool: the pool to unreference + * + * Unreference the pool. If the use count drops to zero, the structure is + * actually freed. + * + * Returns the reference count or -1 in case of failure. + */ +int +virUnrefStoragePool(virStoragePoolPtr pool) { + int refs; + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virHashError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + pthread_mutex_lock(&pool->conn->lock); + DEBUG("unref pool %p %s %d", pool, pool->name, pool->refs); + pool->refs--; + refs = pool->refs; + if (refs == 0) { + virReleaseStoragePool(pool); + /* Already unlocked mutex */ + return (0); + } + + pthread_mutex_unlock(&pool->conn->lock); + return (refs); +} + + +/** + * virGetStorageVol: + * @conn: the hypervisor connection + * @pool: pool owning the volume + * @name: pointer to the storage vol name + * @uuid: pointer to the uuid + * + * Lookup if the storage vol 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 + * virFreeStorageVol() is needed to not leak data. + * + * Returns a pointer to the storage vol, or NULL in case of failure + */ +virStorageVolPtr +__virGetStorageVol(virConnectPtr conn, const char *pool, const char *name, const char *key) { + virStorageVolPtr ret = NULL; + + if ((!VIR_IS_CONNECT(conn)) || (name == NULL) || (key == NULL)) { + virHashError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(NULL); + } + pthread_mutex_lock(&conn->lock); + + ret = (virStorageVolPtr) virHashLookup(conn->storageVols, key); + if (ret == NULL) { + ret = (virStorageVolPtr) calloc(1, sizeof(*ret)); + if (ret == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage vol")); + goto error; + } + ret->pool = strdup(pool); + if (ret->pool == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage vol")); + goto error; + } + ret->name = strdup(name); + if (ret->name == NULL) { + virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage vol")); + goto error; + } + strncpy(ret->key, key, sizeof(ret->key)-1); + ret->key[sizeof(ret->key)-1] = '\0'; + ret->magic = VIR_STORAGE_VOL_MAGIC; + ret->conn = conn; + + if (virHashAddEntry(conn->storageVols, key, ret) < 0) { + virHashError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to add storage vol to connection hash table")); + goto error; + } + conn->refs++; + } + ret->refs++; + pthread_mutex_unlock(&conn->lock); + return(ret); + +error: + pthread_mutex_unlock(&conn->lock); + if (ret != NULL) { + free(ret->name); + free(ret->pool); + free(ret); + } + return(NULL); +} + + +/** + * virReleaseStorageVol: + * @vol: the vol to release + * + * Unconditionally release all memory associated with a vol. + * The conn.lock mutex must be held prior to calling this, and will + * be released prior to this returning. The vol 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 +virReleaseStorageVol(virStorageVolPtr vol) { + virConnectPtr conn = vol->conn; + DEBUG("release vol %p %s", vol, vol->name); + + /* TODO search by UUID first as they are better differenciators */ + if (virHashRemoveEntry(conn->storageVols, vol->key, NULL) < 0) + virHashError(conn, VIR_ERR_INTERNAL_ERROR, + _("vol missing from connection hash table")); + + vol->magic = -1; + free(vol->name); + free(vol->pool); + free(vol); + + DEBUG("unref connection %p %s %d", conn, conn->name, conn->refs); + conn->refs--; + if (conn->refs == 0) { + virReleaseConnect(conn); + /* Already unlocked mutex */ + return; + } + + pthread_mutex_unlock(&conn->lock); +} + + +/** + * virUnrefStorageVol: + * @vol: the vol to unreference + * + * Unreference the vol. If the use count drops to zero, the structure is + * actually freed. + * + * Returns the reference count or -1 in case of failure. + */ +int +virUnrefStorageVol(virStorageVolPtr vol) { + int refs; + + if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { + virHashError(vol->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + pthread_mutex_lock(&vol->conn->lock); + DEBUG("unref vol %p %s %d", vol, vol->name, vol->refs); + vol->refs--; + refs = vol->refs; + if (refs == 0) { + virReleaseStorageVol(vol); + /* Already unlocked mutex */ + return (0); + } + + pthread_mutex_unlock(&vol->conn->lock); + return (refs); +} + + /* * vim: set tabstop=4: * vim: set shiftwidth=4: diff -r ebc0562abcea src/internal.h --- a/src/internal.h Thu Feb 14 15:58:47 2008 -0500 +++ b/src/internal.h Thu Feb 14 16:04:16 2008 -0500 @@ -120,7 +120,7 @@ extern int debugFlag; * VIR_DOMAIN_MAGIC: * * magic value used to protect the API when pointers to domain structures - * are passed down by the uers. + * are passed down by the users. */ #define VIR_DOMAIN_MAGIC 0xDEAD4321 #define VIR_IS_DOMAIN(obj) ((obj) && (obj)->magic==VIR_DOMAIN_MAGIC) @@ -130,11 +130,31 @@ extern int debugFlag; * VIR_NETWORK_MAGIC: * * magic value used to protect the API when pointers to network structures - * are passed down by the uers. + * are passed down by the users. */ #define VIR_NETWORK_MAGIC 0xDEAD1234 #define VIR_IS_NETWORK(obj) ((obj) && (obj)->magic==VIR_NETWORK_MAGIC) #define VIR_IS_CONNECTED_NETWORK(obj) (VIR_IS_NETWORK(obj) && VIR_IS_CONNECT((obj)->conn)) + +/** + * VIR_STORAGE_POOL_MAGIC: + * + * magic value used to protect the API when pointers to storage pool structures + * are passed down by the users. + */ +#define VIR_STORAGE_POOL_MAGIC 0xDEAD5678 +#define VIR_IS_STORAGE_POOL(obj) ((obj) && (obj)->magic==VIR_STORAGE_POOL_MAGIC) +#define VIR_IS_CONNECTED_STORAGE_POOL(obj) (VIR_IS_STORAGE_POOL(obj) && VIR_IS_CONNECT((obj)->conn)) + +/** + * VIR_STORAGE_VOL_MAGIC: + * + * magic value used to protect the API when pointers to storage vol structures + * are passed down by the users. + */ +#define VIR_STORAGE_VOL_MAGIC 0xDEAD8765 +#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)) /* * arbitrary limitations @@ -155,6 +175,7 @@ struct _virConnect { /* The underlying hypervisor driver and network driver. */ virDriverPtr driver; virNetworkDriverPtr networkDriver; + virStorageDriverPtr storageDriver; /* Private data pointer which can be used by driver and * network driver as they wish. @@ -162,6 +183,7 @@ struct _virConnect { */ void * privateData; void * networkPrivateData; + void * storagePrivateData; /* Per-connection error. */ virError err; /* the last error */ @@ -177,6 +199,8 @@ struct _virConnect { pthread_mutex_t lock; virHashTablePtr domains; /* hash table for known domains */ virHashTablePtr networks; /* hash table for known domains */ + virHashTablePtr storagePools;/* hash table for known storage pools */ + virHashTablePtr storageVols;/* hash table for known storage vols */ int refs; /* reference count */ }; @@ -205,6 +229,34 @@ struct _virNetwork { virConnectPtr conn; /* pointer back to the connection */ char *name; /* the network external name */ unsigned char uuid[VIR_UUID_BUFLEN]; /* the network unique identifier */ +}; + +/** +* _virStoragePool: +* +* Internal structure associated to a storage pool +*/ +struct _virStoragePool { + unsigned int magic; /* specific value to check */ + int refs; /* reference count */ + virConnectPtr conn; /* pointer back to the connection */ + char *name; /* the storage pool external name */ + unsigned char uuid[VIR_UUID_BUFLEN]; /* the storage pool unique identifier */ +}; + +/** +* _virStorageVol: +* +* Internal structure associated to a storage volume +*/ +struct _virStorageVol { + unsigned int magic; /* specific value to check */ + int refs; /* reference count */ + virConnectPtr conn; /* pointer back to the connection */ + char *pool; /* Pool name of owner */ + char *name; /* the storage vol external name */ + /* XXX currently abusing path for this. Ought not to be so evil */ + char key[PATH_MAX]; /* unique key for storage vol */ }; @@ -243,8 +295,20 @@ virNetworkPtr __virGetNetwork (virConne const unsigned char *uuid); int virUnrefNetwork (virNetworkPtr network); +virStoragePoolPtr __virGetStoragePool (virConnectPtr conn, + const char *name, + const unsigned char *uuid); +int virUnrefStoragePool (virStoragePoolPtr pool); +virStorageVolPtr __virGetStorageVol (virConnectPtr conn, + const char *pool, + const char *name, + const char *key); +int virUnrefStorageVol (virStorageVolPtr vol); + #define virGetDomain(c,n,u) __virGetDomain((c),(n),(u)) #define virGetNetwork(c,n,u) __virGetNetwork((c),(n),(u)) +#define virGetStoragePool(c,n,u) __virGetStoragePool((c),(n),(u)) +#define virGetStorageVol(c,p,n,u) __virGetStorageVol((c),(p),(n),(u)) int __virStateInitialize(void); int __virStateCleanup(void); diff -r ebc0562abcea src/libvirt.c --- a/src/libvirt.c Thu Feb 14 15:58:47 2008 -0500 +++ b/src/libvirt.c Thu Feb 14 16:04:16 2008 -0500 @@ -51,6 +51,8 @@ static int virDriverTabCount = 0; static int virDriverTabCount = 0; static virNetworkDriverPtr virNetworkDriverTab[MAX_DRIVERS]; static int virNetworkDriverTabCount = 0; +static virStorageDriverPtr virStorageDriverTab[MAX_DRIVERS]; +static int virStorageDriverTabCount = 0; static virStateDriverPtr virStateDriverTab[MAX_DRIVERS]; static int virStateDriverTabCount = 0; static int initialized = 0; @@ -317,6 +319,58 @@ virLibNetworkError(virNetworkPtr network } /** + * virLibStoragePoolError: + * @conn: the connection if available + * @error: the error noumber + * @info: extra information string + * + * Handle an error at the connection level + */ +static void +virLibStoragePoolError(virStoragePoolPtr pool, 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_STORAGE_POOL) + conn = pool->conn; + + __virRaiseError(conn, NULL, NULL, VIR_FROM_STORAGE, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +/** + * virLibStorageVolError: + * @conn: the connection if available + * @error: the error noumber + * @info: extra information string + * + * Handle an error at the connection level + */ +static void +virLibStorageVolError(virStorageVolPtr vol, 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_STORAGE_VOL) + conn = vol->conn; + + __virRaiseError(conn, NULL, NULL, VIR_FROM_STORAGE, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +/** * virRegisterNetworkDriver: * @driver: pointer to a network driver block * @@ -342,6 +396,34 @@ virRegisterNetworkDriver(virNetworkDrive virNetworkDriverTab[virNetworkDriverTabCount] = driver; return virNetworkDriverTabCount++; +} + +/** + * virRegisterStorageDriver: + * @driver: pointer to a storage driver block + * + * Register a storage virtualization driver + * + * Returns the driver priority or -1 in case of error. + */ +int +virRegisterStorageDriver(virStorageDriverPtr driver) +{ + if (virInitialize() < 0) + return -1; + + if (driver == NULL) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + if (virStorageDriverTabCount >= MAX_DRIVERS) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + virStorageDriverTab[virStorageDriverTabCount] = driver; + return virStorageDriverTabCount++; } /** @@ -607,6 +689,29 @@ do_open (const char *name, } } + + /* Secondary driver for storage. Optional */ + for (i = 0; i < virStorageDriverTabCount; i++) { + res = virStorageDriverTab[i]->open (ret, uri, auth, flags); +#ifdef ENABLE_DEBUG + DEBUG("storage driver %d %s returned %s", + i, virStorageDriverTab[i]->name, + res == VIR_DRV_OPEN_SUCCESS ? "SUCCESS" : + (res == VIR_DRV_OPEN_DECLINED ? "DECLINED" : + (res == VIR_DRV_OPEN_ERROR ? "ERROR" : "unknown status"))); +#endif + if (res == VIR_DRV_OPEN_ERROR) { + if (0 && STREQ(virStorageDriverTab[i]->name, "remote")) { + virLibConnWarning (NULL, VIR_WAR_NO_STORAGE, + "Is the daemon running ?"); + } + break; + } else if (res == VIR_DRV_OPEN_SUCCESS) { + ret->storageDriver = virStorageDriverTab[i]; + break; + } + } + /* Cleansing flags */ ret->flags = flags & VIR_CONNECT_RO; @@ -702,6 +807,8 @@ virConnectClose(virConnectPtr conn) if (conn->networkDriver) conn->networkDriver->close (conn); + if (conn->storageDriver) + conn->storageDriver->close (conn); conn->driver->close (conn); if (virUnrefConnect(conn) < 0) @@ -3535,6 +3642,1266 @@ virNetworkSetAutostart(virNetworkPtr net return -1; } + +/** + * virStoragePoolGetConnect: + * @pool: pointer to a poool + * + * Provides the connection pointer associated with a storage pool. The + * reference counter on the connection is not increased by this + * call. + * + * WARNING: When writing libvirt bindings in other languages, do + * not use this function. Instead, store the connection and + * the pool object together. + * + * Returns the virConnectPtr or NULL in case of failure. + */ +virConnectPtr +virStoragePoolGetConnect (virStoragePoolPtr pool) +{ + DEBUG("pool=%p", pool); + + if (!VIR_IS_STORAGE_POOL (pool)) { + virLibStoragePoolError (NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return NULL; + } + return pool->conn; +} + +/** + * virConnectNumOfStoragePools: + * @conn: pointer to hypervisor connection + * + * Provides the number of active storage pools + * + * Returns the number of pools found, or -1 on error + */ +int +virConnectNumOfStoragePools (virConnectPtr conn) +{ + DEBUG("conn=%p", conn); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->numOfPools) + return conn->storageDriver->numOfPools (conn); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +/** + * virConnectListStoragePools: + * @conn: pointer to hypervisor connection + * @names: array of char * to fill with pool names (allocated by caller) + * @maxnames: size of the names array + * + * Provides the list of names of active storage pools + * upto maxnames. If there are more than maxnames, the + * remaining names will be silently ignored. + * + * Returns 0 on success, -1 on error + */ +int +virConnectListStoragePools (virConnectPtr conn, + char **const names, + int maxnames) +{ + DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if ((names == NULL) || (maxnames < 0)) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->listPools) + return conn->storageDriver->listPools (conn, names, maxnames); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +} + + +/** + * virConnectNumOfDefinedStoragePools: + * @conn: pointer to hypervisor connection + * + * Provides the number of inactive storage pools + * + * Returns the number of pools found, or -1 on error + */ +int +virConnectNumOfDefinedStoragePools(virConnectPtr conn) +{ + DEBUG("conn=%p", conn); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->numOfDefinedPools) + return conn->storageDriver->numOfDefinedPools (conn); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virConnectListDefinedStoragePools: + * @conn: pointer to hypervisor connection + * @names: array of char * to fill with pool names (allocated by caller) + * @maxnames: size of the names array + * + * Provides the list of names of inactive storage pools + * upto maxnames. If there are more than maxnames, the + * remaining names will be silently ignored. + * + * Returns 0 on success, -1 on error + */ +int +virConnectListDefinedStoragePools(virConnectPtr conn, + char **const names, + int maxnames) +{ + DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if ((names == NULL) || (maxnames < 0)) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->listDefinedPools) + return conn->storageDriver->listDefinedPools (conn, names, maxnames); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolLookupByName: + * @conn: pointer to hypervisor connection + * @name: name of pool to fetch + * + * Fetch a storage pool based on its unique name + * + * Returns a virStoragePoolPtr object, or NULL if no matching pool is found + */ +virStoragePoolPtr +virStoragePoolLookupByName(virConnectPtr conn, + const char *name) +{ + DEBUG("conn=%p, name=%s", 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->storageDriver && conn->storageDriver->poolLookupByName) + return conn->storageDriver->poolLookupByName (conn, name); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + +/** + * virStoragePoolLookupByUUID: + * @conn: pointer to hypervisor connection + * @uuid: globally unique id of pool to fetch + * + * Fetch a storage pool based on its globally unique id + * + * Returns a virStoragePoolPtr object, or NULL if no matching pool is found + */ +virStoragePoolPtr +virStoragePoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + DEBUG("conn=%p, uuid=%s", conn, uuid); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (uuid == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (conn->storageDriver && conn->storageDriver->poolLookupByUUID) + return conn->storageDriver->poolLookupByUUID (conn, uuid); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + +} + + +/** + * virStoragePoolLookupByUUIDString: + * @conn: pointer to hypervisor connection + * @uuidstr: globally unique id of pool to fetch + * + * Fetch a storage pool based on its globally unique id + * + * Returns a virStoragePoolPtr object, or NULL if no matching pool is found + */ +virStoragePoolPtr +virStoragePoolLookupByUUIDString(virConnectPtr conn, + const char *uuidstr) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + DEBUG("conn=%p, uuidstr=%s", conn, uuidstr); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (uuidstr == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (virUUIDParse(uuidstr, uuid) < 0) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + return virStoragePoolLookupByUUID(conn, uuid); +} + + +/** + * virStoragePoolLookupByVolume: + * @vol: pointer to storage volume + * + * Fetch a storage pool which contains a particular volume + * + * Returns a virStoragePoolPtr object, or NULL if no matching pool is found + */ +virStoragePoolPtr +virStoragePoolLookupByVolume(virStorageVolPtr vol) +{ + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + + if (vol->conn->storageDriver && vol->conn->storageDriver->poolLookupByVolume) + return vol->conn->storageDriver->poolLookupByVolume (vol); + + virLibConnError (vol->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + +} + +/** + * virStoragePoolCreateXML: + * @conn: pointer to hypervisor connection + * @xmlDesc: XML description for new pool + * + * Create a new storage based on its XML description. The + * pool is not persitent, so its definition will disappear + * when it is destroyed, or if the host is restarted + * + * Returns a virStoragePoolPtr object, or NULL if creation failed + */ +virStoragePoolPtr +virStoragePoolCreateXML(virConnectPtr conn, + const char *xmlDesc, + unsigned int flags) +{ + DEBUG("conn=%p, xmlDesc=%s", conn, xmlDesc); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (xmlDesc == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (NULL); + } + + if (conn->storageDriver && conn->storageDriver->poolCreateXML) + return conn->storageDriver->poolCreateXML (conn, xmlDesc, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + +/** + * virStoragePoolDefineXML: + * @conn: pointer to hypervisor connection + * @xml: XML description for new pool + * + * Define a new inactive storage pool based on its XML description. The + * pool is persitent, until explicitly undefined. + * + * Returns a virStoragePoolPtr object, or NULL if creation failed + */ +virStoragePoolPtr +virStoragePoolDefineXML(virConnectPtr conn, + const char *xml, + unsigned int flags) +{ + DEBUG("conn=%p, xml=%s", conn, xml); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (NULL); + } + if (xml == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (conn->storageDriver && conn->storageDriver->poolDefineXML) + return conn->storageDriver->poolDefineXML (conn, xml, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + +} + +/** + * virStoragePoolBuild: + * @pool: pointer to storage pool + * + * Build the underlying storage pool + * + * Returns 0 on success, or -1 upon failure + */ +int +virStoragePoolBuild(virStoragePoolPtr pool, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("pool=%p, flags=%u", pool, flags); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (-1); + } + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolBuild) + return conn->storageDriver->poolBuild (pool, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +} + + +/** + * virStoragePoolUndefine: + * @pool: pointer to storage pool + * + * Undefine an inactive storage pool + * + * Returns a virStoragePoolPtr object, or NULL if creation failed + */ +int +virStoragePoolUndefine(virStoragePoolPtr pool) +{ + virConnectPtr conn; + DEBUG("pool=%p", pool); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); + return (-1); + } + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolUndefine) + return conn->storageDriver->poolUndefine (pool); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +} + + +/** + * virStoragePoolCreate: + * @pool: pointer to storage pool + * + * Starts an inactive storage pool + * + * Returns 0 on success, or -1 if it could not be started + */ +int +virStoragePoolCreate(virStoragePoolPtr pool, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("pool=%p", pool); + + if (pool == NULL) { + TODO; + return (-1); + } + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolCreate) + return conn->storageDriver->poolCreate (pool, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +} + + +/** + * virStoragePoolDestroy: + * @pool: pointer to storage pool + * + * Destroy an active storage pool. This will deactivate the + * pool on the host, but keep any persistent config associated + * with it. If it has a persistent config it can later be + * restarted with virStoragePoolCreate(). This does not free + * the associated virStoragePoolPtr object. + * + * Returns 0 on success, or -1 if it could not be destroyed + */ +int +virStoragePoolDestroy(virStoragePoolPtr pool) +{ + virConnectPtr conn; + DEBUG("pool=%p", pool); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolDestroy) + return conn->storageDriver->poolDestroy (pool); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +/** + * virStoragePoolDelete: + * @pool: pointer to storage pool + * @flags: flags for obliteration process + * + * Delete the underlying pool resources. This is + * a non-recoverable operation. The virStoragePoolPtr object + * itself is not free'd. + * + * Returns 0 on success, or -1 if it could not be obliterate + */ +int +virStoragePoolDelete(virStoragePoolPtr pool, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("pool=%p, flags=%u", pool, flags); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolDelete) + return conn->storageDriver->poolDelete (pool, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolFree: + * @pool: pointer to storage pool + * + * Free a storage pool object, releasing all memory associated with + * it. Does not change the state of the pool on the host. + * + * Returns 0 on success, or -1 if it could not be free'd. + */ +int +virStoragePoolFree(virStoragePoolPtr pool) +{ + DEBUG("pool=%p", pool); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + if (virUnrefStoragePool(pool) < 0) + return (-1); + return(0); + +} + + +/** + * virStoragePoolRefresh: + * @pool: pointer to storage pool + * @flags: flags to control refresh behaviour (currently unused, use 0) + * + * Request that the pool refresh its list of volumes. This may + * involve communicating with a remote server, and/or initializing + * new devices at the OS layer + * + * Return 0 if the volume list was refreshed, -1 on failure + */ +int +virStoragePoolRefresh(virStoragePoolPtr pool, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("pool=%p flags=%u", pool, flags); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + conn = pool->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->poolRefresh) + return conn->storageDriver->poolRefresh (pool, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolGetName: + * @pool: pointer to storage pool + * + * Fetch the locally unique name of the storage pool + * + * Return the name of the pool, or NULL on error + */ +const char* +virStoragePoolGetName(virStoragePoolPtr pool) +{ + DEBUG("pool=%p", pool); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (NULL); + } + return (pool->name); + +} + + +/** + * virStoragePoolGetUUID: + * @pool: pointer to storage pool + * @uuid: buffer of VIR_UUID_BUFLEN bytes in size + * + * Fetch the globally unique ID of the storage pool + * + * Return 0 on success, or -1 on error; + */ +int +virStoragePoolGetUUID(virStoragePoolPtr pool, + unsigned char *uuid) +{ + DEBUG("pool=%p, uuid=%p", pool, uuid); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + if (uuid == NULL) { + virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + memcpy(uuid, &pool->uuid[0], VIR_UUID_BUFLEN); + + return (0); + +} + +/** + * virStoragePoolGetUUIDString: + * @pool: pointer to storage pool + * @buf: buffer of VIR_UUID_STRING_BUFLEN bytes in size + * + * Fetch the globally unique ID of the storage pool as a string + * + * Return 0 on success, or -1 on error; + */ +int +virStoragePoolGetUUIDString(virStoragePoolPtr pool, + char *buf) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + DEBUG("pool=%p, buf=%p", pool, buf); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + if (buf == NULL) { + virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (virStoragePoolGetUUID(pool, &uuid[0])) + return (-1); + + virUUIDFormat(uuid, buf); + return (0); + +} + + +/** + * virStoragePoolGetInfo: + * @pool: pointer to storage pool + * @info: pointer at which to store info + * + * Get volatile information about the storage pool + * such as free space / usage summary + * + * returns 0 on success, or -1 on failure. + */ +int +virStoragePoolGetInfo(virStoragePoolPtr pool, + virStoragePoolInfoPtr info) +{ + virConnectPtr conn; + DEBUG("pool=%p, info=%p", pool, info); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + if (info == NULL) { + virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + memset(info, 0, sizeof(virStoragePoolInfo)); + + conn = pool->conn; + + if (conn->storageDriver->poolGetInfo) + return conn->storageDriver->poolGetInfo (pool, info); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + +} + + +/** + * virStoragePoolGetXMLDesc: + * @pool: pointer to storage pool + * @flags: flags for XML format options (unused, pass 0) + * + * Fetch an XML document describing all aspects of the + * storage pool. This is suitable for later feeding back + * into the virStoragePoolCreateXML method. + * + * returns a XML document, or NULL on error + */ +char * +virStoragePoolGetXMLDesc(virStoragePoolPtr pool, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("pool=%p, flags=%u", pool, flags); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (NULL); + } + if (flags != 0) { + virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + conn = pool->conn; + + if (conn->storageDriver && conn->storageDriver->poolGetXMLDesc) + return conn->storageDriver->poolGetXMLDesc (pool, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + +} + + +/** + * virStoragePoolGetAutostart: + * @pool: pointer to storage pool + * @autostart: location in which to store autostart flag + * + * Fetches the value of the autostart flag, which determines + * whether the pool is automatically started at boot time + * + * return 0 on success, -1 on failure + */ +int +virStoragePoolGetAutostart(virStoragePoolPtr pool, + int *autostart) +{ + virConnectPtr conn; + DEBUG("pool=%p, autostart=%p", pool, autostart); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + if (!autostart) { + virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + conn = pool->conn; + + if (conn->storageDriver && conn->storageDriver->poolGetAutostart) + return conn->storageDriver->poolGetAutostart (pool, autostart); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolSetAutostart: + * @pool: pointer to storage pool + * @autostart: new flag setting + * + * Sets the autostart flag + * + * returns 0 on success, -1 on failure + */ +int +virStoragePoolSetAutostart(virStoragePoolPtr pool, + int autostart) +{ + virConnectPtr conn; + DEBUG("pool=%p, autostart=%d", pool, autostart); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + conn = pool->conn; + + if (conn->storageDriver && conn->storageDriver->poolSetAutostart) + return conn->storageDriver->poolSetAutostart (pool, autostart); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolNumOfVolumes: + * @pool: pointer to storage pool + * + * Fetch the number of storage volumes within a pool + * + * Returns the number of storage pools, or -1 on failure + */ +int +virStoragePoolNumOfVolumes(virStoragePoolPtr pool) +{ + DEBUG("pool=%p", pool); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->poolNumOfVolumes) + return pool->conn->storageDriver->poolNumOfVolumes (pool); + + virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStoragePoolListVolumes: + * @pool: pointer to storage pool + * @names: array in which to storage volume names + * @maxnames: size of names array + * + * Fetch list of storage volume names, limiting to + * at most maxnames. + * + * Returns the number of names fetched, or -1 on error + */ +int +virStoragePoolListVolumes(virStoragePoolPtr pool, + char **const names, + int maxnames) +{ + DEBUG("pool=%p, names=%p, maxnames=%d", pool, names, maxnames); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (-1); + } + + if ((names == NULL) || (maxnames < 0)) { + virLibConnError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->poolListVolumes) + return pool->conn->storageDriver->poolListVolumes (pool, names, maxnames); + + virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStorageVolGetConnect: + * @vol: pointer to a poool + * + * Provides the connection pointer associated with a storage volume. The + * reference counter on the connection is not increased by this + * call. + * + * WARNING: When writing libvirt bindings in other languages, do + * not use this function. Instead, store the connection and + * the volume object together. + * + * Returns the virConnectPtr or NULL in case of failure. + */ +virConnectPtr +virStorageVolGetConnect (virStorageVolPtr vol) +{ + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL (vol)) { + virLibStoragePoolError (NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return NULL; + } + return vol->conn; +} + + +/** + * virStorageVolLookupByName: + * @pool: pointer to storage pool + * @name: name of storage volume + * + * Fetch a pointer to a storage volume based on its name + * within a pool + * + * return a storage volume, or NULL if not found / error + */ +virStorageVolPtr +virStorageVolLookupByName(virStoragePoolPtr pool, + const char *name) +{ + DEBUG("pool=%p, name=%s", pool, name); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (name == NULL) { + virLibConnError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->volLookupByName) + return pool->conn->storageDriver->volLookupByName (pool, name); + + virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + + +/** + * virStorageVolLookupByKey: + * @conn: pointer to hypervisor connection + * @key: globally unique key + * + * Fetch a pointer to a storage volume based on its + * globally unique key + * + * return a storage volume, or NULL if not found / error + */ +virStorageVolPtr +virStorageVolLookupByKey(virConnectPtr conn, + const char *key) +{ + DEBUG("conn=%p, key=%s", conn, key); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (key == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (conn->storageDriver && conn->storageDriver->volLookupByKey) + return conn->storageDriver->volLookupByKey (conn, key); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + +/** + * virStorageVolLookupByPath: + * @conn: pointer to hypervisor connection + * @path: locally unique path + * + * Fetch a pointer to a storage volume based on its + * locally (host) unique path + * + * return a storage volume, or NULL if not found / error + */ +virStorageVolPtr +virStorageVolLookupByPath(virConnectPtr conn, + const char *path) +{ + DEBUG("conn=%p, path=%s", conn, path); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (path == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (conn->storageDriver && conn->storageDriver->volLookupByPath) + return conn->storageDriver->volLookupByPath (conn, path); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + +/** + * virStorageVolGetName: + * @vol: pointer to storage volume + * + * Fetch the storage volume name. This is unique + * within the scope of a pool + * + * return the volume name, or NULL on error + */ +const char* +virStorageVolGetName(virStorageVolPtr vol) +{ + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + return (vol->name); +} + + +/** + * virStorageVolGetKey: + * @vol: pointer to storage volume + * + * Fetch the storage volume key. This is globally + * unique, so the same volume will hve the same + * key no matter what host it is accessed from + * + * return the volume key, or NULL on error + */ +const char* +virStorageVolGetKey(virStorageVolPtr vol) +{ + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + return (vol->key); +} + + +/** + * virStorageVolCreateXML: + * @pool: pointer to storage pool + * @xmldesc: description of volume to create + * @flags: flags for creation (unused, pass 0) + * + * Create a storage volume within a pool based + * on an XML description. Not all pools support + * creation of volumes + * + * return the storage volume, or NULL on error + */ +virStorageVolPtr +virStorageVolCreateXML(virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags) +{ + DEBUG("pool=%p, flags=%u", pool, flags); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + + if (pool->conn->flags & VIR_CONNECT_RO) { + virLibConnError(pool->conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (NULL); + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->volCreateXML) + return pool->conn->storageDriver->volCreateXML (pool, xmldesc, flags); + + virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + +/** + * virStorageVolDelete: + * @vol: pointer to storage volume + * + * Delete the storage volume from the pool + * + * Return 0 on success, or -1 on error + */ +int +virStorageVolDelete(virStorageVolPtr vol, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("vol=%p, flags=%u", vol, flags); + + if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (-1); + } + + conn = vol->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibStorageVolError(vol, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->volDelete) + return conn->storageDriver->volDelete (vol, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStorageVolFree: + * @vol: pointer to storage volume + * + * Release the storage volume handle. The underlying + * storage volume contains to exist + * + * Return 0 on success, or -1 on error + */ +int +virStorageVolFree(virStorageVolPtr vol) +{ + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (-1); + } + if (virUnrefStorageVol(vol) < 0) + return (-1); + return(0); +} + + +/** + * virStorageVolGetInfo: + * @vol: pointer to storage volume + * @info: pointer at which to store info + * + * Fetches volatile information about the storage + * volume such as its current allocation + * + * Return 0 on success, or -1 on failure + */ +int +virStorageVolGetInfo(virStorageVolPtr vol, + virStorageVolInfoPtr info) +{ + virConnectPtr conn; + DEBUG("vol=%p, info=%p", vol, info); + + if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (-1); + } + if (info == NULL) { + virLibStorageVolError(vol, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + memset(info, 0, sizeof(virStorageVolInfo)); + + conn = vol->conn; + + if (conn->storageDriver->volGetInfo) + return conn->storageDriver->volGetInfo (vol, info); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** + * virStorageVolGetXMLDesc: + * @vol: pointer to storage volume + * @flags: flags for XML generation (unused, pass 0) + * + * Fetch an XML document describing all aspects of + * the storage volume + * + * Return the XML document, or NULL on error + */ +char * +virStorageVolGetXMLDesc(virStorageVolPtr vol, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("vol=%p, flags=%u", vol, flags); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + if (flags != 0) { + virLibStorageVolError(vol, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + conn = vol->conn; + + if (conn->storageDriver && conn->storageDriver->volGetXMLDesc) + return conn->storageDriver->volGetXMLDesc (vol, flags); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + +} + + +/** + * virStorageVolGetPath: + * @vol: pointer to storage volume + * + * Fetch the storage volume path. Depending on the pool + * configuration this is either persistent across hosts, + * or dynamically assigned at pool startup. Consult + * pool documentation for information on getting the + * persistent naming + * + * Returns the storage volume path, or NULL on error + */ +char * +virStorageVolGetPath(virStorageVolPtr vol) +{ + virConnectPtr conn; + DEBUG("vol=%p", vol); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + + conn = vol->conn; + + if (conn->storageDriver && conn->storageDriver->volGetPath) + return conn->storageDriver->volGetPath (vol); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + /* * vim: set tabstop=4: * vim: set shiftwidth=4: diff -r ebc0562abcea src/virterror.c --- a/src/virterror.c Thu Feb 14 15:58:47 2008 -0500 +++ b/src/virterror.c Thu Feb 14 16:04:16 2008 -0500 @@ -296,6 +296,9 @@ virDefaultErrorFunc(virErrorPtr err) case VIR_FROM_STATS_LINUX: dom = "Linux Stats "; break; + case VIR_FROM_STORAGE: + dom = "Storage "; + break; } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { @@ -675,6 +678,24 @@ __virErrorMsg(virErrorNumber error, cons else errmsg = _("authentication failed: %s"); break; + case VIR_ERR_INVALID_STORAGE_POOL: + if (info == NULL) + errmsg = _("invalid storage pool pointer in"); + else + errmsg = _("invalid storage pool pointer in %s"); + break; + case VIR_ERR_INVALID_STORAGE_VOL: + if (info == NULL) + errmsg = _("invalid storage volume pointer in"); + else + errmsg = _("invalid storage volume pointer in %s"); + break; + case VIR_WAR_NO_STORAGE: + if (info == NULL) + errmsg = _("Failed to find a storage driver"); + else + errmsg = _("Failed to find a storage driver: %s"); + break; } return (errmsg); } -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This refreshes the API docs to detail the new public APIs which were added by the last patch. libvirt-api.xml | 358 +++++++++++++++++ libvirt-refs.xml | 1126 ++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 1311 insertions(+), 173 deletions(-) diff -r 171b14380600 docs/libvirt-api.xml --- a/docs/libvirt-api.xml Wed Jan 30 17:30:34 2008 -0500 +++ b/docs/libvirt-api.xml Wed Jan 30 17:45:30 2008 -0500 @@ -20,13 +20,17 @@ <exports symbol='VIR_CRED_EXTERNAL' type='enum'/> <exports symbol='VIR_DOMAIN_SHUTDOWN' type='enum'/> <exports symbol='VIR_DOMAIN_SCHED_FIELD_UINT' type='enum'/> + <exports symbol='VIR_STORAGE_POOL_BUILDING' type='enum'/> <exports symbol='VIR_CRED_CNONCE' type='enum'/> <exports symbol='VIR_CRED_ECHOPROMPT' type='enum'/> <exports symbol='VIR_DOMAIN_SCHED_FIELD_DOUBLE' type='enum'/> + <exports symbol='VIR_STORAGE_VOL_BLOCK' type='enum'/> + <exports symbol='VIR_STORAGE_POOL_RUNNING' type='enum'/> <exports symbol='VIR_DOMAIN_SCHED_FIELD_LLONG' type='enum'/> <exports symbol='VIR_JOB_FAILED' type='enum'/> <exports symbol='VIR_CONNECT_RO' type='enum'/> <exports symbol='VIR_CRED_AUTHNAME' type='enum'/> + <exports symbol='VIR_STORAGE_POOL_BUILD_REPAIR' type='enum'/> <exports symbol='VIR_CRED_LANGUAGE' type='enum'/> <exports symbol='VIR_CRED_NOECHOPROMPT' type='enum'/> <exports symbol='VIR_JOB_COMPLETE' type='enum'/> @@ -34,11 +38,16 @@ <exports symbol='VIR_MIGRATE_LIVE' type='enum'/> <exports symbol='VIR_CRED_USERNAME' type='enum'/> <exports symbol='VIR_VCPU_OFFLINE' type='enum'/> + <exports symbol='VIR_STORAGE_VOL_VIRTUAL' type='enum'/> + <exports symbol='VIR_STORAGE_POOL_BUILD_EXTEND' type='enum'/> <exports symbol='VIR_DOMAIN_RUNNING' type='enum'/> <exports symbol='VIR_DOMAIN_BLOCKED' type='enum'/> <exports symbol='VIR_DOMAIN_SHUTOFF' type='enum'/> <exports symbol='VIR_JOB_CANCELLED' type='enum'/> + <exports symbol='VIR_STORAGE_POOL_DELETE_CLEAR' type='enum'/> + <exports symbol='VIR_STORAGE_POOL_BUILD_NEW' type='enum'/> <exports symbol='VIR_VCPU_BLOCKED' type='enum'/> + <exports symbol='VIR_STORAGE_POOL_DELETE_NORMAL' type='enum'/> <exports symbol='VIR_DOMAIN_SCHED_FIELD_INT' type='enum'/> <exports symbol='VIR_DOMAIN_SCHED_FIELD_ULLONG' type='enum'/> <exports symbol='VIR_CRED_REALM' type='enum'/> @@ -48,10 +57,15 @@ <exports symbol='VIR_JOB_UNBOUNDED' type='enum'/> <exports symbol='VIR_DOMAIN_CRASHED' type='enum'/> <exports symbol='VIR_DOMAIN_XML_INACTIVE' type='enum'/> + <exports symbol='VIR_STORAGE_VOL_FILE' type='enum'/> <exports symbol='VIR_DOMAIN_NONE' type='enum'/> + <exports symbol='VIR_STORAGE_VOL_DELETE_CLEAR' type='enum'/> <exports symbol='VIR_DOMAIN_NOSTATE' type='enum'/> <exports symbol='VIR_CRED_PASSPHRASE' type='enum'/> + <exports symbol='VIR_STORAGE_VOL_DELETE_NORMAL' type='enum'/> + <exports symbol='VIR_STORAGE_POOL_DEGRADED' type='enum'/> <exports symbol='VIR_DOMAIN_XML_SECURE' type='enum'/> + <exports symbol='VIR_STORAGE_POOL_INACTIVE' type='enum'/> <exports symbol='virDomainBlockStatsStruct' type='typedef'/> <exports symbol='virDomainMigrateFlags' type='typedef'/> <exports symbol='virNodeInfo' type='typedef'/> @@ -61,56 +75,81 @@ <exports symbol='virConnect' type='typedef'/> <exports symbol='virVcpuInfo' type='typedef'/> <exports symbol='virDomainInfo' type='typedef'/> + <exports symbol='virStoragePoolDeleteFlags' type='typedef'/> + <exports symbol='virStoragePool' type='typedef'/> + <exports symbol='virStoragePoolPtr' type='typedef'/> <exports symbol='virDomainInterfaceStatsStruct' type='typedef'/> <exports symbol='virConnectPtr' type='typedef'/> <exports symbol='virDomainState' type='typedef'/> <exports symbol='virDomain' type='typedef'/> <exports symbol='virDomainInterfaceStatsPtr' type='typedef'/> <exports symbol='virConnectAuthPtr' type='typedef'/> + <exports symbol='virStorageVolInfo' type='typedef'/> <exports symbol='virSchedParameterType' type='typedef'/> <exports symbol='virJobPtr' type='typedef'/> <exports symbol='virConnectCredentialPtr' type='typedef'/> + <exports symbol='virStoragePoolInfo' type='typedef'/> <exports symbol='virNodeInfoPtr' type='typedef'/> <exports symbol='virNetworkPtr' type='typedef'/> <exports symbol='virDomainInfoPtr' type='typedef'/> <exports symbol='virJob' type='typedef'/> + <exports symbol='virStorageVol' type='typedef'/> + <exports symbol='virStorageVolInfoPtr' type='typedef'/> <exports symbol='virSchedParameter' type='typedef'/> <exports symbol='virJobType' type='typedef'/> <exports symbol='virConnectFlags' type='typedef'/> <exports symbol='virSchedParameterPtr' type='typedef'/> + <exports symbol='virStorageVolPtr' type='typedef'/> <exports symbol='virVcpuState' type='typedef'/> <exports symbol='virJobState' type='typedef'/> + <exports symbol='virStorageVolDeleteFlags' type='typedef'/> <exports symbol='virJobInfoPtr' type='typedef'/> <exports symbol='virConnectAuth' type='typedef'/> <exports symbol='virConnectCredential' type='typedef'/> <exports symbol='virVcpuInfoPtr' type='typedef'/> + <exports symbol='virStoragePoolBuildFlags' type='typedef'/> <exports symbol='virDomainXMLFlags' type='typedef'/> + <exports symbol='virStorageVolType' type='typedef'/> <exports symbol='virDomainPtr' type='typedef'/> <exports symbol='virConnectCredentialType' type='typedef'/> + <exports symbol='virStoragePoolState' type='typedef'/> + <exports symbol='virStoragePoolInfoPtr' type='typedef'/> <exports symbol='virDomainCreateFlags' type='typedef'/> <exports symbol='_virDomainInfo' type='struct'/> <exports symbol='_virConnectAuth' type='struct'/> <exports symbol='_virVcpuInfo' type='struct'/> <exports symbol='_virDomainInterfaceStats' type='struct'/> + <exports symbol='_virStoragePoolInfo' type='struct'/> <exports symbol='_virConnectCredential' type='struct'/> + <exports symbol='_virStorageVolInfo' type='struct'/> <exports symbol='_virJobInfo' type='struct'/> <exports symbol='_virDomainBlockStats' type='struct'/> <exports symbol='_virNodeInfo' type='struct'/> <exports symbol='_virSchedParameter' type='struct'/> <exports symbol='virConnectAuthPtrDefault' type='variable'/> + <exports symbol='virStoragePoolGetXMLDesc' type='function'/> + <exports symbol='virStorageVolGetKey' type='function'/> <exports symbol='virConnectClose' type='function'/> <exports symbol='virDomainDefineXML' type='function'/> <exports symbol='virDomainShutdown' type='function'/> + <exports symbol='virConnectListStoragePools' type='function'/> <exports symbol='virGetVersion' type='function'/> <exports symbol='virNodeGetCellsFreeMemory' type='function'/> + <exports symbol='virStoragePoolSetAutostart' type='function'/> + <exports symbol='virStorageVolCreateXML' type='function'/> <exports symbol='virDomainGetSchedulerParameters' type='function'/> <exports symbol='virDomainLookupByUUIDString' type='function'/> <exports symbol='virConnectNumOfDefinedNetworks' type='function'/> <exports symbol='virConnectNumOfDomains' type='function'/> <exports symbol='virNetworkGetUUID' type='function'/> - <exports symbol='virDomainAttachDevice' type='function'/> + <exports symbol='virStoragePoolGetConnect' type='function'/> + <exports symbol='virConnectGetVersion' type='function'/> <exports symbol='virDomainFree' type='function'/> + <exports symbol='virStoragePoolGetName' type='function'/> <exports symbol='virDomainSetAutostart' type='function'/> + <exports symbol='virStoragePoolDefineXML' type='function'/> + <exports symbol='virStorageVolLookupByPath' type='function'/> + <exports symbol='virStorageVolLookupByName' type='function'/> <exports symbol='virDomainCreateLinux' type='function'/> <exports symbol='virDomainGetUUIDString' type='function'/> <exports symbol='virInitialize' type='function'/> @@ -119,11 +158,17 @@ <exports symbol='virNetworkCreate' type='function'/> <exports symbol='virDomainDestroy' type='function'/> <exports symbol='virConnectNumOfNetworks' type='function'/> + <exports symbol='virStoragePoolLookupByUUIDString' type='function'/> <exports symbol='virDomainGetXMLDesc' type='function'/> + <exports symbol='virStoragePoolGetUUID' type='function'/> + <exports symbol='virConnectDiscoverStoragePools' type='function'/> + <exports symbol='virStorageVolGetInfo' type='function'/> <exports symbol='virDomainGetInfo' type='function'/> <exports symbol='virNetworkDestroy' type='function'/> + <exports symbol='virStoragePoolLookupByName' type='function'/> <exports symbol='virNetworkGetAutostart' type='function'/> <exports symbol='virNetworkGetBridgeName' type='function'/> + <exports symbol='virStorageVolGetXMLDesc' type='function'/> <exports symbol='virDomainSetSchedulerParameters' type='function'/> <exports symbol='virConnectGetType' type='function'/> <exports symbol='virDomainSave' type='function'/> @@ -136,17 +181,23 @@ <exports symbol='virDomainGetMaxMemory' type='function'/> <exports symbol='virDomainSetMaxMemory' type='function'/> <exports symbol='virJobGetDomain' type='function'/> + <exports symbol='virStoragePoolFree' type='function'/> <exports symbol='virJobGetInfo' type='function'/> <exports symbol='virNetworkDefineXML' type='function'/> <exports symbol='virDomainBlockStats' type='function'/> <exports symbol='virConnectOpenAuth' type='function'/> + <exports symbol='virStoragePoolDelete' type='function'/> <exports symbol='virJobFree' type='function'/> <exports symbol='virNetworkCreateXMLJob' type='function'/> + <exports symbol='virStorageVolGetName' type='function'/> + <exports symbol='virStoragePoolGetAutostart' type='function'/> <exports symbol='virDomainGetAutostart' type='function'/> + <exports symbol='virStoragePoolListVolumes' type='function'/> <exports symbol='virConnectGetHostname' type='function'/> <exports symbol='virDomainRestoreJob' type='function'/> <exports symbol='virDomainGetName' type='function'/> <exports symbol='virNetworkGetXMLDesc' type='function'/> + <exports symbol='virConnectNumOfStoragePools' type='function'/> <exports symbol='virJobGetNetwork' type='function'/> <exports symbol='virNetworkGetName' type='function'/> <exports symbol='virConnectListDefinedDomains' type='function'/> @@ -154,15 +205,21 @@ <exports symbol='virDomainLookupByName' type='function'/> <exports symbol='virDomainPinVcpu' type='function'/> <exports symbol='virDomainRestore' type='function'/> + <exports symbol='virStorageVolGetPath' type='function'/> <exports symbol='virNetworkLookupByUUIDString' type='function'/> <exports symbol='virDomainLookupByID' type='function'/> + <exports symbol='virStorageVolFree' type='function'/> <exports symbol='virNetworkLookupByUUID' type='function'/> <exports symbol='virConnectListDefinedNetworks' type='function'/> <exports symbol='virDomainGetUUID' type='function'/> <exports symbol='virNetworkCreateXML' type='function'/> <exports symbol='virDomainGetVcpus' type='function'/> + <exports symbol='virStoragePoolCreateXML' type='function'/> + <exports symbol='virStoragePoolGetInfo' type='function'/> <exports symbol='virDomainResume' type='function'/> + <exports symbol='virStoragePoolRefresh' type='function'/> <exports symbol='virConnectNumOfDefinedDomains' type='function'/> + <exports symbol='virStorageVolLookupByKey' type='function'/> <exports symbol='virDomainUndefine' type='function'/> <exports symbol='virDomainReboot' type='function'/> <exports symbol='virNetworkGetUUIDString' type='function'/> @@ -171,23 +228,35 @@ <exports symbol='virDomainGetMaxVcpus' type='function'/> <exports symbol='virDomainGetSchedulerType' type='function'/> <exports symbol='virDomainDetachDevice' type='function'/> + <exports symbol='virStoragePoolNumOfVolumes' type='function'/> + <exports symbol='virStoragePoolGetUUIDString' type='function'/> <exports symbol='virDomainCoreDumpJob' type='function'/> <exports symbol='virNetworkCreateJob' type='function'/> + <exports symbol='virStoragePoolUndefine' type='function'/> <exports symbol='virConnectAuthCallbackPtr' type='function'/> - <exports symbol='virConnectGetVersion' type='function'/> + <exports symbol='virDomainAttachDevice' type='function'/> <exports symbol='virConnectGetURI' type='function'/> <exports symbol='virConnectOpenReadOnly' type='function'/> <exports symbol='virNetworkFree' type='function'/> + <exports symbol='virStoragePoolLookupByUUID' type='function'/> + <exports symbol='virStorageVolDelete' type='function'/> <exports symbol='virNetworkUndefine' type='function'/> + <exports symbol='virConnectListDefinedStoragePools' type='function'/> <exports symbol='virNetworkGetConnect' type='function'/> <exports symbol='virNodeGetFreeMemory' type='function'/> + <exports symbol='virStorageVolGetConnect' type='function'/> + <exports symbol='virStoragePoolDestroy' type='function'/> + <exports symbol='virStoragePoolLookupByVolume' type='function'/> <exports symbol='virDomainLookupByUUID' type='function'/> <exports symbol='virDomainGetOSType' type='function'/> <exports symbol='virJobCancel' type='function'/> + <exports symbol='virStoragePoolBuild' type='function'/> <exports symbol='virConnectGetMaxVcpus' type='function'/> <exports symbol='virDomainSaveJob' type='function'/> <exports symbol='virDomainGetConnect' type='function'/> + <exports symbol='virConnectNumOfDefinedStoragePools' type='function'/> <exports symbol='virConnectOpen' type='function'/> + <exports symbol='virStoragePoolCreate' type='function'/> <exports symbol='virDomainCreateLinuxJob' type='function'/> <exports symbol='virDomainSetVcpus' type='function'/> <exports symbol='virDomainGetID' type='function'/> @@ -225,6 +294,7 @@ <exports symbol='VIR_ERR_INVALID_NETWORK' type='enum'/> <exports symbol='VIR_ERR_AUTH_FAILED' type='enum'/> <exports symbol='VIR_ERR_OPERATION_DENIED' type='enum'/> + <exports symbol='VIR_FROM_STORAGE' type='enum'/> <exports symbol='VIR_ERR_NO_KERNEL' type='enum'/> <exports symbol='VIR_ERR_GNUTLS_ERROR' type='enum'/> <exports symbol='VIR_ERR_POST_FAILED' type='enum'/> @@ -246,12 +316,15 @@ <exports symbol='VIR_ERR_NO_SOURCE' type='enum'/> <exports symbol='VIR_ERR_NO_TARGET' type='enum'/> <exports symbol='VIR_ERR_NETWORK_EXIST' type='enum'/> + <exports symbol='VIR_WAR_NO_STORAGE' type='enum'/> <exports symbol='VIR_ERR_WRITE_FAILED' type='enum'/> <exports symbol='VIR_ERR_INTERNAL_ERROR' type='enum'/> <exports symbol='VIR_ERR_CONF_SYNTAX' type='enum'/> + <exports symbol='VIR_ERR_INVALID_STORAGE_POOL' type='enum'/> <exports symbol='VIR_FROM_REMOTE' type='enum'/> <exports symbol='VIR_ERR_NO_SUPPORT' type='enum'/> <exports symbol='VIR_FROM_XEND' type='enum'/> + <exports symbol='VIR_ERR_INVALID_STORAGE_VOL' type='enum'/> <exports symbol='VIR_FROM_PROXY' type='enum'/> <exports symbol='VIR_ERR_NO_NETWORK' type='enum'/> <exports symbol='VIR_ERR_NO_DOMAIN' type='enum'/> @@ -362,7 +435,7 @@ <enum name='VIR_DOMAIN_SHUTOFF' file='libvirt' value='5' type='virDomainState' info='the domain is shut off'/> <enum name='VIR_DOMAIN_XML_INACTIVE' file='libvirt' value='2' type='virDomainXMLFlags' info=' dump inactive domain informations'/> <enum name='VIR_DOMAIN_XML_SECURE' file='libvirt' value='1' type='virDomainXMLFlags' info='dump security sensitive informations too'/> - <enum name='VIR_ERR_AUTH_FAILED' file='virterror' value='45' type='virErrorNumber' info=' authentication failed'/> + <enum name='VIR_ERR_AUTH_FAILED' file='virterror' value='45' type='virErrorNumber' info='authentication failed'/> <enum name='VIR_ERR_CALL_FAILED' file='virterror' value='26' type='virErrorNumber' info='not supported by the drivers (DEPRECATED)'/> <enum name='VIR_ERR_CONF_SYNTAX' file='virterror' value='33' type='virErrorNumber' info='failed to parse the syntax of a conf file'/> <enum name='VIR_ERR_DOM_EXIST' file='virterror' value='28' type='virErrorNumber' info='the domain already exist'/> @@ -377,6 +450,8 @@ <enum name='VIR_ERR_INVALID_DOMAIN' file='virterror' value='7' type='virErrorNumber' info='invalid domain object'/> <enum name='VIR_ERR_INVALID_MAC' file='virterror' value='44' type='virErrorNumber' info='invalid MAC adress'/> <enum name='VIR_ERR_INVALID_NETWORK' file='virterror' value='36' type='virErrorNumber' info='invalid network object'/> + <enum name='VIR_ERR_INVALID_STORAGE_POOL' file='virterror' value='46' type='virErrorNumber' info='invalid storage pool object'/> + <enum name='VIR_ERR_INVALID_STORAGE_VOL' file='virterror' value='47' type='virErrorNumber' info='invalid storage vol object'/> <enum name='VIR_ERR_NETWORK_EXIST' file='virterror' value='37' type='virErrorNumber' info='the network already exist'/> <enum name='VIR_ERR_NONE' file='virterror' value='0' type='virErrorLevel'/> <enum name='VIR_ERR_NO_CONNECT' file='virterror' value='5' type='virErrorNumber' info='can't connect to hypervisor'/> @@ -420,7 +495,8 @@ <enum name='VIR_FROM_REMOTE' file='virterror' value='13' type='virErrorDomain' info='Error from remote driver'/> <enum name='VIR_FROM_RPC' file='virterror' value='7' type='virErrorDomain' info='Error in the XML-RPC code'/> <enum name='VIR_FROM_SEXPR' file='virterror' value='4' type='virErrorDomain' info='Error in the S-Epression code'/> - <enum name='VIR_FROM_STATS_LINUX' file='virterror' value='16' type='virErrorDomain' info=' Error in the Linux Stats code'/> + <enum name='VIR_FROM_STATS_LINUX' file='virterror' value='16' type='virErrorDomain' info='Error in the Linux Stats code'/> + <enum name='VIR_FROM_STORAGE' file='virterror' value='17' type='virErrorDomain' info=' Error from storage driver'/> <enum name='VIR_FROM_TEST' file='virterror' value='12' type='virErrorDomain' info='Error from test driver'/> <enum name='VIR_FROM_XEN' file='virterror' value='1' type='virErrorDomain' info='Error at Xen hypervisor layer'/> <enum name='VIR_FROM_XEND' file='virterror' value='2' type='virErrorDomain' info='Error at connection with xend daemon'/> @@ -434,10 +510,25 @@ <enum name='VIR_JOB_RUNNING' file='libvirt' value='0' type='virJobState' info='Still active'/> <enum name='VIR_JOB_UNBOUNDED' file='libvirt' value='1' type='virJobType' info=' unknown completion percent'/> <enum name='VIR_MIGRATE_LIVE' file='libvirt' value='1' type='virDomainMigrateFlags' info=' live migration'/> + <enum name='VIR_STORAGE_POOL_BUILDING' file='libvirt' value='1' type='virStoragePoolState' info='Initializing pool, not available'/> + <enum name='VIR_STORAGE_POOL_BUILD_EXTEND' file='libvirt' value='2' type='virStoragePoolBuildFlags' info=' Extend existing pool'/> + <enum name='VIR_STORAGE_POOL_BUILD_NEW' file='libvirt' value='0' type='virStoragePoolBuildFlags' info='Regular build from scratch'/> + <enum name='VIR_STORAGE_POOL_BUILD_REPAIR' file='libvirt' value='1' type='virStoragePoolBuildFlags' info='Repair / reinitialize'/> + <enum name='VIR_STORAGE_POOL_DEGRADED' file='libvirt' value='3' type='virStoragePoolState' info=' Running degraded'/> + <enum name='VIR_STORAGE_POOL_DELETE_CLEAR' file='libvirt' value='1' type='virStoragePoolDeleteFlags' info=' Clear all data to zeros (slow)'/> + <enum name='VIR_STORAGE_POOL_DELETE_NORMAL' file='libvirt' value='0' type='virStoragePoolDeleteFlags' info='Delete metadata only (fast)'/> + <enum name='VIR_STORAGE_POOL_INACTIVE' file='libvirt' value='0' type='virStoragePoolState' info='Not running'/> + <enum name='VIR_STORAGE_POOL_RUNNING' file='libvirt' value='2' type='virStoragePoolState' info='Running normally'/> + <enum name='VIR_STORAGE_VOL_BLOCK' file='libvirt' value='1' type='virStorageVolType' info='Block based volumes'/> + <enum name='VIR_STORAGE_VOL_DELETE_CLEAR' file='libvirt' value='1' type='virStorageVolDeleteFlags' info=' Clear all data to zeros (slow)'/> + <enum name='VIR_STORAGE_VOL_DELETE_NORMAL' file='libvirt' value='0' type='virStorageVolDeleteFlags' info='Delete metadata only (fast)'/> + <enum name='VIR_STORAGE_VOL_FILE' file='libvirt' value='0' type='virStorageVolType' info='Regular file based volumes'/> + <enum name='VIR_STORAGE_VOL_VIRTUAL' file='libvirt' value='2' type='virStorageVolType' info=' Volumes which aren't assignable to guests'/> <enum name='VIR_VCPU_BLOCKED' file='libvirt' value='2' type='virVcpuState' info=' the virtual CPU is blocked on resource'/> <enum name='VIR_VCPU_OFFLINE' file='libvirt' value='0' type='virVcpuState' info='the virtual CPU is offline'/> <enum name='VIR_VCPU_RUNNING' file='libvirt' value='1' type='virVcpuState' info='the virtual CPU is running'/> <enum name='VIR_WAR_NO_NETWORK' file='virterror' value='41' type='virErrorNumber' info='failed to start network'/> + <enum name='VIR_WAR_NO_STORAGE' file='virterror' value='48' type='virErrorNumber' info=' failed to start storage'/> <struct name='virConnect' file='libvirt' type='struct _virConnect'/> <struct name='virConnectAuth' file='libvirt' type='struct _virConnectAuth'> <field name='credtype' type='int *' info=' List of supported virConnectCredentialType values'/> @@ -560,6 +651,33 @@ <info>a virSchedParameterPtr is a pointer to a virSchedParameter structure.</info> </typedef> <typedef name='virSchedParameterType' file='libvirt' type='enum'/> + <struct name='virStoragePool' file='libvirt' type='struct _virStoragePool'/> + <typedef name='virStoragePoolBuildFlags' file='libvirt' type='enum'/> + <typedef name='virStoragePoolDeleteFlags' file='libvirt' type='enum'/> + <struct name='virStoragePoolInfo' file='libvirt' type='struct _virStoragePoolInfo'> + <field name='state' type='int' info=' virStoragePoolState flags'/> + <field name='capacity' type='unsigned long long' info=' Logical size bytes'/> + <field name='allocation' type='unsigned long long' info=' Current allocation bytes'/> + <field name='available' type='unsigned long long' info=' Remaining free space bytes'/> + </struct> + <typedef name='virStoragePoolInfoPtr' file='libvirt' type='virStoragePoolInfo *'/> + <typedef name='virStoragePoolPtr' file='libvirt' type='virStoragePool *'> + <info>a virStoragePoolPtr is pointer to a virStoragePool private structure, this is the type used to reference a storage pool in the API.</info> + </typedef> + <typedef name='virStoragePoolState' file='libvirt' type='enum'/> + <struct name='virStorageVol' file='libvirt' type='struct _virStorageVol'/> + <typedef name='virStorageVolDeleteFlags' file='libvirt' type='enum'/> + <struct name='virStorageVolInfo' file='libvirt' type='struct _virStorageVolInfo'> + <field name='type' type='int' info=' virStorageVolType flags'/> + <field name='capacity' type='unsigned long long' info=' Logical size bytes'/> + <field name='allocation' type='unsigned long long' info=' Current allocation bytes'/> + <field name='available' type='unsigned long long' info=' Remaining free space bytes'/> + </struct> + <typedef name='virStorageVolInfoPtr' file='libvirt' type='virStorageVolInfo *'/> + <typedef name='virStorageVolPtr' file='libvirt' type='virStorageVol *'> + <info>a virStorageVolPtr is pointer to a virStorageVol private structure, this is the type used to reference a storage volume in the API.</info> + </typedef> + <typedef name='virStorageVolType' file='libvirt' type='enum'/> <struct name='virVcpuInfo' file='libvirt' type='struct _virVcpuInfo'> <field name='number' type='unsigned int' info=' virtual CPU number'/> <field name='state' type='int' info=' value from virVcpuState'/> @@ -604,6 +722,15 @@ <return type='int' info='0 in case of success or -1 in case of error.'/> <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> </function> + <function name='virConnectDiscoverStoragePools' file='libvirt' module='libvirt'> + <info>Talks to a host and attempt to auto-discover a set of exported storage pools available. eg For iSCSI this would be a set of iSCSI targets. For NFS this would be a list of exported paths. Each discovered pool is returned as an XML document suitable for feeding into virStoragePoolCreateXML</info> + <return type='int' info='number of discovered pools, or -1 on error'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + <arg name='hostname' type='const char *' info='host to discover pools on'/> + <arg name='type' type='const char *' info='type of storge pool to discover'/> + <arg name='flags' type='unsigned int' info='flags for discovery (unused, pass 0)'/> + <arg name='xmlDesc' type='char ** *' info='return array of of XML documents, one per pool'/> + </function> <function name='virConnectGetCapabilities' file='libvirt' module='libvirt'> <info>Provides capabilities of the hypervisor / driver.</info> <return type='char *' info='NULL in case of error, or an XML string defining the capabilities. The client must free the returned string after use.'/> @@ -650,6 +777,13 @@ <arg name='names' type='char ** const' info='pointer to an array to store the names'/> <arg name='maxnames' type='int' info='size of the array'/> </function> + <function name='virConnectListDefinedStoragePools' file='libvirt' module='libvirt'> + <info>Provides the list of names of inactive storage pools upto maxnames. If there are more than maxnames, the remaining names will be silently ignored.</info> + <return type='int' info='0 on success, -1 on error'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + <arg name='names' type='char ** const' info='array of char * to fill with pool names (allocated by caller)'/> + <arg name='maxnames' type='int' info='size of the names array'/> + </function> <function name='virConnectListDomains' file='libvirt' module='libvirt'> <info>Collect the list of active domains, and store their ID in @maxids</info> <return type='int' info='the number of domain found or -1 in case of error'/> @@ -664,6 +798,13 @@ <arg name='names' type='char ** const' info='array to collect the list of names of active networks'/> <arg name='maxnames' type='int' info='size of @names'/> </function> + <function name='virConnectListStoragePools' file='libvirt' module='libvirt'> + <info>Provides the list of names of active storage pools upto maxnames. If there are more than maxnames, the remaining names will be silently ignored.</info> + <return type='int' info='0 on success, -1 on error'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + <arg name='names' type='char ** const' info='array of char * to fill with pool names (allocated by caller)'/> + <arg name='maxnames' type='int' info='size of the names array'/> + </function> <function name='virConnectNumOfDefinedDomains' file='libvirt' module='libvirt'> <info>Provides the number of inactive domains.</info> <return type='int' info='the number of domain found or -1 in case of error'/> @@ -674,6 +815,11 @@ <return type='int' info='the number of networks found or -1 in case of error'/> <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> </function> + <function name='virConnectNumOfDefinedStoragePools' file='libvirt' module='libvirt'> + <info>Provides the number of inactive storage pools</info> + <return type='int' info='the number of pools found, or -1 on error'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + </function> <function name='virConnectNumOfDomains' file='libvirt' module='libvirt'> <info>Provides the number of active domains.</info> <return type='int' info='the number of domain found or -1 in case of error'/> @@ -683,6 +829,11 @@ <info>Provides the number of active networks.</info> <return type='int' info='the number of network found or -1 in case of error'/> <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> + </function> + <function name='virConnectNumOfStoragePools' file='libvirt' module='libvirt'> + <info>Provides the number of active storage pools</info> + <return type='int' info='the number of pools found, or -1 on error'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> </function> <function name='virConnectOpen' file='libvirt' module='libvirt'> <info>This function should be called first to get a connection to the Hypervisor and xen store</info> @@ -1197,5 +1348,204 @@ <arg name='userData' type='void *' info='pointer to the user data provided in the handler callback'/> <arg name='handler' type='virErrorFunc' info='the function to get called in case of error or NULL'/> </function> + <function name='virStoragePoolBuild' file='libvirt' module='libvirt'> + <info>Build the underlying storage pool</info> + <return type='int' info='0 on success, or -1 upon failure'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='flags' type='unsigned int' info=''/> + </function> + <function name='virStoragePoolCreate' file='libvirt' module='libvirt'> + <info>Starts an inactive storage pool</info> + <return type='int' info='0 on success, or -1 if it could not be started'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + </function> + <function name='virStoragePoolCreateXML' file='libvirt' module='libvirt'> + <info>Create a new storage based on its XML description. The pool is not persitent, so its definition will disappear when it is destroyed, or if the host is restarted</info> + <return type='virStoragePoolPtr' info='a virStoragePoolPtr object, or NULL if creation failed'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + <arg name='xmlDesc' type='const char *' info='XML description for new pool'/> + </function> + <function name='virStoragePoolDefineXML' file='libvirt' module='libvirt'> + <info>Define a new inactive storage pool based on its XML description. The pool is persitent, until explicitly undefined.</info> + <return type='virStoragePoolPtr' info='a virStoragePoolPtr object, or NULL if creation failed'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + <arg name='xml' type='const char *' info='XML description for new pool'/> + </function> + <function name='virStoragePoolDelete' file='libvirt' module='libvirt'> + <info>Delete the underlying pool resources. This is a non-recoverable operation.</info> + <return type='int' info='0 on success, or -1 if it could not be obliterate'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='flags' type='unsigned int' info='flags for obliteration process'/> + </function> + <function name='virStoragePoolDestroy' file='libvirt' module='libvirt'> + <info>Destroy an active storage pool. The virStoragePoolPtr object should not be used after this method returns successfully as it has been free'd</info> + <return type='int' info='0 on success, or -1 if it could not be destroyed'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + </function> + <function name='virStoragePoolFree' file='libvirt' module='libvirt'> + <info>Free a storage pool object</info> + <return type='int' info='0 on success, or -1 if it could not be free'd.'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + </function> + <function name='virStoragePoolGetAutostart' file='libvirt' module='libvirt'> + <info>Fetches the value of the autostart flag, which determines whether the pool is automatically started at boot time</info> + <return type='int' info='0 on success, -1 on failure'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='autostart' type='int *' info='location in which to store autostart flag'/> + </function> + <function name='virStoragePoolGetConnect' file='libvirt' module='libvirt'> + <info>Provides the connection pointer associated with a storage poolk. The reference counter on the connection is not increased by this call. WARNING: When writing libvirt bindings in other languages, do not use this function. Instead, store the connection and the pool object together.</info> + <return type='virConnectPtr' info='the virConnectPtr or NULL in case of failure.'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to a poool'/> + </function> + <function name='virStoragePoolGetInfo' file='libvirt' module='libvirt'> + <info>Get volatile information about the storage pool such as free space / usage summary</info> + <return type='int' info='0 on success, or -1 on failure.'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='info' type='virStoragePoolInfoPtr' info='pointer at which to store info'/> + </function> + <function name='virStoragePoolGetName' file='libvirt' module='libvirt'> + <info>Fetch the locally unique name of the storage pool</info> + <return type='const char *' info='the name of the pool, or NULL on error'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + </function> + <function name='virStoragePoolGetUUID' file='libvirt' module='libvirt'> + <info>Fetch the globally unique ID of the storage pool</info> + <return type='int' info='0 on success, or -1 on error;'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='uuid' type='unsigned char *' info='buffer of VIR_UUID_BUFLEN bytes in size'/> + </function> + <function name='virStoragePoolGetUUIDString' file='libvirt' module='libvirt'> + <info>Fetch the globally unique ID of the storage pool as a string</info> + <return type='int' info='0 on success, or -1 on error;'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='buf' type='char *' info='buffer of VIR_UUID_STRING_BUFLEN bytes in size'/> + </function> + <function name='virStoragePoolGetXMLDesc' file='libvirt' module='libvirt'> + <info>Fetch an XML document describing all aspects of the storage pool. This is suitable for later feeding back into the virStoragePoolCreateXML method.</info> + <return type='char *' info='a XML document, or NULL on error'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='flags' type='unsigned int' info='flags for XML format options (unused, pass 0)'/> + </function> + <function name='virStoragePoolListVolumes' file='libvirt' module='libvirt'> + <info>Fetch list of storage volume names, limiting to at most maxnames.</info> + <return type='int' info='the number of names fetched, or -1 on error'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='names' type='char ** const' info='array in which to storage volume names'/> + <arg name='maxnames' type='int' info='size of names array'/> + </function> + <function name='virStoragePoolLookupByName' file='libvirt' module='libvirt'> + <info>Fetch a storage pool based on its unique name</info> + <return type='virStoragePoolPtr' info='a virStoragePoolPtr object, or NULL if no matching pool is found'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + <arg name='name' type='const char *' info='name of pool to fetch'/> + </function> + <function name='virStoragePoolLookupByUUID' file='libvirt' module='libvirt'> + <info>Fetch a storage pool based on its globally unique id</info> + <return type='virStoragePoolPtr' info='a virStoragePoolPtr object, or NULL if no matching pool is found'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + <arg name='uuid' type='const unsigned char *' info='globally unique id of pool to fetch'/> + </function> + <function name='virStoragePoolLookupByUUIDString' file='libvirt' module='libvirt'> + <info>Fetch a storage pool based on its globally unique id</info> + <return type='virStoragePoolPtr' info='a virStoragePoolPtr object, or NULL if no matching pool is found'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + <arg name='uuidstr' type='const char *' info='globally unique id of pool to fetch'/> + </function> + <function name='virStoragePoolLookupByVolume' file='libvirt' module='libvirt'> + <info>Fetch a storage pool which contains a particular volume</info> + <return type='virStoragePoolPtr' info='a virStoragePoolPtr object, or NULL if no matching pool is found'/> + <arg name='vol' type='virStorageVolPtr' info='pointer to storage volume'/> + </function> + <function name='virStoragePoolNumOfVolumes' file='libvirt' module='libvirt'> + <info>Fetch the number of storage volumes within a pool</info> + <return type='int' info='the number of storage pools, or -1 on failure'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + </function> + <function name='virStoragePoolRefresh' file='libvirt' module='libvirt'> + <info>Request that the pool refresh its list of volumes. This may involve communicating with a remote server, and/or initializing new devices at the OS layer</info> + <return type='int' info='0 if the volume list was refreshed, -1 on failure'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='flags' type='unsigned int' info='flags to control refresh behaviour (currently unused, use 0)'/> + </function> + <function name='virStoragePoolSetAutostart' file='libvirt' module='libvirt'> + <info>Sets the autostart flag</info> + <return type='int' info='0 on success, -1 on failure'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='autostart' type='int' info='new flag setting'/> + </function> + <function name='virStoragePoolUndefine' file='libvirt' module='libvirt'> + <info>Undefine an inactive storage pool</info> + <return type='int' info='a virStoragePoolPtr object, or NULL if creation failed'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + </function> + <function name='virStorageVolCreateXML' file='libvirt' module='libvirt'> + <info>Create a storage volume within a pool based on an XML description. Not all pools support creation of volumes</info> + <return type='virStorageVolPtr' info='the storage volume, or NULL on error'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='xmldesc' type='const char *' info='description of volume to create'/> + <arg name='flags' type='unsigned int' info='flags for creation (unused, pass 0)'/> + </function> + <function name='virStorageVolDelete' file='libvirt' module='libvirt'> + <info>Delete the storage volume from the pool</info> + <return type='int' info='0 on success, or -1 on error'/> + <arg name='vol' type='virStorageVolPtr' info='pointer to storage volume'/> + <arg name='flags' type='unsigned int' info=''/> + </function> + <function name='virStorageVolFree' file='libvirt' module='libvirt'> + <info>Release the storage volume handle. The underlying storage volume contains to exist</info> + <return type='int' info='0 on success, or -1 on error'/> + <arg name='vol' type='virStorageVolPtr' info='pointer to storage volume'/> + </function> + <function name='virStorageVolGetConnect' file='libvirt' module='libvirt'> + <info>Provides the connection pointer associated with a storage poolk. The reference counter on the connection is not increased by this call. WARNING: When writing libvirt bindings in other languages, do not use this function. Instead, store the connection and the pool object together.</info> + <return type='virConnectPtr' info='the virConnectPtr or NULL in case of failure.'/> + <arg name='vol' type='virStorageVolPtr' info='pointer to a poool'/> + </function> + <function name='virStorageVolGetInfo' file='libvirt' module='libvirt'> + <info>Fetches volatile information about the storage volume such as its current allocation</info> + <return type='int' info='0 on success, or -1 on failure'/> + <arg name='vol' type='virStorageVolPtr' info='pointer to storage volume'/> + <arg name='info' type='virStorageVolInfoPtr' info='pointer at which to store info'/> + </function> + <function name='virStorageVolGetKey' file='libvirt' module='libvirt'> + <info>Fetch the storage volume key. This is globally unique, so the same volume will hve the same key no matter what host it is accessed from</info> + <return type='const char *' info='the volume key, or NULL on error'/> + <arg name='vol' type='virStorageVolPtr' info='pointer to storage volume'/> + </function> + <function name='virStorageVolGetName' file='libvirt' module='libvirt'> + <info>Fetch the storage volume name. This is unique within the scope of a pool</info> + <return type='const char *' info='the volume name, or NULL on error'/> + <arg name='vol' type='virStorageVolPtr' info='pointer to storage volume'/> + </function> + <function name='virStorageVolGetPath' file='libvirt' module='libvirt'> + <info>Fetch the storage volume path. Depending on the pool configuration this is either persistent across hosts, or dynamically assigned at pool startup. Consult pool documentation for information on getting the persistent naming</info> + <return type='char *' info='the storage volume path, or NULL on error'/> + <arg name='vol' type='virStorageVolPtr' info='pointer to storage volume'/> + </function> + <function name='virStorageVolGetXMLDesc' file='libvirt' module='libvirt'> + <info>Fetch an XML document describing all aspects of the storage volume</info> + <return type='char *' info='the XML document, or NULL on error'/> + <arg name='vol' type='virStorageVolPtr' info='pointer to storage volume'/> + <arg name='flags' type='unsigned int' info='flags for XML generation (unused, pass 0)'/> + </function> + <function name='virStorageVolLookupByKey' file='libvirt' module='libvirt'> + <info>Fetch a pointer to a storage volume based on its globally unique key</info> + <return type='virStorageVolPtr' info='a storage volume, or NULL if not found / error'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + <arg name='key' type='const char *' info='globally unique key'/> + </function> + <function name='virStorageVolLookupByName' file='libvirt' module='libvirt'> + <info>Fetch a pointer to a storage volume based on its name within a pool</info> + <return type='virStorageVolPtr' info='a storage volume, or NULL if not found / error'/> + <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/> + <arg name='name' type='const char *' info='name of storage volume'/> + </function> + <function name='virStorageVolLookupByPath' file='libvirt' module='libvirt'> + <info>Fetch a pointer to a storage volume based on its locally (host) unique path</info> + <return type='virStorageVolPtr' info='a storage volume, or NULL if not found / error'/> + <arg name='conn' type='virConnectPtr' info='pointer to hypervisor connection'/> + <arg name='path' type='const char *' info='locally unique path'/> + </function> </symbols> </api> diff -r 171b14380600 docs/libvirt-refs.xml --- a/docs/libvirt-refs.xml Wed Jan 30 17:30:34 2008 -0500 +++ b/docs/libvirt-refs.xml Wed Jan 30 17:45:30 2008 -0500 @@ -47,6 +47,8 @@ <reference name='VIR_ERR_INVALID_DOMAIN' href='html/libvirt-virterror.html#VIR_ERR_INVALID_DOMAIN'/> <reference name='VIR_ERR_INVALID_MAC' href='html/libvirt-virterror.html#VIR_ERR_INVALID_MAC'/> <reference name='VIR_ERR_INVALID_NETWORK' href='html/libvirt-virterror.html#VIR_ERR_INVALID_NETWORK'/> + <reference name='VIR_ERR_INVALID_STORAGE_POOL' href='html/libvirt-virterror.html#VIR_ERR_INVALID_STORAGE_POOL'/> + <reference name='VIR_ERR_INVALID_STORAGE_VOL' href='html/libvirt-virterror.html#VIR_ERR_INVALID_STORAGE_VOL'/> <reference name='VIR_ERR_NETWORK_EXIST' href='html/libvirt-virterror.html#VIR_ERR_NETWORK_EXIST'/> <reference name='VIR_ERR_NONE' href='html/libvirt-virterror.html#VIR_ERR_NONE'/> <reference name='VIR_ERR_NO_CONNECT' href='html/libvirt-virterror.html#VIR_ERR_NO_CONNECT'/> @@ -91,6 +93,7 @@ <reference name='VIR_FROM_RPC' href='html/libvirt-virterror.html#VIR_FROM_RPC'/> <reference name='VIR_FROM_SEXPR' href='html/libvirt-virterror.html#VIR_FROM_SEXPR'/> <reference name='VIR_FROM_STATS_LINUX' href='html/libvirt-virterror.html#VIR_FROM_STATS_LINUX'/> + <reference name='VIR_FROM_STORAGE' href='html/libvirt-virterror.html#VIR_FROM_STORAGE'/> <reference name='VIR_FROM_TEST' href='html/libvirt-virterror.html#VIR_FROM_TEST'/> <reference name='VIR_FROM_XEN' href='html/libvirt-virterror.html#VIR_FROM_XEN'/> <reference name='VIR_FROM_XEND' href='html/libvirt-virterror.html#VIR_FROM_XEND'/> @@ -106,6 +109,20 @@ <reference name='VIR_JOB_UNBOUNDED' href='html/libvirt-libvirt.html#VIR_JOB_UNBOUNDED'/> <reference name='VIR_MIGRATE_LIVE' href='html/libvirt-libvirt.html#VIR_MIGRATE_LIVE'/> <reference name='VIR_NODEINFO_MAXCPUS' href='html/libvirt-libvirt.html#VIR_NODEINFO_MAXCPUS'/> + <reference name='VIR_STORAGE_POOL_BUILDING' href='html/libvirt-libvirt.html#VIR_STORAGE_POOL_BUILDING'/> + <reference name='VIR_STORAGE_POOL_BUILD_EXTEND' href='html/libvirt-libvirt.html#VIR_STORAGE_POOL_BUILD_EXTEND'/> + <reference name='VIR_STORAGE_POOL_BUILD_NEW' href='html/libvirt-libvirt.html#VIR_STORAGE_POOL_BUILD_NEW'/> + <reference name='VIR_STORAGE_POOL_BUILD_REPAIR' href='html/libvirt-libvirt.html#VIR_STORAGE_POOL_BUILD_REPAIR'/> + <reference name='VIR_STORAGE_POOL_DEGRADED' href='html/libvirt-libvirt.html#VIR_STORAGE_POOL_DEGRADED'/> + <reference name='VIR_STORAGE_POOL_DELETE_CLEAR' href='html/libvirt-libvirt.html#VIR_STORAGE_POOL_DELETE_CLEAR'/> + <reference name='VIR_STORAGE_POOL_DELETE_NORMAL' href='html/libvirt-libvirt.html#VIR_STORAGE_POOL_DELETE_NORMAL'/> + <reference name='VIR_STORAGE_POOL_INACTIVE' href='html/libvirt-libvirt.html#VIR_STORAGE_POOL_INACTIVE'/> + <reference name='VIR_STORAGE_POOL_RUNNING' href='html/libvirt-libvirt.html#VIR_STORAGE_POOL_RUNNING'/> + <reference name='VIR_STORAGE_VOL_BLOCK' href='html/libvirt-libvirt.html#VIR_STORAGE_VOL_BLOCK'/> + <reference name='VIR_STORAGE_VOL_DELETE_CLEAR' href='html/libvirt-libvirt.html#VIR_STORAGE_VOL_DELETE_CLEAR'/> + <reference name='VIR_STORAGE_VOL_DELETE_NORMAL' href='html/libvirt-libvirt.html#VIR_STORAGE_VOL_DELETE_NORMAL'/> + <reference name='VIR_STORAGE_VOL_FILE' href='html/libvirt-libvirt.html#VIR_STORAGE_VOL_FILE'/> + <reference name='VIR_STORAGE_VOL_VIRTUAL' href='html/libvirt-libvirt.html#VIR_STORAGE_VOL_VIRTUAL'/> <reference name='VIR_UNUSE_CPU' href='html/libvirt-libvirt.html#VIR_UNUSE_CPU'/> <reference name='VIR_USE_CPU' href='html/libvirt-libvirt.html#VIR_USE_CPU'/> <reference name='VIR_UUID_BUFLEN' href='html/libvirt-libvirt.html#VIR_UUID_BUFLEN'/> @@ -114,6 +131,7 @@ <reference name='VIR_VCPU_OFFLINE' href='html/libvirt-libvirt.html#VIR_VCPU_OFFLINE'/> <reference name='VIR_VCPU_RUNNING' href='html/libvirt-libvirt.html#VIR_VCPU_RUNNING'/> <reference name='VIR_WAR_NO_NETWORK' href='html/libvirt-virterror.html#VIR_WAR_NO_NETWORK'/> + <reference name='VIR_WAR_NO_STORAGE' href='html/libvirt-virterror.html#VIR_WAR_NO_STORAGE'/> <reference name='_virConnectAuth' href='html/libvirt-libvirt.html#_virConnectAuth'/> <reference name='_virConnectCredential' href='html/libvirt-libvirt.html#_virConnectCredential'/> <reference name='_virDomainBlockStats' href='html/libvirt-libvirt.html#_virDomainBlockStats'/> @@ -123,6 +141,8 @@ <reference name='_virJobInfo' href='html/libvirt-libvirt.html#_virJobInfo'/> <reference name='_virNodeInfo' href='html/libvirt-libvirt.html#_virNodeInfo'/> <reference name='_virSchedParameter' href='html/libvirt-libvirt.html#_virSchedParameter'/> + <reference name='_virStoragePoolInfo' href='html/libvirt-libvirt.html#_virStoragePoolInfo'/> + <reference name='_virStorageVolInfo' href='html/libvirt-libvirt.html#_virStorageVolInfo'/> <reference name='_virVcpuInfo' href='html/libvirt-libvirt.html#_virVcpuInfo'/> <reference name='virConnCopyLastError' href='html/libvirt-virterror.html#virConnCopyLastError'/> <reference name='virConnGetLastError' href='html/libvirt-virterror.html#virConnGetLastError'/> @@ -137,6 +157,7 @@ <reference name='virConnectCredential' href='html/libvirt-libvirt.html#virConnectCredential'/> <reference name='virConnectCredentialPtr' href='html/libvirt-libvirt.html#virConnectCredentialPtr'/> <reference name='virConnectCredentialType' href='html/libvirt-libvirt.html#virConnectCredentialType'/> + <reference name='virConnectDiscoverStoragePools' href='html/libvirt-libvirt.html#virConnectDiscoverStoragePools'/> <reference name='virConnectFlags' href='html/libvirt-libvirt.html#virConnectFlags'/> <reference name='virConnectGetCapabilities' href='html/libvirt-libvirt.html#virConnectGetCapabilities'/> <reference name='virConnectGetHostname' href='html/libvirt-libvirt.html#virConnectGetHostname'/> @@ -146,12 +167,16 @@ <reference name='virConnectGetVersion' href='html/libvirt-libvirt.html#virConnectGetVersion'/> <reference name='virConnectListDefinedDomains' href='html/libvirt-libvirt.html#virConnectListDefinedDomains'/> <reference name='virConnectListDefinedNetworks' href='html/libvirt-libvirt.html#virConnectListDefinedNetworks'/> + <reference name='virConnectListDefinedStoragePools' href='html/libvirt-libvirt.html#virConnectListDefinedStoragePools'/> <reference name='virConnectListDomains' href='html/libvirt-libvirt.html#virConnectListDomains'/> <reference name='virConnectListNetworks' href='html/libvirt-libvirt.html#virConnectListNetworks'/> + <reference name='virConnectListStoragePools' href='html/libvirt-libvirt.html#virConnectListStoragePools'/> <reference name='virConnectNumOfDefinedDomains' href='html/libvirt-libvirt.html#virConnectNumOfDefinedDomains'/> <reference name='virConnectNumOfDefinedNetworks' href='html/libvirt-libvirt.html#virConnectNumOfDefinedNetworks'/> + <reference name='virConnectNumOfDefinedStoragePools' href='html/libvirt-libvirt.html#virConnectNumOfDefinedStoragePools'/> <reference name='virConnectNumOfDomains' href='html/libvirt-libvirt.html#virConnectNumOfDomains'/> <reference name='virConnectNumOfNetworks' href='html/libvirt-libvirt.html#virConnectNumOfNetworks'/> + <reference name='virConnectNumOfStoragePools' href='html/libvirt-libvirt.html#virConnectNumOfStoragePools'/> <reference name='virConnectOpen' href='html/libvirt-libvirt.html#virConnectOpen'/> <reference name='virConnectOpenAuth' href='html/libvirt-libvirt.html#virConnectOpenAuth'/> <reference name='virConnectOpenReadOnly' href='html/libvirt-libvirt.html#virConnectOpenReadOnly'/> @@ -271,6 +296,54 @@ <reference name='virSchedParameterPtr' href='html/libvirt-libvirt.html#virSchedParameterPtr'/> <reference name='virSchedParameterType' href='html/libvirt-libvirt.html#virSchedParameterType'/> <reference name='virSetErrorFunc' href='html/libvirt-virterror.html#virSetErrorFunc'/> + <reference name='virStoragePool' href='html/libvirt-libvirt.html#virStoragePool'/> + <reference name='virStoragePoolBuild' href='html/libvirt-libvirt.html#virStoragePoolBuild'/> + <reference name='virStoragePoolBuildFlags' href='html/libvirt-libvirt.html#virStoragePoolBuildFlags'/> + <reference name='virStoragePoolCreate' href='html/libvirt-libvirt.html#virStoragePoolCreate'/> + <reference name='virStoragePoolCreateXML' href='html/libvirt-libvirt.html#virStoragePoolCreateXML'/> + <reference name='virStoragePoolDefineXML' href='html/libvirt-libvirt.html#virStoragePoolDefineXML'/> + <reference name='virStoragePoolDelete' href='html/libvirt-libvirt.html#virStoragePoolDelete'/> + <reference name='virStoragePoolDeleteFlags' href='html/libvirt-libvirt.html#virStoragePoolDeleteFlags'/> + <reference name='virStoragePoolDestroy' href='html/libvirt-libvirt.html#virStoragePoolDestroy'/> + <reference name='virStoragePoolFree' href='html/libvirt-libvirt.html#virStoragePoolFree'/> + <reference name='virStoragePoolGetAutostart' href='html/libvirt-libvirt.html#virStoragePoolGetAutostart'/> + <reference name='virStoragePoolGetConnect' href='html/libvirt-libvirt.html#virStoragePoolGetConnect'/> + <reference name='virStoragePoolGetInfo' href='html/libvirt-libvirt.html#virStoragePoolGetInfo'/> + <reference name='virStoragePoolGetName' href='html/libvirt-libvirt.html#virStoragePoolGetName'/> + <reference name='virStoragePoolGetUUID' href='html/libvirt-libvirt.html#virStoragePoolGetUUID'/> + <reference name='virStoragePoolGetUUIDString' href='html/libvirt-libvirt.html#virStoragePoolGetUUIDString'/> + <reference name='virStoragePoolGetXMLDesc' href='html/libvirt-libvirt.html#virStoragePoolGetXMLDesc'/> + <reference name='virStoragePoolInfo' href='html/libvirt-libvirt.html#virStoragePoolInfo'/> + <reference name='virStoragePoolInfoPtr' href='html/libvirt-libvirt.html#virStoragePoolInfoPtr'/> + <reference name='virStoragePoolListVolumes' href='html/libvirt-libvirt.html#virStoragePoolListVolumes'/> + <reference name='virStoragePoolLookupByName' href='html/libvirt-libvirt.html#virStoragePoolLookupByName'/> + <reference name='virStoragePoolLookupByUUID' href='html/libvirt-libvirt.html#virStoragePoolLookupByUUID'/> + <reference name='virStoragePoolLookupByUUIDString' href='html/libvirt-libvirt.html#virStoragePoolLookupByUUIDString'/> + <reference name='virStoragePoolLookupByVolume' href='html/libvirt-libvirt.html#virStoragePoolLookupByVolume'/> + <reference name='virStoragePoolNumOfVolumes' href='html/libvirt-libvirt.html#virStoragePoolNumOfVolumes'/> + <reference name='virStoragePoolPtr' href='html/libvirt-libvirt.html#virStoragePoolPtr'/> + <reference name='virStoragePoolRefresh' href='html/libvirt-libvirt.html#virStoragePoolRefresh'/> + <reference name='virStoragePoolSetAutostart' href='html/libvirt-libvirt.html#virStoragePoolSetAutostart'/> + <reference name='virStoragePoolState' href='html/libvirt-libvirt.html#virStoragePoolState'/> + <reference name='virStoragePoolUndefine' href='html/libvirt-libvirt.html#virStoragePoolUndefine'/> + <reference name='virStorageVol' href='html/libvirt-libvirt.html#virStorageVol'/> + <reference name='virStorageVolCreateXML' href='html/libvirt-libvirt.html#virStorageVolCreateXML'/> + <reference name='virStorageVolDelete' href='html/libvirt-libvirt.html#virStorageVolDelete'/> + <reference name='virStorageVolDeleteFlags' href='html/libvirt-libvirt.html#virStorageVolDeleteFlags'/> + <reference name='virStorageVolFree' href='html/libvirt-libvirt.html#virStorageVolFree'/> + <reference name='virStorageVolGetConnect' href='html/libvirt-libvirt.html#virStorageVolGetConnect'/> + <reference name='virStorageVolGetInfo' href='html/libvirt-libvirt.html#virStorageVolGetInfo'/> + <reference name='virStorageVolGetKey' href='html/libvirt-libvirt.html#virStorageVolGetKey'/> + <reference name='virStorageVolGetName' href='html/libvirt-libvirt.html#virStorageVolGetName'/> + <reference name='virStorageVolGetPath' href='html/libvirt-libvirt.html#virStorageVolGetPath'/> + <reference name='virStorageVolGetXMLDesc' href='html/libvirt-libvirt.html#virStorageVolGetXMLDesc'/> + <reference name='virStorageVolInfo' href='html/libvirt-libvirt.html#virStorageVolInfo'/> + <reference name='virStorageVolInfoPtr' href='html/libvirt-libvirt.html#virStorageVolInfoPtr'/> + <reference name='virStorageVolLookupByKey' href='html/libvirt-libvirt.html#virStorageVolLookupByKey'/> + <reference name='virStorageVolLookupByName' href='html/libvirt-libvirt.html#virStorageVolLookupByName'/> + <reference name='virStorageVolLookupByPath' href='html/libvirt-libvirt.html#virStorageVolLookupByPath'/> + <reference name='virStorageVolPtr' href='html/libvirt-libvirt.html#virStorageVolPtr'/> + <reference name='virStorageVolType' href='html/libvirt-libvirt.html#virStorageVolType'/> <reference name='virVcpuInfo' href='html/libvirt-libvirt.html#virVcpuInfo'/> <reference name='virVcpuInfoPtr' href='html/libvirt-libvirt.html#virVcpuInfoPtr'/> <reference name='virVcpuState' href='html/libvirt-libvirt.html#virVcpuState'/> @@ -325,6 +398,8 @@ <ref name='VIR_ERR_INVALID_DOMAIN'/> <ref name='VIR_ERR_INVALID_MAC'/> <ref name='VIR_ERR_INVALID_NETWORK'/> + <ref name='VIR_ERR_INVALID_STORAGE_POOL'/> + <ref name='VIR_ERR_INVALID_STORAGE_VOL'/> <ref name='VIR_ERR_NETWORK_EXIST'/> <ref name='VIR_ERR_NONE'/> <ref name='VIR_ERR_NO_CONNECT'/> @@ -369,6 +444,7 @@ <ref name='VIR_FROM_RPC'/> <ref name='VIR_FROM_SEXPR'/> <ref name='VIR_FROM_STATS_LINUX'/> + <ref name='VIR_FROM_STORAGE'/> <ref name='VIR_FROM_TEST'/> <ref name='VIR_FROM_XEN'/> <ref name='VIR_FROM_XEND'/> @@ -384,6 +460,20 @@ <ref name='VIR_JOB_UNBOUNDED'/> <ref name='VIR_MIGRATE_LIVE'/> <ref name='VIR_NODEINFO_MAXCPUS'/> + <ref name='VIR_STORAGE_POOL_BUILDING'/> + <ref name='VIR_STORAGE_POOL_BUILD_EXTEND'/> + <ref name='VIR_STORAGE_POOL_BUILD_NEW'/> + <ref name='VIR_STORAGE_POOL_BUILD_REPAIR'/> + <ref name='VIR_STORAGE_POOL_DEGRADED'/> + <ref name='VIR_STORAGE_POOL_DELETE_CLEAR'/> + <ref name='VIR_STORAGE_POOL_DELETE_NORMAL'/> + <ref name='VIR_STORAGE_POOL_INACTIVE'/> + <ref name='VIR_STORAGE_POOL_RUNNING'/> + <ref name='VIR_STORAGE_VOL_BLOCK'/> + <ref name='VIR_STORAGE_VOL_DELETE_CLEAR'/> + <ref name='VIR_STORAGE_VOL_DELETE_NORMAL'/> + <ref name='VIR_STORAGE_VOL_FILE'/> + <ref name='VIR_STORAGE_VOL_VIRTUAL'/> <ref name='VIR_UNUSE_CPU'/> <ref name='VIR_USE_CPU'/> <ref name='VIR_UUID_BUFLEN'/> @@ -392,6 +482,7 @@ <ref name='VIR_VCPU_OFFLINE'/> <ref name='VIR_VCPU_RUNNING'/> <ref name='VIR_WAR_NO_NETWORK'/> + <ref name='VIR_WAR_NO_STORAGE'/> </letter> <letter name='_'> <ref name='_virConnectAuth'/> @@ -403,6 +494,8 @@ <ref name='_virJobInfo'/> <ref name='_virNodeInfo'/> <ref name='_virSchedParameter'/> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> <ref name='_virVcpuInfo'/> </letter> <letter name='v'> @@ -419,6 +512,7 @@ <ref name='virConnectCredential'/> <ref name='virConnectCredentialPtr'/> <ref name='virConnectCredentialType'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virConnectFlags'/> <ref name='virConnectGetCapabilities'/> <ref name='virConnectGetHostname'/> @@ -428,12 +522,16 @@ <ref name='virConnectGetVersion'/> <ref name='virConnectListDefinedDomains'/> <ref name='virConnectListDefinedNetworks'/> + <ref name='virConnectListDefinedStoragePools'/> <ref name='virConnectListDomains'/> <ref name='virConnectListNetworks'/> + <ref name='virConnectListStoragePools'/> <ref name='virConnectNumOfDefinedDomains'/> <ref name='virConnectNumOfDefinedNetworks'/> + <ref name='virConnectNumOfDefinedStoragePools'/> <ref name='virConnectNumOfDomains'/> <ref name='virConnectNumOfNetworks'/> + <ref name='virConnectNumOfStoragePools'/> <ref name='virConnectOpen'/> <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> @@ -553,6 +651,54 @@ <ref name='virSchedParameterPtr'/> <ref name='virSchedParameterType'/> <ref name='virSetErrorFunc'/> + <ref name='virStoragePool'/> + <ref name='virStoragePoolBuild'/> + <ref name='virStoragePoolBuildFlags'/> + <ref name='virStoragePoolCreate'/> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStoragePoolDelete'/> + <ref name='virStoragePoolDeleteFlags'/> + <ref name='virStoragePoolDestroy'/> + <ref name='virStoragePoolFree'/> + <ref name='virStoragePoolGetAutostart'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStoragePoolGetInfo'/> + <ref name='virStoragePoolGetName'/> + <ref name='virStoragePoolGetUUID'/> + <ref name='virStoragePoolGetUUIDString'/> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStoragePoolInfo'/> + <ref name='virStoragePoolInfoPtr'/> + <ref name='virStoragePoolListVolumes'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStoragePoolLookupByVolume'/> + <ref name='virStoragePoolNumOfVolumes'/> + <ref name='virStoragePoolPtr'/> + <ref name='virStoragePoolRefresh'/> + <ref name='virStoragePoolSetAutostart'/> + <ref name='virStoragePoolState'/> + <ref name='virStoragePoolUndefine'/> + <ref name='virStorageVol'/> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolDelete'/> + <ref name='virStorageVolDeleteFlags'/> + <ref name='virStorageVolFree'/> + <ref name='virStorageVolGetConnect'/> + <ref name='virStorageVolGetInfo'/> + <ref name='virStorageVolGetKey'/> + <ref name='virStorageVolGetName'/> + <ref name='virStorageVolGetPath'/> + <ref name='virStorageVolGetXMLDesc'/> + <ref name='virStorageVolInfo'/> + <ref name='virStorageVolInfoPtr'/> + <ref name='virStorageVolLookupByKey'/> + <ref name='virStorageVolLookupByName'/> + <ref name='virStorageVolLookupByPath'/> + <ref name='virStorageVolPtr'/> + <ref name='virStorageVolType'/> <ref name='virVcpuInfo'/> <ref name='virVcpuInfoPtr'/> <ref name='virVcpuState'/> @@ -574,6 +720,8 @@ <ref name='virConnectOpenReadOnly'/> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </type> <type name='virDomainPtr'> <ref name='virDomainCreateLinux'/> @@ -607,16 +755,37 @@ <ref name='virNetworkLookupByUUID'/> <ref name='virNetworkLookupByUUIDString'/> </type> + <type name='virStoragePoolPtr'> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStoragePoolLookupByVolume'/> + </type> + <type name='virStorageVolPtr'> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolLookupByKey'/> + <ref name='virStorageVolLookupByName'/> + <ref name='virStorageVolLookupByPath'/> + </type> </constructors> <functions> + <type name='char ** *'> + <ref name='virConnectDiscoverStoragePools'/> + </type> <type name='char ** const'> <ref name='virConnectListDefinedDomains'/> <ref name='virConnectListDefinedNetworks'/> + <ref name='virConnectListDefinedStoragePools'/> <ref name='virConnectListNetworks'/> + <ref name='virConnectListStoragePools'/> + <ref name='virStoragePoolListVolumes'/> </type> <type name='const unsigned char *'> <ref name='virDomainLookupByUUID'/> <ref name='virNetworkLookupByUUID'/> + <ref name='virStoragePoolLookupByUUID'/> </type> <type name='int *'> <ref name='virConnectListDomains'/> @@ -624,6 +793,7 @@ <ref name='virDomainGetSchedulerParameters'/> <ref name='virDomainGetSchedulerType'/> <ref name='virNetworkGetAutostart'/> + <ref name='virStoragePoolGetAutostart'/> </type> <type name='size_t'> <ref name='virDomainBlockStats'/> @@ -634,15 +804,24 @@ <ref name='virDomainGetVcpus'/> <ref name='virDomainPinVcpu'/> <ref name='virNetworkGetUUID'/> + <ref name='virStoragePoolGetUUID'/> </type> <type name='unsigned int'> <ref name='virConnectAuthCallbackPtr'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virDomainCreateJob'/> <ref name='virDomainCreateLinux'/> <ref name='virDomainCreateLinuxJob'/> <ref name='virDomainPinVcpu'/> <ref name='virDomainReboot'/> <ref name='virDomainSetVcpus'/> + <ref name='virStoragePoolBuild'/> + <ref name='virStoragePoolDelete'/> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStoragePoolRefresh'/> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolDelete'/> + <ref name='virStorageVolGetXMLDesc'/> </type> <type name='unsigned long'> <ref name='virDomainMigrate'/> @@ -668,6 +847,7 @@ <ref name='virConnResetLastError'/> <ref name='virConnSetErrorFunc'/> <ref name='virConnectClose'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virConnectGetCapabilities'/> <ref name='virConnectGetHostname'/> <ref name='virConnectGetMaxVcpus'/> @@ -676,12 +856,16 @@ <ref name='virConnectGetVersion'/> <ref name='virConnectListDefinedDomains'/> <ref name='virConnectListDefinedNetworks'/> + <ref name='virConnectListDefinedStoragePools'/> <ref name='virConnectListDomains'/> <ref name='virConnectListNetworks'/> + <ref name='virConnectListStoragePools'/> <ref name='virConnectNumOfDefinedDomains'/> <ref name='virConnectNumOfDefinedNetworks'/> + <ref name='virConnectNumOfDefinedStoragePools'/> <ref name='virConnectNumOfDomains'/> <ref name='virConnectNumOfNetworks'/> + <ref name='virConnectNumOfStoragePools'/> <ref name='virDomainCreateLinux'/> <ref name='virDomainCreateLinuxJob'/> <ref name='virDomainDefineXML'/> @@ -701,6 +885,13 @@ <ref name='virNodeGetCellsFreeMemory'/> <ref name='virNodeGetFreeMemory'/> <ref name='virNodeGetInfo'/> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStorageVolLookupByKey'/> + <ref name='virStorageVolLookupByPath'/> </type> <type name='virDomainBlockStatsPtr'> <ref name='virDomainBlockStats'/> @@ -797,6 +988,44 @@ <ref name='virDomainGetSchedulerParameters'/> <ref name='virDomainSetSchedulerParameters'/> </type> + <type name='virStoragePoolInfoPtr'> + <ref name='virStoragePoolGetInfo'/> + </type> + <type name='virStoragePoolPtr'> + <ref name='virStoragePoolBuild'/> + <ref name='virStoragePoolCreate'/> + <ref name='virStoragePoolDelete'/> + <ref name='virStoragePoolDestroy'/> + <ref name='virStoragePoolFree'/> + <ref name='virStoragePoolGetAutostart'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStoragePoolGetInfo'/> + <ref name='virStoragePoolGetName'/> + <ref name='virStoragePoolGetUUID'/> + <ref name='virStoragePoolGetUUIDString'/> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStoragePoolListVolumes'/> + <ref name='virStoragePoolNumOfVolumes'/> + <ref name='virStoragePoolRefresh'/> + <ref name='virStoragePoolSetAutostart'/> + <ref name='virStoragePoolUndefine'/> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolLookupByName'/> + </type> + <type name='virStorageVolInfoPtr'> + <ref name='virStorageVolGetInfo'/> + </type> + <type name='virStorageVolPtr'> + <ref name='virStoragePoolLookupByVolume'/> + <ref name='virStorageVolDelete'/> + <ref name='virStorageVolFree'/> + <ref name='virStorageVolGetConnect'/> + <ref name='virStorageVolGetInfo'/> + <ref name='virStorageVolGetKey'/> + <ref name='virStorageVolGetName'/> + <ref name='virStorageVolGetPath'/> + <ref name='virStorageVolGetXMLDesc'/> + </type> <type name='virVcpuInfoPtr'> <ref name='virDomainGetVcpus'/> </type> @@ -849,6 +1078,20 @@ <ref name='VIR_JOB_UNBOUNDED'/> <ref name='VIR_MIGRATE_LIVE'/> <ref name='VIR_NODEINFO_MAXCPUS'/> + <ref name='VIR_STORAGE_POOL_BUILDING'/> + <ref name='VIR_STORAGE_POOL_BUILD_EXTEND'/> + <ref name='VIR_STORAGE_POOL_BUILD_NEW'/> + <ref name='VIR_STORAGE_POOL_BUILD_REPAIR'/> + <ref name='VIR_STORAGE_POOL_DEGRADED'/> + <ref name='VIR_STORAGE_POOL_DELETE_CLEAR'/> + <ref name='VIR_STORAGE_POOL_DELETE_NORMAL'/> + <ref name='VIR_STORAGE_POOL_INACTIVE'/> + <ref name='VIR_STORAGE_POOL_RUNNING'/> + <ref name='VIR_STORAGE_VOL_BLOCK'/> + <ref name='VIR_STORAGE_VOL_DELETE_CLEAR'/> + <ref name='VIR_STORAGE_VOL_DELETE_NORMAL'/> + <ref name='VIR_STORAGE_VOL_FILE'/> + <ref name='VIR_STORAGE_VOL_VIRTUAL'/> <ref name='VIR_UNUSE_CPU'/> <ref name='VIR_USE_CPU'/> <ref name='VIR_UUID_BUFLEN'/> @@ -864,6 +1107,8 @@ <ref name='_virJobInfo'/> <ref name='_virNodeInfo'/> <ref name='_virSchedParameter'/> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> <ref name='_virVcpuInfo'/> <ref name='virConnect'/> <ref name='virConnectAuth'/> @@ -874,6 +1119,7 @@ <ref name='virConnectCredential'/> <ref name='virConnectCredentialPtr'/> <ref name='virConnectCredentialType'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virConnectFlags'/> <ref name='virConnectGetCapabilities'/> <ref name='virConnectGetHostname'/> @@ -883,12 +1129,16 @@ <ref name='virConnectGetVersion'/> <ref name='virConnectListDefinedDomains'/> <ref name='virConnectListDefinedNetworks'/> + <ref name='virConnectListDefinedStoragePools'/> <ref name='virConnectListDomains'/> <ref name='virConnectListNetworks'/> + <ref name='virConnectListStoragePools'/> <ref name='virConnectNumOfDefinedDomains'/> <ref name='virConnectNumOfDefinedNetworks'/> + <ref name='virConnectNumOfDefinedStoragePools'/> <ref name='virConnectNumOfDomains'/> <ref name='virConnectNumOfNetworks'/> + <ref name='virConnectNumOfStoragePools'/> <ref name='virConnectOpen'/> <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> @@ -994,6 +1244,54 @@ <ref name='virSchedParameter'/> <ref name='virSchedParameterPtr'/> <ref name='virSchedParameterType'/> + <ref name='virStoragePool'/> + <ref name='virStoragePoolBuild'/> + <ref name='virStoragePoolBuildFlags'/> + <ref name='virStoragePoolCreate'/> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStoragePoolDelete'/> + <ref name='virStoragePoolDeleteFlags'/> + <ref name='virStoragePoolDestroy'/> + <ref name='virStoragePoolFree'/> + <ref name='virStoragePoolGetAutostart'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStoragePoolGetInfo'/> + <ref name='virStoragePoolGetName'/> + <ref name='virStoragePoolGetUUID'/> + <ref name='virStoragePoolGetUUIDString'/> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStoragePoolInfo'/> + <ref name='virStoragePoolInfoPtr'/> + <ref name='virStoragePoolListVolumes'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStoragePoolLookupByVolume'/> + <ref name='virStoragePoolNumOfVolumes'/> + <ref name='virStoragePoolPtr'/> + <ref name='virStoragePoolRefresh'/> + <ref name='virStoragePoolSetAutostart'/> + <ref name='virStoragePoolState'/> + <ref name='virStoragePoolUndefine'/> + <ref name='virStorageVol'/> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolDelete'/> + <ref name='virStorageVolDeleteFlags'/> + <ref name='virStorageVolFree'/> + <ref name='virStorageVolGetConnect'/> + <ref name='virStorageVolGetInfo'/> + <ref name='virStorageVolGetKey'/> + <ref name='virStorageVolGetName'/> + <ref name='virStorageVolGetPath'/> + <ref name='virStorageVolGetXMLDesc'/> + <ref name='virStorageVolInfo'/> + <ref name='virStorageVolInfoPtr'/> + <ref name='virStorageVolLookupByKey'/> + <ref name='virStorageVolLookupByName'/> + <ref name='virStorageVolLookupByPath'/> + <ref name='virStorageVolPtr'/> + <ref name='virStorageVolType'/> <ref name='virVcpuInfo'/> <ref name='virVcpuInfoPtr'/> <ref name='virVcpuState'/> @@ -1014,6 +1312,8 @@ <ref name='VIR_ERR_INVALID_DOMAIN'/> <ref name='VIR_ERR_INVALID_MAC'/> <ref name='VIR_ERR_INVALID_NETWORK'/> + <ref name='VIR_ERR_INVALID_STORAGE_POOL'/> + <ref name='VIR_ERR_INVALID_STORAGE_VOL'/> <ref name='VIR_ERR_NETWORK_EXIST'/> <ref name='VIR_ERR_NONE'/> <ref name='VIR_ERR_NO_CONNECT'/> @@ -1058,6 +1358,7 @@ <ref name='VIR_FROM_RPC'/> <ref name='VIR_FROM_SEXPR'/> <ref name='VIR_FROM_STATS_LINUX'/> + <ref name='VIR_FROM_STORAGE'/> <ref name='VIR_FROM_TEST'/> <ref name='VIR_FROM_XEN'/> <ref name='VIR_FROM_XEND'/> @@ -1065,6 +1366,7 @@ <ref name='VIR_FROM_XENXM'/> <ref name='VIR_FROM_XML'/> <ref name='VIR_WAR_NO_NETWORK'/> + <ref name='VIR_WAR_NO_STORAGE'/> <ref name='_virError'/> <ref name='virConnCopyLastError'/> <ref name='virConnGetLastError'/> @@ -1120,6 +1422,9 @@ </word> </letter> <letter name='B'> + <word name='Build'> + <ref name='virStoragePoolBuild'/> + </word> <word name='Bytes'> <ref name='virDomainPinVcpu'/> </word> @@ -1176,6 +1481,9 @@ <ref name='virDomainSetAutostart'/> <ref name='virNetworkSetAutostart'/> </word> + <word name='Consult'> + <ref name='virStorageVolGetPath'/> + </word> <word name='Copy'> <ref name='virConnCopyLastError'/> <ref name='virCopyLastError'/> @@ -1187,6 +1495,12 @@ <ref name='virNetworkCreateJob'/> <ref name='virNetworkCreateXML'/> <ref name='virNetworkCreateXMLJob'/> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStorageVolCreateXML'/> + </word> + <word name='Current'> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> </word> </letter> <letter name='D'> @@ -1199,11 +1513,20 @@ </word> <word name='Define'> <ref name='virNetworkDefineXML'/> + <ref name='virStoragePoolDefineXML'/> + </word> + <word name='Delete'> + <ref name='virStoragePoolDelete'/> + <ref name='virStorageVolDelete'/> + </word> + <word name='Depending'> + <ref name='virStorageVolGetPath'/> </word> <word name='Destroy'> <ref name='virDomainDestroy'/> <ref name='virDomainDetachDevice'/> <ref name='virNetworkDestroy'/> + <ref name='virStoragePoolDestroy'/> </word> <word name='Domain0'> <ref name='virDomainGetMaxMemory'/> @@ -1229,6 +1552,7 @@ </letter> <letter name='E'> <word name='Each'> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virDomainPinVcpu'/> </word> <word name='Estimated'> @@ -1246,11 +1570,33 @@ <ref name='virJobGetDomain'/> <ref name='virJobGetError'/> <ref name='virJobGetNetwork'/> + <ref name='virStoragePoolGetName'/> + <ref name='virStoragePoolGetUUID'/> + <ref name='virStoragePoolGetUUIDString'/> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStoragePoolListVolumes'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStoragePoolLookupByVolume'/> + <ref name='virStoragePoolNumOfVolumes'/> + <ref name='virStorageVolGetKey'/> + <ref name='virStorageVolGetName'/> + <ref name='virStorageVolGetPath'/> + <ref name='virStorageVolGetXMLDesc'/> + <ref name='virStorageVolLookupByKey'/> + <ref name='virStorageVolLookupByName'/> + <ref name='virStorageVolLookupByPath'/> + </word> + <word name='Fetches'> + <ref name='virStoragePoolGetAutostart'/> + <ref name='virStorageVolGetInfo'/> </word> <word name='Flags'> <ref name='virDomainMigrate'/> </word> <word name='For'> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virDomainBlockStats'/> <ref name='virDomainGetUUIDString'/> <ref name='virNetworkGetUUIDString'/> @@ -1258,6 +1604,7 @@ <word name='Free'> <ref name='virDomainFree'/> <ref name='virNetworkFree'/> + <ref name='virStoragePoolFree'/> </word> </letter> <letter name='G'> @@ -1275,6 +1622,7 @@ <ref name='virNetworkGetName'/> <ref name='virNetworkGetUUID'/> <ref name='virNetworkGetUUIDString'/> + <ref name='virStoragePoolGetInfo'/> </word> </letter> <letter name='H'> @@ -1300,6 +1648,8 @@ <word name='Instead'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> </letter> <letter name='K'> @@ -1322,6 +1672,10 @@ <word name='List'> <ref name='_virConnectAuth'/> </word> + <word name='Logical'> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> + </word> </letter> <letter name='M'> <word name='Macro'> @@ -1339,6 +1693,9 @@ </word> </letter> <letter name='N'> + <word name='NFS'> + <ref name='virConnectDiscoverStoragePools'/> + </word> <word name='NUMA'> <ref name='_virNodeInfo'/> <ref name='virNodeGetCellsFreeMemory'/> @@ -1348,6 +1705,9 @@ </word> <word name='Normally'> <ref name='virConnectGetURI'/> + </word> + <word name='Not'> + <ref name='virStorageVolCreateXML'/> </word> <word name='Note'> <ref name='virDomainCoreDump'/> @@ -1393,10 +1753,14 @@ <word name='Provides'> <ref name='virConnectGetCapabilities'/> <ref name='virConnectGetMaxVcpus'/> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> <ref name='virConnectNumOfDefinedDomains'/> <ref name='virConnectNumOfDefinedNetworks'/> + <ref name='virConnectNumOfDefinedStoragePools'/> <ref name='virConnectNumOfDomains'/> <ref name='virConnectNumOfNetworks'/> + <ref name='virConnectNumOfStoragePools'/> <ref name='virDomainGetAutostart'/> <ref name='virDomainGetConnect'/> <ref name='virDomainGetMaxVcpus'/> @@ -1404,8 +1768,12 @@ <ref name='virNetworkGetAutostart'/> <ref name='virNetworkGetBridgeName'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> </letter> + </chunk> + <chunk name='chunk1'> <letter name='R'> <word name='RFC4122'> <ref name='virDomainGetUUIDString'/> @@ -1422,9 +1790,15 @@ </word> <word name='Release'> <ref name='virJobFree'/> + <ref name='virStorageVolFree'/> + </word> + <word name='Remaining'> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> </word> <word name='Request'> <ref name='virJobCancel'/> + <ref name='virStoragePoolRefresh'/> </word> <word name='Reset'> <ref name='virConnResetLastError'/> @@ -1449,6 +1823,9 @@ <ref name='virConnSetErrorFunc'/> <ref name='virSetErrorFunc'/> </word> + <word name='Sets'> + <ref name='virStoragePoolSetAutostart'/> + </word> <word name='Shutdown'> <ref name='virDomainShutdown'/> </word> @@ -1464,6 +1841,9 @@ </word> <word name='Some'> <ref name='virDomainMigrate'/> + </word> + <word name='Starts'> + <ref name='virStoragePoolCreate'/> </word> <word name='Suspends'> <ref name='virDomainSuspend'/> @@ -1472,6 +1852,9 @@ <letter name='T'> <word name='TODO:'> <ref name='virDomainShutdown'/> + </word> + <word name='Talks'> + <ref name='virConnectDiscoverStoragePools'/> </word> <word name='There'> <ref name='virDomainMigrate'/> @@ -1522,6 +1905,7 @@ </word> <word name='Undefine'> <ref name='virNetworkUndefine'/> + <ref name='virStoragePoolUndefine'/> </word> <word name='Uri'> <ref name='virDomainMigrate'/> @@ -1532,8 +1916,6 @@ <ref name='virDomainSuspend'/> </word> </letter> - </chunk> - <chunk name='chunk1'> <letter name='V'> <word name='VIR_COPY_CPUMAP'> <ref name='VIR_COPY_CPUMAP'/> @@ -1568,16 +1950,20 @@ <word name='VIR_UUID_BUFLEN'> <ref name='virDomainGetUUID'/> <ref name='virNetworkGetUUID'/> + <ref name='virStoragePoolGetUUID'/> </word> <word name='VIR_UUID_STRING_BUFLEN'> <ref name='virDomainGetUUIDString'/> <ref name='virNetworkGetUUIDString'/> + <ref name='virStoragePoolGetUUIDString'/> </word> </letter> <letter name='W'> <word name='WARNING:'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='What'> <ref name='_virError'/> @@ -1585,23 +1971,11 @@ <word name='When'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> </letter> <letter name='X'> - <word name='XML'> - <ref name='virConnectGetCapabilities'/> - <ref name='virConnectGetMaxVcpus'/> - <ref name='virDomainAttachDevice'/> - <ref name='virDomainCreateLinux'/> - <ref name='virDomainCreateLinuxJob'/> - <ref name='virDomainDefineXML'/> - <ref name='virDomainDetachDevice'/> - <ref name='virDomainGetXMLDesc'/> - <ref name='virNetworkCreateXML'/> - <ref name='virNetworkCreateXMLJob'/> - <ref name='virNetworkDefineXML'/> - <ref name='virNetworkGetXMLDesc'/> - </word> <word name='Xen'> <ref name='_virDomainBlockStats'/> <ref name='virDomainCoreDump'/> @@ -1619,6 +1993,8 @@ <ref name='virJobGetInfo'/> <ref name='virNetworkGetUUIDString'/> <ref name='virNodeGetInfo'/> + <ref name='virStoragePoolGetInfo'/> + <ref name='virStorageVolGetInfo'/> </word> <word name='access'> <ref name='_virNodeInfo'/> @@ -1634,18 +2010,27 @@ <ref name='virDomainSuspend'/> <ref name='virNetworkDestroy'/> </word> + <word name='accessed'> + <ref name='virStorageVolGetKey'/> + </word> <word name='accesses'> <ref name='virConnGetLastError'/> <ref name='virGetLastError'/> + </word> + <word name='across'> + <ref name='virStorageVolGetPath'/> </word> <word name='active'> <ref name='VIR_NODEINFO_MAXCPUS'/> <ref name='_virNodeInfo'/> <ref name='virConnectListDomains'/> <ref name='virConnectListNetworks'/> + <ref name='virConnectListStoragePools'/> <ref name='virConnectNumOfDomains'/> <ref name='virConnectNumOfNetworks'/> + <ref name='virConnectNumOfStoragePools'/> <ref name='virDomainSuspend'/> + <ref name='virStoragePoolDestroy'/> </word> <word name='add'> <ref name='virDomainShutdown'/> @@ -1662,6 +2047,7 @@ <ref name='virDomainSave'/> <ref name='virDomainSaveJob'/> <ref name='virDomainShutdown'/> + <ref name='virStoragePoolDestroy'/> </word> <word name='against'> <ref name='virGetVersion'/> @@ -1676,9 +2062,14 @@ <ref name='virDomainGetVcpus'/> <ref name='virJobCancel'/> <ref name='virNetworkDestroy'/> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolGetXMLDesc'/> </word> <word name='allocated'> <ref name='cpumap'/> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> <ref name='virDomainGetInfo'/> <ref name='virDomainGetMaxMemory'/> <ref name='virDomainGetVcpus'/> @@ -1690,6 +2081,11 @@ <ref name='virNodeGetCellsFreeMemory'/> <ref name='virNodeGetInfo'/> </word> + <word name='allocation'> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> + <ref name='virStorageVolGetInfo'/> + </word> <word name='allowed'> <ref name='_virDomainInfo'/> </word> @@ -1744,6 +2140,8 @@ <word name='are'> <ref name='virConnectClose'/> <ref name='virConnectGetHostname'/> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> <ref name='virConnectOpen'/> <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> @@ -1753,21 +2151,12 @@ <ref name='virNetworkDestroy'/> <ref name='virSetErrorFunc'/> </word> - <word name='array'> - <ref name='VIR_COPY_CPUMAP'/> - <ref name='VIR_GET_CPUMAP'/> - <ref name='cpumaps'/> - <ref name='virConnectListDefinedDomains'/> - <ref name='virConnectListDefinedNetworks'/> - <ref name='virConnectListDomains'/> - <ref name='virConnectListNetworks'/> - <ref name='virDomainGetSchedulerParameters'/> - <ref name='virDomainGetUUID'/> - <ref name='virDomainGetUUIDString'/> - <ref name='virDomainGetVcpus'/> - <ref name='virNetworkGetUUID'/> - <ref name='virNetworkGetUUIDString'/> - <ref name='virNodeGetCellsFreeMemory'/> + <word name='aspects'> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStorageVolGetXMLDesc'/> + </word> + <word name='assigned'> + <ref name='virStorageVolGetPath'/> </word> <word name='associated'> <ref name='virDomainGetConnect'/> @@ -1775,6 +2164,8 @@ <ref name='virJobFree'/> <ref name='virJobGetError'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='assumed'> <ref name='virDomainGetVcpus'/> @@ -1789,6 +2180,7 @@ <ref name='virDomainDetachDevice'/> </word> <word name='attempt'> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virDomainCreateJob'/> <ref name='virDomainCreateLinuxJob'/> </word> @@ -1798,15 +2190,24 @@ </word> <word name='authentication'> <ref name='virConnectOpenAuth'/> + </word> + <word name='auto-discover'> + <ref name='virConnectDiscoverStoragePools'/> </word> <word name='automatically'> <ref name='virDomainGetAutostart'/> <ref name='virDomainSetAutostart'/> <ref name='virNetworkGetAutostart'/> <ref name='virNetworkSetAutostart'/> + <ref name='virStoragePoolGetAutostart'/> + </word> + <word name='autostart'> + <ref name='virStoragePoolGetAutostart'/> + <ref name='virStoragePoolSetAutostart'/> </word> <word name='available'> <ref name='_virError'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virConnectOpenReadOnly'/> <ref name='virNodeGetFreeMemory'/> </word> @@ -1818,12 +2219,15 @@ <ref name='virInitialize'/> </word> </letter> + </chunk> + <chunk name='chunk2'> <letter name='b'> <word name='back'> <ref name='virConnSetErrorFunc'/> <ref name='virDomainDestroy'/> <ref name='virGetVersion'/> <ref name='virNetworkDestroy'/> + <ref name='virStoragePoolGetXMLDesc'/> </word> <word name='backend'> <ref name='virDomainAttachDevice'/> @@ -1844,12 +2248,25 @@ <ref name='virNetworkLookupByName'/> <ref name='virNetworkLookupByUUID'/> <ref name='virNetworkLookupByUUIDString'/> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolLookupByKey'/> + <ref name='virStorageVolLookupByName'/> + <ref name='virStorageVolLookupByPath'/> </word> <word name='basically'> <ref name='virDomainGetMaxVcpus'/> </word> + <word name='been'> + <ref name='virStoragePoolDestroy'/> + </word> <word name='behaviour'> <ref name='virDomainCreateJob'/> + <ref name='virStoragePoolRefresh'/> </word> <word name='being'> <ref name='virDomainReboot'/> @@ -1871,6 +2288,8 @@ <word name='bindings'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='bit'> <ref name='VIR_UNUSE_CPU'/> @@ -1886,6 +2305,9 @@ <ref name='virDomainGetAutostart'/> <ref name='virNetworkGetAutostart'/> </word> + <word name='boot'> + <ref name='virStoragePoolGetAutostart'/> + </word> <word name='booted'> <ref name='virDomainGetMaxVcpus'/> </word> @@ -1901,6 +2323,8 @@ <word name='buffer'> <ref name='VIR_UUID_BUFLEN'/> <ref name='VIR_UUID_STRING_BUFLEN'/> + <ref name='virStoragePoolGetUUID'/> + <ref name='virStoragePoolGetUUIDString'/> </word> <word name='but'> <ref name='VIR_NODEINFO_MAXCPUS'/> @@ -1920,6 +2344,8 @@ </word> <word name='bytes'> <ref name='VIR_CPU_MAPLEN'/> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> <ref name='cpumap'/> <ref name='cpumaps'/> <ref name='maplen'/> @@ -1929,10 +2355,10 @@ <ref name='virDomainPinVcpu'/> <ref name='virNetworkGetUUID'/> <ref name='virNetworkGetUUIDString'/> + <ref name='virStoragePoolGetUUID'/> + <ref name='virStoragePoolGetUUIDString'/> </word> </letter> - </chunk> - <chunk name='chunk2'> <letter name='c'> <word name='calculate'> <ref name='VIR_NODEINFO_MAXCPUS'/> @@ -1954,6 +2380,8 @@ <ref name='virNetworkDestroy'/> <ref name='virNetworkGetConnect'/> <ref name='virNodeGetCellsFreeMemory'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='callback'> <ref name='virConnSetErrorFunc'/> @@ -1974,6 +2402,8 @@ <ref name='cpumap'/> <ref name='virConnectGetHostname'/> <ref name='virConnectGetURI'/> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> <ref name='virDomainGetOSType'/> <ref name='virDomainGetSchedulerType'/> <ref name='virDomainGetXMLDesc'/> @@ -2047,6 +2477,10 @@ <ref name='virDomainSetMemory'/> <ref name='virDomainSetVcpus'/> </word> + <word name='char'> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> + </word> <word name='choose'> <ref name='virDomainMigrate'/> </word> @@ -2077,12 +2511,18 @@ <word name='com'> <ref name='virConnectGetType'/> </word> + <word name='communicating'> + <ref name='virStoragePoolRefresh'/> + </word> <word name='compiled'> <ref name='virGetVersion'/> </word> <word name='complete'> <ref name='VIR_CPU_MAPLEN'/> <ref name='virJobCancel'/> + </word> + <word name='configuration'> + <ref name='virStorageVolGetPath'/> </word> <word name='configured'> <ref name='virDomainGetAutostart'/> @@ -2111,6 +2551,10 @@ <ref name='_virConnectCredential'/> <ref name='_virJobInfo'/> </word> + <word name='contains'> + <ref name='virStoragePoolLookupByVolume'/> + <ref name='virStorageVolFree'/> + </word> <word name='content'> <ref name='virConnCopyLastError'/> <ref name='virCopyLastError'/> @@ -2122,6 +2566,7 @@ <word name='control'> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainCreateJob'/> + <ref name='virStoragePoolRefresh'/> </word> <word name='copy'> <ref name='VIR_COPY_CPUMAP'/> @@ -2142,10 +2587,16 @@ <word name='could'> <ref name='virJobCancel'/> <ref name='virSetErrorFunc'/> + <ref name='virStoragePoolCreate'/> + <ref name='virStoragePoolDelete'/> + <ref name='virStoragePoolDestroy'/> + <ref name='virStoragePoolFree'/> </word> <word name='counter'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='cpu'> <ref name='VIR_CPU_USABLE'/> @@ -2170,12 +2621,17 @@ </word> <word name='create'> <ref name='virNetworkDefineXML'/> + <ref name='virStorageVolCreateXML'/> </word> <word name='creation'> <ref name='virDomainCreateJob'/> <ref name='virDomainCreateLinuxJob'/> <ref name='virJobGetDomain'/> <ref name='virJobGetNetwork'/> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStoragePoolUndefine'/> + <ref name='virStorageVolCreateXML'/> </word> <word name='credentials'> <ref name='_virConnectAuth'/> @@ -2186,13 +2642,17 @@ </word> <word name='current'> <ref name='virDomainMigrate'/> + <ref name='virStorageVolGetInfo'/> </word> <word name='currently'> <ref name='virDomainCoreDump'/> <ref name='virDomainCoreDumpJob'/> <ref name='virDomainCreateJob'/> + <ref name='virStoragePoolRefresh'/> </word> </letter> + </chunk> + <chunk name='chunk3'> <letter name='d'> <word name='data'> <ref name='virConnSetErrorFunc'/> @@ -2231,11 +2691,18 @@ <word name='defining'> <ref name='virConnectGetCapabilities'/> </word> + <word name='definition'> + <ref name='virStoragePoolCreateXML'/> + </word> <word name='defresult'> <ref name='_virConnectCredential'/> </word> <word name='described'> <ref name='virDomainGetVcpus'/> + </word> + <word name='describing'> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStorageVolGetXMLDesc'/> </word> <word name='description'> <ref name='virDomainAttachDevice'/> @@ -2248,6 +2715,9 @@ <ref name='virNetworkCreateXMLJob'/> <ref name='virNetworkDefineXML'/> <ref name='virNetworkGetXMLDesc'/> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStorageVolCreateXML'/> </word> <word name='dest'> <ref name='virDomainMigrate'/> @@ -2255,9 +2725,16 @@ <word name='destination'> <ref name='virDomainMigrate'/> <ref name='virJobCopyLastError'/> + </word> + <word name='destroyed'> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDestroy'/> </word> <word name='details'> <ref name='virJobCopyLastError'/> + </word> + <word name='determines'> + <ref name='virStoragePoolGetAutostart'/> </word> <word name='dev='> <ref name='virDomainBlockStats'/> @@ -2269,12 +2746,25 @@ </word> <word name='devices'> <ref name='virDomainBlockStats'/> + <ref name='virStoragePoolRefresh'/> </word> <word name='different'> <ref name='virDomainMigrate'/> </word> <word name='directly'> <ref name='virDomainMigrate'/> + </word> + <word name='disappear'> + <ref name='virStoragePoolCreateXML'/> + </word> + <word name='discover'> + <ref name='virConnectDiscoverStoragePools'/> + </word> + <word name='discovered'> + <ref name='virConnectDiscoverStoragePools'/> + </word> + <word name='discovery'> + <ref name='virConnectDiscoverStoragePools'/> </word> <word name='disk'> <ref name='virDomainBlockStats'/> @@ -2289,13 +2779,22 @@ <word name='doable'> <ref name='virDomainShutdown'/> </word> + <word name='document'> + <ref name='virConnectDiscoverStoragePools'/> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStorageVolGetXMLDesc'/> + </word> <word name='documentation'> <ref name='virDomainMigrate'/> + <ref name='virStorageVolGetPath'/> </word> <word name='documented'> <ref name='virConnectOpen'/> <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> + </word> + <word name='documents'> + <ref name='virConnectDiscoverStoragePools'/> </word> <word name='does'> <ref name='virDomainBlockStats'/> @@ -2336,9 +2835,10 @@ <word name='during'> <ref name='virDomainMigrate'/> </word> + <word name='dynamically'> + <ref name='virStorageVolGetPath'/> + </word> </letter> - </chunk> - <chunk name='chunk3'> <letter name='e'> <word name='each'> <ref name='virDomainBlockStats'/> @@ -2352,6 +2852,7 @@ </word> <word name='either'> <ref name='virNodeGetCellsFreeMemory'/> + <ref name='virStorageVolGetPath'/> </word> <word name='element'> <ref name='virConnectGetMaxVcpus'/> @@ -2380,11 +2881,20 @@ <ref name='virDomainBlockStats'/> <ref name='virDomainMigrate'/> </word> + <word name='exist'> + <ref name='virStorageVolFree'/> + </word> <word name='exists'> <ref name='virDomainMigrate'/> </word> <word name='expected'> <ref name='_virNodeInfo'/> + </word> + <word name='explicitly'> + <ref name='virStoragePoolDefineXML'/> + </word> + <word name='exported'> + <ref name='virConnectDiscoverStoragePools'/> </word> <word name='extra'> <ref name='_virError'/> @@ -2407,11 +2917,28 @@ <word name='fail'> <ref name='virDomainSetVcpus'/> </word> + <word name='failed'> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStoragePoolUndefine'/> + </word> <word name='feature'> <ref name='virDomainMigrate'/> </word> <word name='features'> <ref name='virDomainMigrate'/> + </word> + <word name='feeding'> + <ref name='virConnectDiscoverStoragePools'/> + <ref name='virStoragePoolGetXMLDesc'/> + </word> + <word name='fetch'> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + </word> + <word name='fetched'> + <ref name='virStoragePoolListVolumes'/> </word> <word name='fetching'> <ref name='virConnectOpenAuth'/> @@ -2429,6 +2956,10 @@ <ref name='virDomainSave'/> <ref name='virDomainSaveJob'/> </word> + <word name='fill'> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> + </word> <word name='filled'> <ref name='_virConnectCredential'/> <ref name='virDomainGetSchedulerParameters'/> @@ -2448,7 +2979,14 @@ <ref name='virConnectOpenReadOnly'/> <ref name='virNodeGetCellsFreeMemory'/> </word> + <word name='flag'> + <ref name='virStoragePoolGetAutostart'/> + <ref name='virStoragePoolSetAutostart'/> + </word> <word name='flags'> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virConnectOpenAuth'/> <ref name='virDomainCoreDump'/> <ref name='virDomainCoreDumpJob'/> @@ -2456,6 +2994,11 @@ <ref name='virDomainMigrate'/> <ref name='virDomainReboot'/> <ref name='virNetworkGetXMLDesc'/> + <ref name='virStoragePoolDelete'/> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStoragePoolRefresh'/> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolGetXMLDesc'/> </word> <word name='following:'> <ref name='virDomainMigrate'/> @@ -2466,6 +3009,7 @@ <word name='format'> <ref name='virDomainGetVcpus'/> <ref name='virGetVersion'/> + <ref name='virStoragePoolGetXMLDesc'/> </word> <word name='found'> <ref name='virConnCopyLastError'/> @@ -2473,8 +3017,10 @@ <ref name='virConnectListNetworks'/> <ref name='virConnectNumOfDefinedDomains'/> <ref name='virConnectNumOfDefinedNetworks'/> + <ref name='virConnectNumOfDefinedStoragePools'/> <ref name='virConnectNumOfDomains'/> <ref name='virConnectNumOfNetworks'/> + <ref name='virConnectNumOfStoragePools'/> <ref name='virCopyLastError'/> <ref name='virDomainLookupByID'/> <ref name='virDomainLookupByName'/> @@ -2483,8 +3029,17 @@ <ref name='virNetworkLookupByName'/> <ref name='virNetworkLookupByUUID'/> <ref name='virNetworkLookupByUUIDString'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStoragePoolLookupByVolume'/> + <ref name='virStorageVolLookupByKey'/> + <ref name='virStorageVolLookupByName'/> + <ref name='virStorageVolLookupByPath'/> </word> <word name='free'> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> <ref name='virConnCopyLastError'/> <ref name='virConnectGetCapabilities'/> <ref name='virCopyLastError'/> @@ -2494,6 +3049,9 @@ <ref name='virNetworkGetXMLDesc'/> <ref name='virNodeGetCellsFreeMemory'/> <ref name='virNodeGetFreeMemory'/> + <ref name='virStoragePoolDestroy'/> + <ref name='virStoragePoolFree'/> + <ref name='virStoragePoolGetInfo'/> </word> <word name='freeMems'> <ref name='virNodeGetCellsFreeMemory'/> @@ -2522,6 +3080,8 @@ <ref name='virDomainResume'/> <ref name='virNetworkCreate'/> <ref name='virNetworkCreateJob'/> + <ref name='virStorageVolDelete'/> + <ref name='virStorageVolGetKey'/> </word> <word name='frozen'> <ref name='virDomainResume'/> @@ -2549,6 +3109,8 @@ <ref name='virNetworkDestroy'/> <ref name='virNetworkGetConnect'/> <ref name='virSetErrorFunc'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='functionalities'> <ref name='virConnectOpenReadOnly'/> @@ -2558,9 +3120,14 @@ <ref name='virDomainSuspend'/> </word> </letter> + </chunk> + <chunk name='chunk4'> <letter name='g'> <word name='general'> <ref name='virDomainShutdown'/> + </word> + <word name='generation'> + <ref name='virStorageVolGetXMLDesc'/> </word> <word name='get'> <ref name='virConnSetErrorFunc'/> @@ -2577,6 +3144,9 @@ <word name='gethostname'> <ref name='virConnectGetHostname'/> </word> + <word name='getting'> + <ref name='virStorageVolGetPath'/> + </word> <word name='given'> <ref name='virDomainCoreDump'/> <ref name='virDomainCoreDumpJob'/> @@ -2597,6 +3167,14 @@ <ref name='virConnSetErrorFunc'/> <ref name='virSetErrorFunc'/> </word> + <word name='globally'> + <ref name='virStoragePoolGetUUID'/> + <ref name='virStoragePoolGetUUIDString'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStorageVolGetKey'/> + <ref name='virStorageVolLookupByKey'/> + </word> <word name='growing'> <ref name='virDomainSetVcpus'/> </word> @@ -2613,6 +3191,9 @@ </word> </letter> <letter name='h'> + <word name='handle'> + <ref name='virStorageVolFree'/> + </word> <word name='handler'> <ref name='virConnSetErrorFunc'/> <ref name='virSetErrorFunc'/> @@ -2626,6 +3207,7 @@ </word> <word name='has'> <ref name='virDomainGetVcpus'/> + <ref name='virStoragePoolDestroy'/> </word> <word name='have'> <ref name='virDomainBlockStats'/> @@ -2637,6 +3219,7 @@ </word> <word name='host'> <ref name='VIR_NODEINFO_MAXCPUS'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virDomainCoreDump'/> <ref name='virDomainCoreDumpJob'/> <ref name='virDomainGetAutostart'/> @@ -2644,10 +3227,16 @@ <ref name='virDomainSetAutostart'/> <ref name='virNetworkGetAutostart'/> <ref name='virNetworkSetAutostart'/> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStorageVolGetKey'/> + <ref name='virStorageVolLookupByPath'/> </word> <word name='hostname'> <ref name='virConnectGetHostname'/> <ref name='virDomainMigrate'/> + </word> + <word name='hosts'> + <ref name='virStorageVolGetPath'/> </word> <word name='how'> <ref name='_virError'/> @@ -2667,11 +3256,17 @@ <word name='human-readable'> <ref name='_virError'/> </word> + <word name='hve'> + <ref name='virStorageVolGetKey'/> + </word> <word name='hypervisors'> <ref name='virDomainMigrate'/> </word> </letter> <letter name='i'> + <word name='iSCSI'> + <ref name='virConnectDiscoverStoragePools'/> + </word> <word name='ie:'> <ref name='cpumap'/> <ref name='virDomainGetVcpus'/> @@ -2680,6 +3275,10 @@ <ref name='virDomainReboot'/> <ref name='virDomainShutdown'/> </word> + <word name='ignored'> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> + </word> <word name='implementation'> <ref name='virJobCancel'/> </word> @@ -2688,13 +3287,20 @@ </word> <word name='inactive'> <ref name='virConnectListDefinedNetworks'/> + <ref name='virConnectListDefinedStoragePools'/> <ref name='virConnectNumOfDefinedDomains'/> <ref name='virConnectNumOfDefinedNetworks'/> + <ref name='virConnectNumOfDefinedStoragePools'/> <ref name='virDomainGetMaxVcpus'/> + <ref name='virStoragePoolCreate'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStoragePoolUndefine'/> </word> <word name='increased'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='index'> <ref name='virNodeGetCellsFreeMemory'/> @@ -2711,6 +3317,8 @@ <word name='info'> <ref name='virDomainGetVcpus'/> <ref name='virNodeGetCellsFreeMemory'/> + <ref name='virStoragePoolGetInfo'/> + <ref name='virStorageVolGetInfo'/> </word> <word name='information'> <ref name='_virError'/> @@ -2722,12 +3330,16 @@ <ref name='virNetworkGetUUIDString'/> <ref name='virNodeGetCellsFreeMemory'/> <ref name='virNodeGetInfo'/> + <ref name='virStoragePoolGetInfo'/> + <ref name='virStorageVolGetInfo'/> + <ref name='virStorageVolGetPath'/> </word> <word name='informative'> <ref name='_virError'/> </word> <word name='initializing'> <ref name='virInitialize'/> + <ref name='virStoragePoolRefresh'/> </word> <word name='inside'> <ref name='virDomainGetVcpus'/> @@ -2761,6 +3373,11 @@ </word> <word name='into'> <ref name='VIR_COPY_CPUMAP'/> + <ref name='virConnectDiscoverStoragePools'/> + <ref name='virStoragePoolGetXMLDesc'/> + </word> + <word name='involve'> + <ref name='virStoragePoolRefresh'/> </word> <word name='its'> <ref name='virDomainGetName'/> @@ -2774,10 +3391,18 @@ <ref name='virNetworkLookupByName'/> <ref name='virNetworkLookupByUUID'/> <ref name='virNetworkLookupByUUIDString'/> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStoragePoolRefresh'/> + <ref name='virStorageVolGetInfo'/> + <ref name='virStorageVolLookupByKey'/> + <ref name='virStorageVolLookupByName'/> + <ref name='virStorageVolLookupByPath'/> </word> </letter> - </chunk> - <chunk name='chunk4'> <letter name='j'> <word name='job'> <ref name='virDomainCoreDumpJob'/> @@ -2807,6 +3432,10 @@ <ref name='virDomainFree'/> <ref name='virNetworkFree'/> </word> + <word name='key'> + <ref name='virStorageVolGetKey'/> + <ref name='virStorageVolLookupByKey'/> + </word> <word name='kilobytes'> <ref name='_virNodeInfo'/> <ref name='virDomainGetMaxMemory'/> @@ -2826,6 +3455,8 @@ <word name='languages'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='last'> <ref name='virConnCopyLastError'/> @@ -2840,10 +3471,14 @@ <ref name='virConnectGetURI'/> <ref name='virDomainGetXMLDesc'/> <ref name='virNetworkGetXMLDesc'/> + <ref name='virStoragePoolGetXMLDesc'/> </word> <word name='launch'> <ref name='virDomainCreate'/> <ref name='virDomainCreateJob'/> + </word> + <word name='layer'> + <ref name='virStoragePoolRefresh'/> </word> <word name='least'> <ref name='virDomainPinVcpu'/> @@ -2891,6 +3526,8 @@ <ref name='virDomainGetConnect'/> <ref name='virDomainMigrate'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='lifetime'> <ref name='virDomainGetName'/> @@ -2907,11 +3544,19 @@ <ref name='virDomainGetInfo'/> <ref name='virDomainSetVcpus'/> </word> + <word name='limiting'> + <ref name='virStoragePoolListVolumes'/> + </word> <word name='list'> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virConnectListDefinedDomains'/> <ref name='virConnectListDefinedNetworks'/> + <ref name='virConnectListDefinedStoragePools'/> <ref name='virConnectListDomains'/> <ref name='virConnectListNetworks'/> + <ref name='virConnectListStoragePools'/> + <ref name='virStoragePoolListVolumes'/> + <ref name='virStoragePoolRefresh'/> </word> <word name='listed'> <ref name='virDomainSave'/> @@ -2922,6 +3567,13 @@ </word> <word name='live'> <ref name='virDomainMigrate'/> + </word> + <word name='locally'> + <ref name='virStoragePoolGetName'/> + <ref name='virStorageVolLookupByPath'/> + </word> + <word name='location'> + <ref name='virStoragePoolGetAutostart'/> </word> <word name='long'> <ref name='virNodeGetCellsFreeMemory'/> @@ -2941,6 +3593,8 @@ <ref name='virDomainPinVcpu'/> </word> </letter> + </chunk> + <chunk name='chunk5'> <letter name='m'> <word name='machine'> <ref name='virDomainGetAutostart'/> @@ -2986,6 +3640,15 @@ <ref name='virDomainGetVcpus'/> <ref name='virDomainPinVcpu'/> </word> + <word name='matching'> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStoragePoolLookupByVolume'/> + </word> + <word name='matter'> + <ref name='virStorageVolGetKey'/> + </word> <word name='maxCells'> <ref name='virNodeGetCellsFreeMemory'/> </word> @@ -3000,29 +3663,10 @@ <word name='maxinfo'> <ref name='virDomainGetVcpus'/> </word> - <word name='may'> - <ref name='virConnGetLastError'/> - <ref name='virConnectGetURI'/> - <ref name='virConnectGetVersion'/> - <ref name='virDomainBlockStats'/> - <ref name='virDomainCreateLinux'/> - <ref name='virDomainCreateLinuxJob'/> - <ref name='virDomainDestroy'/> - <ref name='virDomainGetXMLDesc'/> - <ref name='virDomainInterfaceStats'/> - <ref name='virDomainMigrate'/> - <ref name='virDomainReboot'/> - <ref name='virDomainResume'/> - <ref name='virDomainSave'/> - <ref name='virDomainSaveJob'/> - <ref name='virDomainSetMemory'/> - <ref name='virDomainSetVcpus'/> - <ref name='virDomainShutdown'/> - <ref name='virDomainSuspend'/> - <ref name='virGetLastError'/> - <ref name='virNetworkDestroy'/> - <ref name='virNetworkGetBridgeName'/> - <ref name='virNetworkGetXMLDesc'/> + <word name='maxnames'> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> + <ref name='virStoragePoolListVolumes'/> </word> <word name='means'> <ref name='virDomainPinVcpu'/> @@ -3055,6 +3699,8 @@ <ref name='virDomainRestoreJob'/> <ref name='virDomainSave'/> <ref name='virDomainSaveJob'/> + <ref name='virStoragePoolDestroy'/> + <ref name='virStoragePoolGetXMLDesc'/> </word> <word name='methods'> <ref name='virConnectOpenReadOnly'/> @@ -3092,12 +3738,17 @@ <ref name='virNetworkCreateJob'/> </word> <word name='more'> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> <ref name='virDomainBlockStats'/> <ref name='virDomainGetUUIDString'/> <ref name='virDomainInterfaceStats'/> <ref name='virDomainMigrate'/> <ref name='virNetworkGetUUIDString'/> <ref name='virNodeGetCellsFreeMemory'/> + </word> + <word name='most'> + <ref name='virStoragePoolListVolumes'/> </word> <word name='moves'> <ref name='virDomainCreate'/> @@ -3135,8 +3786,6 @@ <ref name='_virDomainBlockStats'/> </word> </letter> - </chunk> - <chunk name='chunk5'> <letter name='n'> <word name='name'> <ref name='_virSchedParameter'/> @@ -3150,11 +3799,21 @@ <ref name='virNetworkGetBridgeName'/> <ref name='virNetworkGetName'/> <ref name='virNetworkLookupByName'/> + <ref name='virStoragePoolGetName'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStorageVolGetName'/> + <ref name='virStorageVolLookupByName'/> </word> <word name='names'> <ref name='virConnectListDefinedDomains'/> <ref name='virConnectListDefinedNetworks'/> + <ref name='virConnectListDefinedStoragePools'/> <ref name='virConnectListNetworks'/> + <ref name='virConnectListStoragePools'/> + <ref name='virStoragePoolListVolumes'/> + </word> + <word name='naming'> + <ref name='virStorageVolGetPath'/> </word> <word name='nanoseconds'> <ref name='_virDomainInfo'/> @@ -3185,28 +3844,6 @@ <ref name='virNetworkCreate'/> <ref name='virNetworkCreateJob'/> </word> - <word name='new'> - <ref name='virDomainCoreDumpJob'/> - <ref name='virDomainCreateJob'/> - <ref name='virDomainCreateLinux'/> - <ref name='virDomainCreateLinuxJob'/> - <ref name='virDomainGetOSType'/> - <ref name='virDomainLookupByID'/> - <ref name='virDomainLookupByName'/> - <ref name='virDomainLookupByUUID'/> - <ref name='virDomainLookupByUUIDString'/> - <ref name='virDomainMigrate'/> - <ref name='virDomainRestoreJob'/> - <ref name='virDomainSaveJob'/> - <ref name='virDomainSetVcpus'/> - <ref name='virJobGetDomain'/> - <ref name='virJobGetNetwork'/> - <ref name='virNetworkCreateXML'/> - <ref name='virNetworkCreateXMLJob'/> - <ref name='virNetworkLookupByName'/> - <ref name='virNetworkLookupByUUID'/> - <ref name='virNetworkLookupByUUIDString'/> - </word> <word name='node'> <ref name='_virNodeInfo'/> <ref name='virNodeGetCellsFreeMemory'/> @@ -3218,6 +3855,9 @@ <word name='non-NULL'> <ref name='virConnectGetURI'/> </word> + <word name='non-recoverable'> + <ref name='virStoragePoolDelete'/> + </word> <word name='none'> <ref name='virConnGetLastError'/> <ref name='virGetLastError'/> @@ -3234,6 +3874,12 @@ <word name='objects'> <ref name='virDomainSetSchedulerParameters'/> </word> + <word name='obliterate'> + <ref name='virStoragePoolDelete'/> + </word> + <word name='obliteration'> + <ref name='virStoragePoolDelete'/> + </word> <word name='occured'> <ref name='virConnGetLastError'/> <ref name='virGetLastError'/> @@ -3244,6 +3890,7 @@ <word name='one'> <ref name='_virDomainInfo'/> <ref name='maplen'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virDomainAttachDevice'/> <ref name='virDomainBlockStats'/> <ref name='virDomainCreateLinux'/> @@ -3265,6 +3912,7 @@ <word name='operation'> <ref name='virDomainGetOSType'/> <ref name='virDomainReboot'/> + <ref name='virStoragePoolDelete'/> </word> <word name='option'> <ref name='virDomainShutdown'/> @@ -3273,6 +3921,9 @@ <ref name='virDomainCreateLinux'/> <ref name='virDomainCreateLinuxJob'/> <ref name='virDomainMigrate'/> + </word> + <word name='options'> + <ref name='virStoragePoolGetXMLDesc'/> </word> <word name='order'> <ref name='virDomainMigrate'/> @@ -3290,6 +3941,8 @@ <ref name='virDomainGetConnect'/> <ref name='virDomainMigrate'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='otherwise'> <ref name='VIR_CPU_USABLE'/> @@ -3308,6 +3961,8 @@ <ref name='virDomainSaveJob'/> </word> </letter> + </chunk> + <chunk name='chunk6'> <letter name='p'> <word name='parameter'> <ref name='_virSchedParameter'/> @@ -3336,9 +3991,14 @@ <ref name='virDomainBlockStats'/> <ref name='virDomainInterfaceStats'/> <ref name='virDomainMigrate'/> + <ref name='virStoragePoolLookupByVolume'/> </word> <word name='pass'> <ref name='virConnSetErrorFunc'/> + <ref name='virConnectDiscoverStoragePools'/> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolGetXMLDesc'/> </word> <word name='passed'> <ref name='virConnectGetURI'/> @@ -3353,9 +4013,15 @@ <ref name='virDomainRestoreJob'/> <ref name='virDomainSave'/> <ref name='virDomainSaveJob'/> + <ref name='virStorageVolGetPath'/> + <ref name='virStorageVolLookupByPath'/> + </word> + <word name='paths'> + <ref name='virConnectDiscoverStoragePools'/> </word> <word name='per'> <ref name='_virNodeInfo'/> + <ref name='virConnectDiscoverStoragePools'/> </word> <word name='perform'> <ref name='virDomainMigrate'/> @@ -3366,6 +4032,13 @@ <word name='performing'> <ref name='virJobGetDomain'/> <ref name='virJobGetNetwork'/> + </word> + <word name='persistent'> + <ref name='virStorageVolGetPath'/> + </word> + <word name='persitent'> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> </word> <word name='physical'> <ref name='VIR_CPU_MAPLEN'/> @@ -3381,11 +4054,26 @@ <ref name='virConnectListDefinedDomains'/> <ref name='virConnectListDefinedNetworks'/> </word> + <word name='poolk'> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> + </word> <word name='pools'> + <ref name='virConnectDiscoverStoragePools'/> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> + <ref name='virConnectNumOfDefinedStoragePools'/> + <ref name='virConnectNumOfStoragePools'/> <ref name='virDomainCreate'/> <ref name='virDomainCreateJob'/> <ref name='virNetworkCreate'/> <ref name='virNetworkCreateJob'/> + <ref name='virStoragePoolNumOfVolumes'/> + <ref name='virStorageVolCreateXML'/> + </word> + <word name='poool'> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='port'> <ref name='virDomainMigrate'/> @@ -3429,6 +4117,7 @@ <word name='process'> <ref name='virDomainResume'/> <ref name='virDomainSuspend'/> + <ref name='virStoragePoolDelete'/> </word> <word name='processed'> <ref name='virJobCancel'/> @@ -3512,9 +4201,17 @@ <word name='reference'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='reflect'> <ref name='virDomainGetMaxVcpus'/> + </word> + <word name='refresh'> + <ref name='virStoragePoolRefresh'/> + </word> + <word name='refreshed'> + <ref name='virStoragePoolRefresh'/> </word> <word name='related'> <ref name='VIR_UNUSE_CPU'/> @@ -3533,11 +4230,14 @@ </word> <word name='remaining'> <ref name='_virJobInfo'/> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> </word> <word name='remote'> <ref name='virConnectGetHostname'/> <ref name='virDomainCoreDump'/> <ref name='virDomainCoreDumpJob'/> + <ref name='virStoragePoolRefresh'/> </word> <word name='rename'> <ref name='virDomainMigrate'/> @@ -3591,6 +4291,7 @@ <ref name='virDomainSuspend'/> <ref name='virJobFree'/> <ref name='virNetworkDestroy'/> + <ref name='virStoragePoolDelete'/> </word> <word name='respond'> <ref name='virJobCancel'/> @@ -3603,6 +4304,7 @@ </word> <word name='restarted'> <ref name='virDomainResume'/> + <ref name='virStoragePoolCreateXML'/> </word> <word name='restore'> <ref name='virDomainRestore'/> @@ -3620,6 +4322,7 @@ <ref name='virCopyLastError'/> </word> <word name='return'> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virConnectGetURI'/> <ref name='virConnectGetVersion'/> <ref name='virDomainDestroy'/> @@ -3631,6 +4334,7 @@ <ref name='virNodeGetCellsFreeMemory'/> </word> <word name='returned'> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virConnectGetCapabilities'/> <ref name='virDomainBlockStats'/> <ref name='virDomainCreateLinux'/> @@ -3662,6 +4366,7 @@ <ref name='virDomainBlockStats'/> <ref name='virDomainInterfaceStats'/> <ref name='virNodeGetCellsFreeMemory'/> + <ref name='virStoragePoolDestroy'/> </word> <word name='reused'> <ref name='virDomainGetXMLDesc'/> @@ -3699,7 +4404,7 @@ </word> </letter> </chunk> - <chunk name='chunk6'> + <chunk name='chunk7'> <letter name='s'> <word name='same'> <ref name='virConnectGetURI'/> @@ -3709,6 +4414,7 @@ <ref name='virDomainMigrate'/> <ref name='virDomainSetSchedulerParameters'/> <ref name='virNetworkGetName'/> + <ref name='virStorageVolGetKey'/> </word> <word name='save'> <ref name='virDomainSave'/> @@ -3729,6 +4435,7 @@ </word> <word name='scope'> <ref name='virDomainMigrate'/> + <ref name='virStorageVolGetName'/> </word> <word name='seconds'> <ref name='_virJobInfo'/> @@ -3741,8 +4448,12 @@ <word name='seen'> <ref name='virDomainMigrate'/> </word> + <word name='server'> + <ref name='virStoragePoolRefresh'/> + </word> <word name='set'> <ref name='VIR_USE_CPU'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainCreateJob'/> <ref name='virDomainCreateLinux'/> @@ -3754,6 +4465,9 @@ <ref name='virJobCopyLastError'/> <ref name='virJobGetError'/> <ref name='virNetworkGetXMLDesc'/> + </word> + <word name='setting'> + <ref name='virStoragePoolSetAutostart'/> </word> <word name='should'> <ref name='virConnectClose'/> @@ -3772,6 +4486,7 @@ <ref name='virNetworkDestroy'/> <ref name='virNetworkFree'/> <ref name='virNetworkSetAutostart'/> + <ref name='virStoragePoolDestroy'/> </word> <word name='show'> <ref name='_virConnectCredential'/> @@ -3782,6 +4497,10 @@ </word> <word name='significant'> <ref name='virDomainPinVcpu'/> + </word> + <word name='silently'> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> </word> <word name='similar'> <ref name='virConnectGetURI'/> @@ -3795,10 +4514,14 @@ </word> <word name='size'> <ref name='_virNodeInfo'/> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> <ref name='virConnectListDefinedDomains'/> <ref name='virConnectListDefinedNetworks'/> + <ref name='virConnectListDefinedStoragePools'/> <ref name='virConnectListDomains'/> <ref name='virConnectListNetworks'/> + <ref name='virConnectListStoragePools'/> <ref name='virDomainBlockStats'/> <ref name='virDomainGetMaxMemory'/> <ref name='virDomainGetVcpus'/> @@ -3806,6 +4529,9 @@ <ref name='virDomainPinVcpu'/> <ref name='virDomainSetMaxMemory'/> <ref name='virDomainSetMemory'/> + <ref name='virStoragePoolGetUUID'/> + <ref name='virStoragePoolGetUUIDString'/> + <ref name='virStoragePoolListVolumes'/> </word> <word name='smaller'> <ref name='virNodeGetCellsFreeMemory'/> @@ -3818,6 +4544,11 @@ </word> <word name='source'> <ref name='virDomainMigrate'/> + </word> + <word name='space'> + <ref name='_virStoragePoolInfo'/> + <ref name='_virStorageVolInfo'/> + <ref name='virStoragePoolGetInfo'/> </word> <word name='specific'> <ref name='virConnectGetMaxVcpus'/> @@ -3845,12 +4576,15 @@ <ref name='virDomainSetAutostart'/> <ref name='virNetworkGetAutostart'/> <ref name='virNetworkSetAutostart'/> + <ref name='virStoragePoolCreate'/> + <ref name='virStoragePoolGetAutostart'/> </word> <word name='starting'> <ref name='virNodeGetCellsFreeMemory'/> </word> <word name='startup'> <ref name='virInitialize'/> + <ref name='virStorageVolGetPath'/> </word> <word name='state'> <ref name='_virDomainInfo'/> @@ -3901,6 +4635,11 @@ <ref name='virDomainGetConnect'/> <ref name='virDomainGetVcpus'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetAutostart'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStoragePoolGetInfo'/> + <ref name='virStorageVolGetConnect'/> + <ref name='virStorageVolGetInfo'/> </word> <word name='stored'> <ref name='virDomainPinVcpu'/> @@ -3908,6 +4647,9 @@ <word name='stores'> <ref name='virConnectListDefinedDomains'/> <ref name='virConnectListDefinedNetworks'/> + </word> + <word name='storge'> + <ref name='virConnectDiscoverStoragePools'/> </word> <word name='string'> <ref name='_virError'/> @@ -3923,6 +4665,7 @@ <ref name='virNetworkGetName'/> <ref name='virNetworkGetUUIDString'/> <ref name='virNetworkLookupByUUIDString'/> + <ref name='virStoragePoolGetUUIDString'/> </word> <word name='structure'> <ref name='virDomainBlockStats'/> @@ -3949,16 +4692,29 @@ <ref name='virDomainSave'/> <ref name='virDomainSaveJob'/> </word> + <word name='successfully'> + <ref name='virStoragePoolDestroy'/> + </word> + <word name='such'> + <ref name='virStoragePoolGetInfo'/> + <ref name='virStorageVolGetInfo'/> + </word> <word name='suitable'> <ref name='virConnGetLastError'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virDomainMigrate'/> <ref name='virGetLastError'/> + <ref name='virStoragePoolGetXMLDesc'/> + </word> + <word name='summary'> + <ref name='virStoragePoolGetInfo'/> </word> <word name='support'> <ref name='virDomainBlockStats'/> <ref name='virDomainInterfaceStats'/> <ref name='virDomainMigrate'/> <ref name='virDomainSetVcpus'/> + <ref name='virStorageVolCreateXML'/> </word> <word name='supported'> <ref name='VIR_NODEINFO_MAXCPUS'/> @@ -3984,6 +4740,8 @@ <ref name='virDomainPinVcpu'/> </word> </letter> + </chunk> + <chunk name='chunk8'> <letter name='t'> <word name='target'> <ref name='virConnCopyLastError'/> @@ -3991,6 +4749,9 @@ <ref name='virDomainBlockStats'/> <ref name='virDomainSetMemory'/> </word> + <word name='targets'> + <ref name='virConnectDiscoverStoragePools'/> + </word> <word name='technology'> <ref name='virDomainMigrate'/> </word> @@ -4001,6 +4762,8 @@ <ref name='virNetworkGetXMLDesc'/> </word> <word name='than'> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> <ref name='virDomainBlockStats'/> <ref name='virDomainGetSchedulerParameters'/> <ref name='virDomainInterfaceStats'/> @@ -4022,6 +4785,7 @@ <ref name='virDomainSetVcpus'/> <ref name='virDomainShutdown'/> <ref name='virNetworkGetName'/> + <ref name='virStoragePoolRefresh'/> </word> <word name='their'> <ref name='virConnectListDomains'/> @@ -4049,6 +4813,8 @@ <ref name='virConnectClose'/> <ref name='virConnectGetHostname'/> <ref name='virConnectGetURI'/> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> <ref name='virDomainGetVcpus'/> <ref name='virDomainReboot'/> <ref name='virDomainShutdown'/> @@ -4061,28 +4827,6 @@ <ref name='virNetworkDestroy'/> <ref name='virNetworkFree'/> </word> - <word name='this'> - <ref name='_virDomainBlockStats'/> - <ref name='_virError'/> - <ref name='virConnectGetHostname'/> - <ref name='virConnectGetURI'/> - <ref name='virDomainBlockStats'/> - <ref name='virDomainGetConnect'/> - <ref name='virDomainGetMaxMemory'/> - <ref name='virDomainGetMaxVcpus'/> - <ref name='virDomainGetSchedulerParameters'/> - <ref name='virDomainGetVcpus'/> - <ref name='virDomainInterfaceStats'/> - <ref name='virDomainMigrate'/> - <ref name='virDomainSave'/> - <ref name='virDomainSaveJob'/> - <ref name='virDomainSetMaxMemory'/> - <ref name='virDomainSetMemory'/> - <ref name='virDomainSetSchedulerParameters'/> - <ref name='virDomainSetVcpus'/> - <ref name='virInitialize'/> - <ref name='virNetworkGetConnect'/> - </word> <word name='those'> <ref name='virSetErrorFunc'/> </word> @@ -4094,10 +4838,13 @@ <ref name='_virJobInfo'/> <ref name='_virVcpuInfo'/> <ref name='virJobCancel'/> + <ref name='virStoragePoolGetAutostart'/> </word> <word name='together'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='total'> <ref name='VIR_NODEINFO_MAXCPUS'/> @@ -4117,6 +4864,7 @@ </word> <word name='type'> <ref name='_virSchedParameter'/> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virConnectGetMaxVcpus'/> <ref name='virDomainGetOSType'/> <ref name='virDomainGetSchedulerType'/> @@ -4129,20 +4877,36 @@ <ref name='virDomainMigrate'/> </word> </letter> - </chunk> - <chunk name='chunk7'> <letter name='u'> <word name='undefine'> <ref name='virDomainUndefine'/> </word> + <word name='undefined'> + <ref name='virStoragePoolDefineXML'/> + </word> <word name='underlying'> <ref name='virDomainGetVcpus'/> <ref name='virDomainMigrate'/> <ref name='virDomainPinVcpu'/> <ref name='virDomainSetVcpus'/> + <ref name='virStoragePoolBuild'/> + <ref name='virStoragePoolDelete'/> + <ref name='virStorageVolFree'/> </word> <word name='uniform'> <ref name='_virNodeInfo'/> + </word> + <word name='unique'> + <ref name='virStoragePoolGetName'/> + <ref name='virStoragePoolGetUUID'/> + <ref name='virStoragePoolGetUUIDString'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStorageVolGetKey'/> + <ref name='virStorageVolGetName'/> + <ref name='virStorageVolLookupByKey'/> + <ref name='virStorageVolLookupByPath'/> </word> <word name='unknown'> <ref name='virGetVersion'/> @@ -4153,11 +4917,24 @@ </word> <word name='until'> <ref name='virJobFree'/> + <ref name='virStoragePoolDefineXML'/> </word> <word name='unused'> + <ref name='virConnectDiscoverStoragePools'/> <ref name='virDomainCoreDump'/> <ref name='virDomainCoreDumpJob'/> <ref name='virDomainCreateJob'/> + <ref name='virStoragePoolGetXMLDesc'/> + <ref name='virStoragePoolRefresh'/> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolGetXMLDesc'/> + </word> + <word name='upon'> + <ref name='virStoragePoolBuild'/> + </word> + <word name='upto'> + <ref name='virConnectListDefinedStoragePools'/> + <ref name='virConnectListStoragePools'/> </word> <word name='uri'> <ref name='virConnectOpen'/> @@ -4174,6 +4951,9 @@ <ref name='virDomainReboot'/> <ref name='virDomainShutdown'/> </word> + <word name='usage'> + <ref name='virStoragePoolGetInfo'/> + </word> <word name='use'> <ref name='virConnGetLastError'/> <ref name='virConnectGetCapabilities'/> @@ -4181,6 +4961,9 @@ <ref name='virErrorFunc'/> <ref name='virGetLastError'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStoragePoolRefresh'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='used'> <ref name='VIR_COPY_CPUMAP'/> @@ -4204,6 +4987,7 @@ <ref name='virNetworkDestroy'/> <ref name='virNetworkFree'/> <ref name='virNetworkGetXMLDesc'/> + <ref name='virStoragePoolDestroy'/> </word> <word name='user'> <ref name='_virConnectCredential'/> @@ -4237,6 +5021,7 @@ <ref name='virNetworkGetAutostart'/> <ref name='virNetworkGetBridgeName'/> <ref name='virNetworkGetXMLDesc'/> + <ref name='virStoragePoolGetAutostart'/> </word> <word name='values'> <ref name='_virConnectAuth'/> @@ -4281,6 +5066,8 @@ <word name='virConnectPtr'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='virCopyLastError'> <ref name='virGetLastError'/> @@ -4379,6 +5166,26 @@ </word> <word name='virSchedParameter'> <ref name='VIR_DOMAIN_SCHED_FIELD_LENGTH'/> + </word> + <word name='virStoragePoolCreateXML'> + <ref name='virConnectDiscoverStoragePools'/> + <ref name='virStoragePoolGetXMLDesc'/> + </word> + <word name='virStoragePoolPtr'> + <ref name='virStoragePoolCreateXML'/> + <ref name='virStoragePoolDefineXML'/> + <ref name='virStoragePoolDestroy'/> + <ref name='virStoragePoolLookupByName'/> + <ref name='virStoragePoolLookupByUUID'/> + <ref name='virStoragePoolLookupByUUIDString'/> + <ref name='virStoragePoolLookupByVolume'/> + <ref name='virStoragePoolUndefine'/> + </word> + <word name='virStoragePoolState'> + <ref name='_virStoragePoolInfo'/> + </word> + <word name='virStorageVolType'> + <ref name='_virStorageVolInfo'/> </word> <word name='virSuspendDomain'> <ref name='virDomainResume'/> @@ -4409,7 +5216,18 @@ <ref name='virDomainPinVcpu'/> <ref name='virDomainSetVcpus'/> </word> + <word name='volatile'> + <ref name='virStoragePoolGetInfo'/> + <ref name='virStorageVolGetInfo'/> + </word> + <word name='volumes'> + <ref name='virStoragePoolNumOfVolumes'/> + <ref name='virStoragePoolRefresh'/> + <ref name='virStorageVolCreateXML'/> + </word> </letter> + </chunk> + <chunk name='chunk9'> <letter name='w'> <word name='was'> <ref name='virConnCopyLastError'/> @@ -4422,6 +5240,10 @@ <ref name='virGetVersion'/> <ref name='virJobGetDomain'/> <ref name='virJobGetNetwork'/> + <ref name='virStoragePoolRefresh'/> + </word> + <word name='what'> + <ref name='virStorageVolGetKey'/> </word> <word name='when'> <ref name='virDomainGetAutostart'/> @@ -4430,6 +5252,7 @@ <ref name='virInitialize'/> <ref name='virNetworkGetAutostart'/> <ref name='virNetworkSetAutostart'/> + <ref name='virStoragePoolCreateXML'/> </word> <word name='where'> <ref name='virDomainGetMaxMemory'/> @@ -4442,6 +5265,7 @@ <ref name='virDomainSetAutostart'/> <ref name='virNetworkGetAutostart'/> <ref name='virNetworkSetAutostart'/> + <ref name='virStoragePoolGetAutostart'/> </word> <word name='which'> <ref name='virConnGetLastError'/> @@ -4458,6 +5282,11 @@ <ref name='virNetworkGetBridgeName'/> <ref name='virNodeGetCellsFreeMemory'/> <ref name='virSetErrorFunc'/> + <ref name='virStoragePoolGetAutostart'/> + <ref name='virStoragePoolGetInfo'/> + <ref name='virStoragePoolListVolumes'/> + <ref name='virStoragePoolLookupByVolume'/> + <ref name='virStorageVolGetInfo'/> </word> <word name='whichever'> <ref name='virNodeGetCellsFreeMemory'/> @@ -4466,75 +5295,33 @@ <ref name='virGetVersion'/> <ref name='virJobFree'/> </word> - <word name='will'> - <ref name='virConnCopyLastError'/> - <ref name='virConnSetErrorFunc'/> - <ref name='virConnectGetURI'/> - <ref name='virConnectOpenAuth'/> - <ref name='virCopyLastError'/> - <ref name='virDomainCoreDump'/> - <ref name='virDomainCoreDumpJob'/> - <ref name='virDomainGetMaxVcpus'/> - <ref name='virDomainGetName'/> - <ref name='virDomainGetSchedulerParameters'/> - <ref name='virDomainMigrate'/> - <ref name='virDomainRestore'/> - <ref name='virDomainRestoreJob'/> - <ref name='virDomainSave'/> - <ref name='virDomainSaveJob'/> - <ref name='virDomainSuspend'/> - <ref name='virGetVersion'/> - <ref name='virJobCancel'/> - <ref name='virJobFree'/> - <ref name='virNetworkGetName'/> - <ref name='virNodeGetCellsFreeMemory'/> - <ref name='virSetErrorFunc'/> - </word> - <word name='with'> - <ref name='VIR_COPY_CPUMAP'/> - <ref name='VIR_CPU_MAPLEN'/> - <ref name='VIR_CPU_USABLE'/> - <ref name='VIR_GET_CPUMAP'/> - <ref name='VIR_UNUSE_CPU'/> - <ref name='VIR_USE_CPU'/> - <ref name='_virConnectCredential'/> - <ref name='virConnCopyLastError'/> - <ref name='virConnectClose'/> - <ref name='virConnectGetVersion'/> - <ref name='virCopyLastError'/> - <ref name='virDomainGetConnect'/> - <ref name='virDomainGetMaxVcpus'/> - <ref name='virDomainGetSchedulerParameters'/> - <ref name='virDomainGetXMLDesc'/> - <ref name='virDomainMigrate'/> - <ref name='virJobCancel'/> - <ref name='virJobCopyLastError'/> - <ref name='virJobFree'/> - <ref name='virJobGetError'/> - <ref name='virNetworkGetConnect'/> - <ref name='virNetworkGetXMLDesc'/> - <ref name='virNodeGetCellsFreeMemory'/> - </word> <word name='within'> <ref name='virDomainBlockStats'/> <ref name='virDomainInterfaceStats'/> + <ref name='virStoragePoolNumOfVolumes'/> + <ref name='virStorageVolCreateXML'/> + <ref name='virStorageVolGetName'/> + <ref name='virStorageVolLookupByName'/> </word> <word name='without'> <ref name='virDomainSuspend'/> </word> <word name='work'> <ref name='virConnectGetVersion'/> + </word> + <word name='would'> + <ref name='virConnectDiscoverStoragePools'/> </word> <word name='writing'> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> + <ref name='virStoragePoolGetConnect'/> + <ref name='virStorageVolGetConnect'/> </word> <word name='www'> <ref name='virConnectGetType'/> </word> </letter> - </chunk> - <chunk name='chunk8'> <letter name='x'> <word name='xen'> <ref name='virConnectOpen'/> @@ -4563,15 +5350,16 @@ </letter> </chunk> <chunks> - <chunk name='chunk0' start='A' end='U'/> - <chunk name='chunk1' start='V' end='b'/> - <chunk name='chunk2' start='c' end='d'/> - <chunk name='chunk3' start='e' end='i'/> - <chunk name='chunk4' start='j' end='m'/> - <chunk name='chunk5' start='n' end='r'/> - <chunk name='chunk6' start='s' end='t'/> - <chunk name='chunk7' start='u' end='w'/> - <chunk name='chunk8' start='x' end='z'/> + <chunk name='chunk0' start='A' end='P'/> + <chunk name='chunk1' start='R' end='a'/> + <chunk name='chunk2' start='b' end='c'/> + <chunk name='chunk3' start='d' end='f'/> + <chunk name='chunk4' start='g' end='l'/> + <chunk name='chunk5' start='m' end='o'/> + <chunk name='chunk6' start='p' end='r'/> + <chunk name='chunk7' start='s' end='s'/> + <chunk name='chunk8' start='t' end='v'/> + <chunk name='chunk9' start='w' end='z'/> </chunks> </index> </apirefs> -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:31:33AM +0000, Daniel P. Berrange wrote:
This refreshes the API docs to detail the new public APIs which were added by the last patch.
Automatically generated, +1, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

This patch implements support for the storage APIs in the remote driver, server end. This is just the boilerplate code glueing the XDR handlers into the libvirt APIs. remote.c | 766 ++++++++++++++++++++++++++++++++++++++++++ remote_dispatch_localvars.h | 50 ++ remote_dispatch_proc_switch.h | 240 +++++++++++++ remote_dispatch_prototypes.h | 30 + remote_protocol.c | 536 +++++++++++++++++++++++++++++ remote_protocol.h | 437 +++++++++++++++++++++++ remote_protocol.x | 289 +++++++++++++++ 7 files changed, 2347 insertions(+), 1 deletion(-) diff -r 871911d8862d qemud/remote.c --- a/qemud/remote.c Thu Feb 07 12:33:16 2008 -0500 +++ b/qemud/remote.c Thu Feb 07 12:33:21 2008 -0500 @@ -65,8 +65,12 @@ static void remoteDispatchError (struct ATTRIBUTE_FORMAT(printf, 3, 4); static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domain domain); 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 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 pool_src); +static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); #include "remote_dispatch_prototypes.h" @@ -2698,6 +2702,739 @@ remoteDispatchAuthPolkit (struct qemud_s } #endif /* HAVE_POLKIT */ + +/*************************************************************** + * STORAGE POOL APIS + ***************************************************************/ + + +static int +remoteDispatchListDefinedStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_list_defined_storage_pools_args *args, + remote_list_defined_storage_pools_ret *ret) +{ + CHECK_CONN(client); + + if (args->maxnames > REMOTE_NETWORK_NAME_LIST_MAX) { + remoteDispatchError (client, req, + "maxnames > REMOTE_NETWORK_NAME_LIST_MAX"); + return -2; + } + + /* Allocate return buffer. */ + ret->names.names_val = calloc (args->maxnames, sizeof (char *)); + + ret->names.names_len = + virConnectListDefinedStoragePools (client->conn, + ret->names.names_val, args->maxnames); + if (ret->names.names_len == -1) return -1; + + return 0; +} + +static int +remoteDispatchListStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_list_storage_pools_args *args, + remote_list_storage_pools_ret *ret) +{ + CHECK_CONN(client); + + if (args->maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { + remoteDispatchError (client, req, + "maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX"); + return -2; + } + + /* Allocate return buffer. */ + ret->names.names_val = calloc (args->maxnames, sizeof (char *)); + + ret->names.names_len = + virConnectListStoragePools (client->conn, + ret->names.names_val, args->maxnames); + if (ret->names.names_len == -1) return -1; + + return 0; +} + +static int +remoteDispatchDiscoverStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_discover_storage_pools_args *args, + remote_discover_storage_pools_ret *ret) +{ + CHECK_CONN(client); + char **xmlDesc = NULL; + + /* Allocate return buffer. */ + ret->xml.xml_len = + virConnectDiscoverStoragePools (client->conn, + args->hostname, + args->type, + args->flags, + &xmlDesc); + if (ret->xml.xml_len == -1) return -1; + + if (ret->xml.xml_len > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { + int i; + for (i = 0 ; i < ret->xml.xml_len ; i++) + free(xmlDesc[i]); + free(xmlDesc); + remoteDispatchError (client, req, + "xmllen > REMOTE_STORAGE_POOL_NAME_LIST_MAX"); + return -2; + } + + ret->xml.xml_val = xmlDesc; + + return 0; +} + +static int +remoteDispatchStoragePoolCreate (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_create_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + if (virStoragePoolCreate (pool) == -1) { + virStoragePoolFree(pool); + return -1; + } + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_create_xml_args *args, + remote_storage_pool_create_xml_ret *ret) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = virStoragePoolCreateXML (client->conn, args->xml); + if (pool == NULL) return -1; + + make_nonnull_storage_pool (&ret->pool, pool); + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_define_xml_args *args, + remote_storage_pool_define_xml_ret *ret) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = virStoragePoolDefineXML (client->conn, args->xml); + if (pool == NULL) return -1; + + make_nonnull_storage_pool (&ret->pool, pool); + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolBuild (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_build_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + if (virStoragePoolBuild (pool, args->flags) == -1) { + virStoragePoolFree(pool); + return -1; + } + virStoragePoolFree(pool); + return 0; +} + + +static int +remoteDispatchStoragePoolDestroy (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_destroy_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + if (virStoragePoolDestroy (pool) == -1) { + virStoragePoolFree(pool); + return -1; + } + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolDelete (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_delete_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + if (virStoragePoolDelete (pool, args->flags) == -1) { + virStoragePoolFree(pool); + return -1; + } + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolRefresh (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_refresh_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + if (virStoragePoolRefresh (pool, args->flags) == -1) { + virStoragePoolFree(pool); + return -1; + } + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolGetInfo (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_get_info_args *args, + remote_storage_pool_get_info_ret *ret) +{ + virStoragePoolPtr pool; + virStoragePoolInfo info; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + if (virStoragePoolGetInfo (pool, &info) == -1) { + virStoragePoolFree(pool); + return -1; + } + + ret->state = info.state; + ret->capacity = info.capacity; + ret->allocation = info.allocation; + ret->available = info.available; + + virStoragePoolFree(pool); + + return 0; +} + +static int +remoteDispatchStoragePoolDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_dump_xml_args *args, + remote_storage_pool_dump_xml_ret *ret) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + /* remoteDispatchClientRequest will free this. */ + ret->xml = virStoragePoolGetXMLDesc (pool, args->flags); + if (!ret->xml) { + virStoragePoolFree(pool); + return -1; + } + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolGetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_get_autostart_args *args, + remote_storage_pool_get_autostart_ret *ret) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + if (virStoragePoolGetAutostart (pool, &ret->autostart) == -1) { + virStoragePoolFree(pool); + return -1; + } + virStoragePoolFree(pool); + return 0; +} + + +static int +remoteDispatchStoragePoolLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_lookup_by_name_args *args, + remote_storage_pool_lookup_by_name_ret *ret) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = virStoragePoolLookupByName (client->conn, args->name); + if (pool == NULL) return -1; + + make_nonnull_storage_pool (&ret->pool, pool); + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolLookupByUuid (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_lookup_by_uuid_args *args, + remote_storage_pool_lookup_by_uuid_ret *ret) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = virStoragePoolLookupByUUID (client->conn, (unsigned char *) args->uuid); + if (pool == NULL) return -1; + + make_nonnull_storage_pool (&ret->pool, pool); + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolLookupByVolume (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_lookup_by_volume_args *args, + remote_storage_pool_lookup_by_volume_ret *ret) +{ + virStoragePoolPtr pool; + virStorageVolPtr vol; + CHECK_CONN(client); + + vol = get_nonnull_storage_vol (client->conn, args->vol); + + pool = virStoragePoolLookupByVolume (vol); + virStorageVolFree(vol); + if (pool == NULL) return -1; + + make_nonnull_storage_pool (&ret->pool, pool); + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolSetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_set_autostart_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + if (virStoragePoolSetAutostart (pool, args->autostart) == -1) { + virStoragePoolFree(pool); + return -1; + } + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchStoragePoolUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_undefine_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + if (virStoragePoolUndefine (pool) == -1) { + virStoragePoolFree(pool); + return -1; + } + virStoragePoolFree(pool); + return 0; +} + +static int +remoteDispatchNumOfStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + void *args ATTRIBUTE_UNUSED, + remote_num_of_storage_pools_ret *ret) +{ + CHECK_CONN(client); + + ret->num = virConnectNumOfStoragePools (client->conn); + if (ret->num == -1) return -1; + + return 0; +} + +static int +remoteDispatchNumOfDefinedStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + void *args ATTRIBUTE_UNUSED, + remote_num_of_defined_storage_pools_ret *ret) +{ + CHECK_CONN(client); + + ret->num = virConnectNumOfDefinedStoragePools (client->conn); + if (ret->num == -1) return -1; + + return 0; +} + +static int +remoteDispatchStoragePoolListVolumes (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_list_volumes_args *args, + remote_storage_pool_list_volumes_ret *ret) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + if (args->maxnames > REMOTE_STORAGE_VOL_NAME_LIST_MAX) { + remoteDispatchError (client, req, + "maxnames > REMOTE_STORAGE_VOL_NAME_LIST_MAX"); + return -2; + } + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + /* Allocate return buffer. */ + ret->names.names_val = calloc (args->maxnames, sizeof (char *)); + + ret->names.names_len = + virStoragePoolListVolumes (pool, + ret->names.names_val, args->maxnames); + virStoragePoolFree(pool); + if (ret->names.names_len == -1) return -1; + + return 0; +} + + +static int +remoteDispatchStoragePoolNumOfVolumes (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_pool_num_of_volumes_args *args, + remote_storage_pool_num_of_volumes_ret *ret) +{ + virStoragePoolPtr pool; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + ret->num = virStoragePoolNumOfVolumes (pool); + virStoragePoolFree(pool); + if (ret->num == -1) return -1; + + return 0; +} + + +/*************************************************************** + * STORAGE VOL APIS + ***************************************************************/ + + + +static int +remoteDispatchStorageVolCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_vol_create_xml_args *args, + remote_storage_vol_create_xml_ret *ret) +{ + virStoragePoolPtr pool; + virStorageVolPtr vol; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + vol = virStorageVolCreateXML (pool, args->xml, args->flags); + virStoragePoolFree(pool); + if (vol == NULL) return -1; + + make_nonnull_storage_vol (&ret->vol, vol); + virStorageVolFree(vol); + return 0; +} + + +static int +remoteDispatchStorageVolDelete (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_vol_delete_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virStorageVolPtr vol; + CHECK_CONN(client); + + vol = get_nonnull_storage_vol (client->conn, args->vol); + if (vol == NULL) { + remoteDispatchError (client, req, "storage_vol not found"); + return -2; + } + + if (virStorageVolDelete (vol, args->flags) == -1) { + virStorageVolFree(vol); + return -1; + } + virStorageVolFree(vol); + return 0; +} + +static int +remoteDispatchStorageVolGetInfo (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_vol_get_info_args *args, + remote_storage_vol_get_info_ret *ret) +{ + virStorageVolPtr vol; + virStorageVolInfo info; + CHECK_CONN(client); + + vol = get_nonnull_storage_vol (client->conn, args->vol); + if (vol == NULL) { + remoteDispatchError (client, req, "storage_vol not found"); + return -2; + } + + if (virStorageVolGetInfo (vol, &info) == -1) { + virStorageVolFree(vol); + return -1; + } + + ret->type = info.type; + ret->capacity = info.capacity; + ret->allocation = info.allocation; + + virStorageVolFree(vol); + + return 0; +} + +static int +remoteDispatchStorageVolDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_vol_dump_xml_args *args, + remote_storage_vol_dump_xml_ret *ret) +{ + virStorageVolPtr vol; + CHECK_CONN(client); + + vol = get_nonnull_storage_vol (client->conn, args->vol); + if (vol == NULL) { + remoteDispatchError (client, req, "storage_vol not found"); + return -2; + } + + /* remoteDispatchClientRequest will free this. */ + ret->xml = virStorageVolGetXMLDesc (vol, args->flags); + if (!ret->xml) { + virStorageVolFree(vol); + return -1; + } + virStorageVolFree(vol); + return 0; +} + + +static int +remoteDispatchStorageVolGetPath (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_vol_get_path_args *args, + remote_storage_vol_get_path_ret *ret) +{ + virStorageVolPtr vol; + CHECK_CONN(client); + + vol = get_nonnull_storage_vol (client->conn, args->vol); + if (vol == NULL) { + remoteDispatchError (client, req, "storage_vol not found"); + return -2; + } + + /* remoteDispatchClientRequest will free this. */ + ret->name = virStorageVolGetPath (vol); + if (!ret->name) { + virStorageVolFree(vol); + return -1; + } + virStorageVolFree(vol); + return 0; +} + + +static int +remoteDispatchStorageVolLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_vol_lookup_by_name_args *args, + remote_storage_vol_lookup_by_name_ret *ret) +{ + virStoragePoolPtr pool; + virStorageVolPtr vol; + CHECK_CONN(client); + + pool = get_nonnull_storage_pool (client->conn, args->pool); + if (pool == NULL) { + remoteDispatchError (client, req, "storage_pool not found"); + return -2; + } + + vol = virStorageVolLookupByName (pool, args->name); + virStoragePoolFree(pool); + if (vol == NULL) return -1; + + make_nonnull_storage_vol (&ret->vol, vol); + virStorageVolFree(vol); + return 0; +} + +static int +remoteDispatchStorageVolLookupByKey (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_vol_lookup_by_key_args *args, + remote_storage_vol_lookup_by_key_ret *ret) +{ + virStorageVolPtr vol; + CHECK_CONN(client); + + vol = virStorageVolLookupByKey (client->conn, args->key); + if (vol == NULL) return -1; + + make_nonnull_storage_vol (&ret->vol, vol); + virStorageVolFree(vol); + return 0; +} + + +static int +remoteDispatchStorageVolLookupByPath (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_storage_vol_lookup_by_path_args *args, + remote_storage_vol_lookup_by_path_ret *ret) +{ + virStorageVolPtr vol; + CHECK_CONN(client); + + vol = virStorageVolLookupByPath (client->conn, args->path); + if (vol == NULL) return -1; + + make_nonnull_storage_vol (&ret->vol, vol); + virStorageVolFree(vol); + return 0; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire @@ -2724,6 +3461,20 @@ get_nonnull_network (virConnectPtr conn, return virGetNetwork (conn, network.name, BAD_CAST network.uuid); } +static virStoragePoolPtr +get_nonnull_storage_pool (virConnectPtr conn, remote_nonnull_storage_pool pool) +{ + return virGetStoragePool (conn, pool.name, BAD_CAST pool.uuid); +} + +static virStorageVolPtr +get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol) +{ + virStorageVolPtr ret; + ret = virGetStorageVol (conn, vol.pool, vol.name, vol.key); + return ret; +} + /* Make remote_nonnull_domain and remote_nonnull_network. */ static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src) @@ -2738,6 +3489,21 @@ make_nonnull_network (remote_nonnull_net { net_dst->name = strdup (net_src->name); memcpy (net_dst->uuid, net_src->uuid, VIR_UUID_BUFLEN); +} + +static void +make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src) +{ + pool_dst->name = strdup (pool_src->name); + memcpy (pool_dst->uuid, pool_src->uuid, VIR_UUID_BUFLEN); +} + +static void +make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src) +{ + vol_dst->pool = strdup (vol_src->pool); + vol_dst->name = strdup (vol_src->name); + vol_dst->key = strdup (vol_src->key); } /* diff -r 871911d8862d qemud/remote_dispatch_localvars.h --- a/qemud/remote_dispatch_localvars.h Thu Feb 07 12:33:16 2008 -0500 +++ b/qemud/remote_dispatch_localvars.h Thu Feb 07 12:33:21 2008 -0500 @@ -4,10 +4,14 @@ remote_domain_lookup_by_uuid_args lv_remote_domain_lookup_by_uuid_args; remote_domain_lookup_by_uuid_ret lv_remote_domain_lookup_by_uuid_ret; +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_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; remote_get_capabilities_ret lv_remote_get_capabilities_ret; +remote_storage_vol_create_xml_args lv_remote_storage_vol_create_xml_args; +remote_storage_vol_create_xml_ret lv_remote_storage_vol_create_xml_ret; remote_domain_set_max_memory_args lv_remote_domain_set_max_memory_args; remote_auth_sasl_init_ret lv_remote_auth_sasl_init_ret; remote_domain_get_os_type_args lv_remote_domain_get_os_type_args; @@ -19,21 +23,30 @@ remote_network_undefine_args lv_remote_n remote_network_undefine_args lv_remote_network_undefine_args; remote_domain_create_args lv_remote_domain_create_args; remote_network_destroy_args lv_remote_network_destroy_args; +remote_storage_vol_lookup_by_key_args lv_remote_storage_vol_lookup_by_key_args; +remote_storage_vol_lookup_by_key_ret lv_remote_storage_vol_lookup_by_key_ret; +remote_storage_pool_lookup_by_uuid_args lv_remote_storage_pool_lookup_by_uuid_args; +remote_storage_pool_lookup_by_uuid_ret lv_remote_storage_pool_lookup_by_uuid_ret; remote_domain_pin_vcpu_args lv_remote_domain_pin_vcpu_args; remote_list_defined_networks_args lv_remote_list_defined_networks_args; remote_list_defined_networks_ret lv_remote_list_defined_networks_ret; remote_network_create_xml_args lv_remote_network_create_xml_args; remote_network_create_xml_ret lv_remote_network_create_xml_ret; remote_open_args lv_remote_open_args; +remote_storage_pool_refresh_args lv_remote_storage_pool_refresh_args; +remote_storage_vol_lookup_by_path_args lv_remote_storage_vol_lookup_by_path_args; +remote_storage_vol_lookup_by_path_ret lv_remote_storage_vol_lookup_by_path_ret; remote_list_domains_args lv_remote_list_domains_args; remote_list_domains_ret lv_remote_list_domains_ret; remote_network_define_xml_args lv_remote_network_define_xml_args; remote_network_define_xml_ret lv_remote_network_define_xml_ret; remote_get_type_ret lv_remote_get_type_ret; +remote_storage_vol_delete_args lv_remote_storage_vol_delete_args; remote_network_dump_xml_args lv_remote_network_dump_xml_args; remote_network_dump_xml_ret lv_remote_network_dump_xml_ret; remote_domain_reboot_args lv_remote_domain_reboot_args; remote_domain_set_memory_args lv_remote_domain_set_memory_args; +remote_storage_pool_destroy_args lv_remote_storage_pool_destroy_args; remote_domain_create_linux_args lv_remote_domain_create_linux_args; remote_domain_create_linux_ret lv_remote_domain_create_linux_ret; remote_domain_set_scheduler_parameters_args lv_remote_domain_set_scheduler_parameters_args; @@ -42,14 +55,25 @@ remote_auth_sasl_start_ret lv_remote_aut remote_auth_sasl_start_ret lv_remote_auth_sasl_start_ret; remote_domain_interface_stats_args lv_remote_domain_interface_stats_args; remote_domain_interface_stats_ret lv_remote_domain_interface_stats_ret; +remote_storage_pool_dump_xml_args lv_remote_storage_pool_dump_xml_args; +remote_storage_pool_dump_xml_ret lv_remote_storage_pool_dump_xml_ret; +remote_storage_pool_create_xml_args lv_remote_storage_pool_create_xml_args; +remote_storage_pool_create_xml_ret lv_remote_storage_pool_create_xml_ret; +remote_storage_pool_build_args lv_remote_storage_pool_build_args; +remote_storage_pool_delete_args lv_remote_storage_pool_delete_args; +remote_storage_pool_lookup_by_volume_args lv_remote_storage_pool_lookup_by_volume_args; +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_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; +remote_storage_pool_num_of_volumes_ret lv_remote_storage_pool_num_of_volumes_ret; remote_supports_feature_args lv_remote_supports_feature_args; remote_supports_feature_ret lv_remote_supports_feature_ret; remote_domain_lookup_by_name_args lv_remote_domain_lookup_by_name_args; remote_domain_lookup_by_name_ret lv_remote_domain_lookup_by_name_ret; +remote_storage_pool_set_autostart_args lv_remote_storage_pool_set_autostart_args; remote_domain_resume_args lv_remote_domain_resume_args; remote_network_get_bridge_name_args lv_remote_network_get_bridge_name_args; remote_network_get_bridge_name_ret lv_remote_network_get_bridge_name_ret; @@ -69,6 +93,7 @@ remote_domain_block_stats_args lv_remote 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; +remote_num_of_storage_pools_ret lv_remote_num_of_storage_pools_ret; remote_domain_save_args lv_remote_domain_save_args; remote_domain_migrate_prepare_args lv_remote_domain_migrate_prepare_args; remote_domain_migrate_prepare_ret lv_remote_domain_migrate_prepare_ret; @@ -77,28 +102,53 @@ remote_domain_get_scheduler_type_ret lv_ remote_domain_get_scheduler_type_ret lv_remote_domain_get_scheduler_type_ret; remote_get_version_ret lv_remote_get_version_ret; remote_domain_suspend_args lv_remote_domain_suspend_args; +remote_storage_pool_lookup_by_name_args lv_remote_storage_pool_lookup_by_name_args; +remote_storage_pool_lookup_by_name_ret lv_remote_storage_pool_lookup_by_name_ret; remote_network_set_autostart_args lv_remote_network_set_autostart_args; remote_network_get_autostart_args lv_remote_network_get_autostart_args; remote_network_get_autostart_ret lv_remote_network_get_autostart_ret; +remote_storage_pool_create_args lv_remote_storage_pool_create_args; remote_auth_list_ret lv_remote_auth_list_ret; +remote_num_of_defined_storage_pools_ret lv_remote_num_of_defined_storage_pools_ret; remote_domain_core_dump_args lv_remote_domain_core_dump_args; +remote_list_defined_storage_pools_args lv_remote_list_defined_storage_pools_args; +remote_list_defined_storage_pools_ret lv_remote_list_defined_storage_pools_ret; remote_domain_get_max_memory_args lv_remote_domain_get_max_memory_args; remote_domain_get_max_memory_ret lv_remote_domain_get_max_memory_ret; remote_num_of_domains_ret lv_remote_num_of_domains_ret; remote_list_networks_args lv_remote_list_networks_args; remote_list_networks_ret lv_remote_list_networks_ret; +remote_storage_pool_undefine_args lv_remote_storage_pool_undefine_args; remote_domain_set_autostart_args lv_remote_domain_set_autostart_args; +remote_storage_pool_get_autostart_args lv_remote_storage_pool_get_autostart_args; +remote_storage_pool_get_autostart_ret lv_remote_storage_pool_get_autostart_ret; +remote_storage_vol_get_path_args lv_remote_storage_vol_get_path_args; +remote_storage_vol_get_path_ret lv_remote_storage_vol_get_path_ret; remote_domain_lookup_by_id_args lv_remote_domain_lookup_by_id_args; remote_domain_lookup_by_id_ret lv_remote_domain_lookup_by_id_ret; remote_domain_attach_device_args lv_remote_domain_attach_device_args; remote_num_of_networks_ret lv_remote_num_of_networks_ret; +remote_storage_pool_get_info_args lv_remote_storage_pool_get_info_args; +remote_storage_pool_get_info_ret lv_remote_storage_pool_get_info_ret; +remote_discover_storage_pools_args lv_remote_discover_storage_pools_args; +remote_discover_storage_pools_ret lv_remote_discover_storage_pools_ret; +remote_list_storage_pools_args lv_remote_list_storage_pools_args; +remote_list_storage_pools_ret lv_remote_list_storage_pools_ret; remote_domain_restore_args lv_remote_domain_restore_args; remote_network_create_args lv_remote_network_create_args; remote_num_of_defined_networks_ret lv_remote_num_of_defined_networks_ret; +remote_storage_vol_lookup_by_name_args lv_remote_storage_vol_lookup_by_name_args; +remote_storage_vol_lookup_by_name_ret lv_remote_storage_vol_lookup_by_name_ret; +remote_storage_pool_define_xml_args lv_remote_storage_pool_define_xml_args; +remote_storage_pool_define_xml_ret lv_remote_storage_pool_define_xml_ret; remote_network_lookup_by_uuid_args lv_remote_network_lookup_by_uuid_args; remote_network_lookup_by_uuid_ret lv_remote_network_lookup_by_uuid_ret; +remote_storage_vol_get_info_args lv_remote_storage_vol_get_info_args; +remote_storage_vol_get_info_ret lv_remote_storage_vol_get_info_ret; remote_domain_define_xml_args lv_remote_domain_define_xml_args; remote_domain_define_xml_ret lv_remote_domain_define_xml_ret; +remote_storage_vol_dump_xml_args lv_remote_storage_vol_dump_xml_args; +remote_storage_vol_dump_xml_ret lv_remote_storage_vol_dump_xml_ret; remote_domain_dump_xml_args lv_remote_domain_dump_xml_args; remote_domain_dump_xml_ret lv_remote_domain_dump_xml_ret; remote_get_max_vcpus_args lv_remote_get_max_vcpus_args; diff -r 871911d8862d qemud/remote_dispatch_proc_switch.h --- a/qemud/remote_dispatch_proc_switch.h Thu Feb 07 12:33:16 2008 -0500 +++ b/qemud/remote_dispatch_proc_switch.h Thu Feb 07 12:33:21 2008 -0500 @@ -41,6 +41,15 @@ case REMOTE_PROC_CLOSE: case REMOTE_PROC_CLOSE: fn = (dispatch_fn) remoteDispatchClose; break; +case REMOTE_PROC_DISCOVER_STORAGE_POOLS: + fn = (dispatch_fn) remoteDispatchDiscoverStoragePools; + args_filter = (xdrproc_t) xdr_remote_discover_storage_pools_args; + args = (char *) &lv_remote_discover_storage_pools_args; + memset (&lv_remote_discover_storage_pools_args, 0, sizeof lv_remote_discover_storage_pools_args); + ret_filter = (xdrproc_t) xdr_remote_discover_storage_pools_ret; + ret = (char *) &lv_remote_discover_storage_pools_ret; + memset (&lv_remote_discover_storage_pools_ret, 0, sizeof lv_remote_discover_storage_pools_ret); + break; case REMOTE_PROC_DOMAIN_ATTACH_DEVICE: fn = (dispatch_fn) remoteDispatchDomainAttachDevice; args_filter = (xdrproc_t) xdr_remote_domain_attach_device_args; @@ -368,6 +377,15 @@ case REMOTE_PROC_LIST_DEFINED_NETWORKS: ret = (char *) &lv_remote_list_defined_networks_ret; memset (&lv_remote_list_defined_networks_ret, 0, sizeof lv_remote_list_defined_networks_ret); break; +case REMOTE_PROC_LIST_DEFINED_STORAGE_POOLS: + fn = (dispatch_fn) remoteDispatchListDefinedStoragePools; + args_filter = (xdrproc_t) xdr_remote_list_defined_storage_pools_args; + args = (char *) &lv_remote_list_defined_storage_pools_args; + memset (&lv_remote_list_defined_storage_pools_args, 0, sizeof lv_remote_list_defined_storage_pools_args); + ret_filter = (xdrproc_t) xdr_remote_list_defined_storage_pools_ret; + ret = (char *) &lv_remote_list_defined_storage_pools_ret; + memset (&lv_remote_list_defined_storage_pools_ret, 0, sizeof lv_remote_list_defined_storage_pools_ret); + break; case REMOTE_PROC_LIST_DOMAINS: fn = (dispatch_fn) remoteDispatchListDomains; args_filter = (xdrproc_t) xdr_remote_list_domains_args; @@ -386,6 +404,15 @@ case REMOTE_PROC_LIST_NETWORKS: ret = (char *) &lv_remote_list_networks_ret; memset (&lv_remote_list_networks_ret, 0, sizeof lv_remote_list_networks_ret); break; +case REMOTE_PROC_LIST_STORAGE_POOLS: + fn = (dispatch_fn) remoteDispatchListStoragePools; + args_filter = (xdrproc_t) xdr_remote_list_storage_pools_args; + args = (char *) &lv_remote_list_storage_pools_args; + memset (&lv_remote_list_storage_pools_args, 0, sizeof lv_remote_list_storage_pools_args); + ret_filter = (xdrproc_t) xdr_remote_list_storage_pools_ret; + ret = (char *) &lv_remote_list_storage_pools_ret; + memset (&lv_remote_list_storage_pools_ret, 0, sizeof lv_remote_list_storage_pools_ret); + break; case REMOTE_PROC_NETWORK_CREATE: fn = (dispatch_fn) remoteDispatchNetworkCreate; args_filter = (xdrproc_t) xdr_remote_network_create_args; @@ -491,6 +518,12 @@ case REMOTE_PROC_NUM_OF_DEFINED_NETWORKS ret = (char *) &lv_remote_num_of_defined_networks_ret; memset (&lv_remote_num_of_defined_networks_ret, 0, sizeof lv_remote_num_of_defined_networks_ret); break; +case REMOTE_PROC_NUM_OF_DEFINED_STORAGE_POOLS: + fn = (dispatch_fn) remoteDispatchNumOfDefinedStoragePools; + ret_filter = (xdrproc_t) xdr_remote_num_of_defined_storage_pools_ret; + ret = (char *) &lv_remote_num_of_defined_storage_pools_ret; + memset (&lv_remote_num_of_defined_storage_pools_ret, 0, sizeof lv_remote_num_of_defined_storage_pools_ret); + break; case REMOTE_PROC_NUM_OF_DOMAINS: fn = (dispatch_fn) remoteDispatchNumOfDomains; ret_filter = (xdrproc_t) xdr_remote_num_of_domains_ret; @@ -503,11 +536,218 @@ case REMOTE_PROC_NUM_OF_NETWORKS: ret = (char *) &lv_remote_num_of_networks_ret; memset (&lv_remote_num_of_networks_ret, 0, sizeof lv_remote_num_of_networks_ret); break; +case REMOTE_PROC_NUM_OF_STORAGE_POOLS: + fn = (dispatch_fn) remoteDispatchNumOfStoragePools; + ret_filter = (xdrproc_t) xdr_remote_num_of_storage_pools_ret; + ret = (char *) &lv_remote_num_of_storage_pools_ret; + memset (&lv_remote_num_of_storage_pools_ret, 0, sizeof lv_remote_num_of_storage_pools_ret); + break; case REMOTE_PROC_OPEN: fn = (dispatch_fn) remoteDispatchOpen; args_filter = (xdrproc_t) xdr_remote_open_args; args = (char *) &lv_remote_open_args; memset (&lv_remote_open_args, 0, sizeof lv_remote_open_args); + break; +case REMOTE_PROC_STORAGE_POOL_BUILD: + fn = (dispatch_fn) remoteDispatchStoragePoolBuild; + args_filter = (xdrproc_t) xdr_remote_storage_pool_build_args; + args = (char *) &lv_remote_storage_pool_build_args; + memset (&lv_remote_storage_pool_build_args, 0, sizeof lv_remote_storage_pool_build_args); + break; +case REMOTE_PROC_STORAGE_POOL_CREATE: + fn = (dispatch_fn) remoteDispatchStoragePoolCreate; + args_filter = (xdrproc_t) xdr_remote_storage_pool_create_args; + args = (char *) &lv_remote_storage_pool_create_args; + memset (&lv_remote_storage_pool_create_args, 0, sizeof lv_remote_storage_pool_create_args); + break; +case REMOTE_PROC_STORAGE_POOL_CREATE_XML: + fn = (dispatch_fn) remoteDispatchStoragePoolCreateXml; + args_filter = (xdrproc_t) xdr_remote_storage_pool_create_xml_args; + args = (char *) &lv_remote_storage_pool_create_xml_args; + memset (&lv_remote_storage_pool_create_xml_args, 0, sizeof lv_remote_storage_pool_create_xml_args); + ret_filter = (xdrproc_t) xdr_remote_storage_pool_create_xml_ret; + ret = (char *) &lv_remote_storage_pool_create_xml_ret; + memset (&lv_remote_storage_pool_create_xml_ret, 0, sizeof lv_remote_storage_pool_create_xml_ret); + break; +case REMOTE_PROC_STORAGE_POOL_DEFINE_XML: + fn = (dispatch_fn) remoteDispatchStoragePoolDefineXml; + args_filter = (xdrproc_t) xdr_remote_storage_pool_define_xml_args; + args = (char *) &lv_remote_storage_pool_define_xml_args; + memset (&lv_remote_storage_pool_define_xml_args, 0, sizeof lv_remote_storage_pool_define_xml_args); + ret_filter = (xdrproc_t) xdr_remote_storage_pool_define_xml_ret; + ret = (char *) &lv_remote_storage_pool_define_xml_ret; + memset (&lv_remote_storage_pool_define_xml_ret, 0, sizeof lv_remote_storage_pool_define_xml_ret); + break; +case REMOTE_PROC_STORAGE_POOL_DELETE: + fn = (dispatch_fn) remoteDispatchStoragePoolDelete; + args_filter = (xdrproc_t) xdr_remote_storage_pool_delete_args; + args = (char *) &lv_remote_storage_pool_delete_args; + memset (&lv_remote_storage_pool_delete_args, 0, sizeof lv_remote_storage_pool_delete_args); + break; +case REMOTE_PROC_STORAGE_POOL_DESTROY: + fn = (dispatch_fn) remoteDispatchStoragePoolDestroy; + args_filter = (xdrproc_t) xdr_remote_storage_pool_destroy_args; + args = (char *) &lv_remote_storage_pool_destroy_args; + memset (&lv_remote_storage_pool_destroy_args, 0, sizeof lv_remote_storage_pool_destroy_args); + break; +case REMOTE_PROC_STORAGE_POOL_DUMP_XML: + fn = (dispatch_fn) remoteDispatchStoragePoolDumpXml; + args_filter = (xdrproc_t) xdr_remote_storage_pool_dump_xml_args; + args = (char *) &lv_remote_storage_pool_dump_xml_args; + memset (&lv_remote_storage_pool_dump_xml_args, 0, sizeof lv_remote_storage_pool_dump_xml_args); + ret_filter = (xdrproc_t) xdr_remote_storage_pool_dump_xml_ret; + ret = (char *) &lv_remote_storage_pool_dump_xml_ret; + memset (&lv_remote_storage_pool_dump_xml_ret, 0, sizeof lv_remote_storage_pool_dump_xml_ret); + break; +case REMOTE_PROC_STORAGE_POOL_GET_AUTOSTART: + fn = (dispatch_fn) remoteDispatchStoragePoolGetAutostart; + args_filter = (xdrproc_t) xdr_remote_storage_pool_get_autostart_args; + args = (char *) &lv_remote_storage_pool_get_autostart_args; + memset (&lv_remote_storage_pool_get_autostart_args, 0, sizeof lv_remote_storage_pool_get_autostart_args); + ret_filter = (xdrproc_t) xdr_remote_storage_pool_get_autostart_ret; + ret = (char *) &lv_remote_storage_pool_get_autostart_ret; + memset (&lv_remote_storage_pool_get_autostart_ret, 0, sizeof lv_remote_storage_pool_get_autostart_ret); + break; +case REMOTE_PROC_STORAGE_POOL_GET_INFO: + fn = (dispatch_fn) remoteDispatchStoragePoolGetInfo; + args_filter = (xdrproc_t) xdr_remote_storage_pool_get_info_args; + args = (char *) &lv_remote_storage_pool_get_info_args; + memset (&lv_remote_storage_pool_get_info_args, 0, sizeof lv_remote_storage_pool_get_info_args); + ret_filter = (xdrproc_t) xdr_remote_storage_pool_get_info_ret; + ret = (char *) &lv_remote_storage_pool_get_info_ret; + memset (&lv_remote_storage_pool_get_info_ret, 0, sizeof lv_remote_storage_pool_get_info_ret); + break; +case REMOTE_PROC_STORAGE_POOL_LIST_VOLUMES: + fn = (dispatch_fn) remoteDispatchStoragePoolListVolumes; + args_filter = (xdrproc_t) xdr_remote_storage_pool_list_volumes_args; + args = (char *) &lv_remote_storage_pool_list_volumes_args; + memset (&lv_remote_storage_pool_list_volumes_args, 0, sizeof lv_remote_storage_pool_list_volumes_args); + ret_filter = (xdrproc_t) xdr_remote_storage_pool_list_volumes_ret; + ret = (char *) &lv_remote_storage_pool_list_volumes_ret; + memset (&lv_remote_storage_pool_list_volumes_ret, 0, sizeof lv_remote_storage_pool_list_volumes_ret); + break; +case REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_NAME: + fn = (dispatch_fn) remoteDispatchStoragePoolLookupByName; + args_filter = (xdrproc_t) xdr_remote_storage_pool_lookup_by_name_args; + args = (char *) &lv_remote_storage_pool_lookup_by_name_args; + memset (&lv_remote_storage_pool_lookup_by_name_args, 0, sizeof lv_remote_storage_pool_lookup_by_name_args); + ret_filter = (xdrproc_t) xdr_remote_storage_pool_lookup_by_name_ret; + ret = (char *) &lv_remote_storage_pool_lookup_by_name_ret; + memset (&lv_remote_storage_pool_lookup_by_name_ret, 0, sizeof lv_remote_storage_pool_lookup_by_name_ret); + break; +case REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_UUID: + fn = (dispatch_fn) remoteDispatchStoragePoolLookupByUuid; + args_filter = (xdrproc_t) xdr_remote_storage_pool_lookup_by_uuid_args; + args = (char *) &lv_remote_storage_pool_lookup_by_uuid_args; + memset (&lv_remote_storage_pool_lookup_by_uuid_args, 0, sizeof lv_remote_storage_pool_lookup_by_uuid_args); + ret_filter = (xdrproc_t) xdr_remote_storage_pool_lookup_by_uuid_ret; + ret = (char *) &lv_remote_storage_pool_lookup_by_uuid_ret; + memset (&lv_remote_storage_pool_lookup_by_uuid_ret, 0, sizeof lv_remote_storage_pool_lookup_by_uuid_ret); + break; +case REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_VOLUME: + fn = (dispatch_fn) remoteDispatchStoragePoolLookupByVolume; + args_filter = (xdrproc_t) xdr_remote_storage_pool_lookup_by_volume_args; + args = (char *) &lv_remote_storage_pool_lookup_by_volume_args; + memset (&lv_remote_storage_pool_lookup_by_volume_args, 0, sizeof lv_remote_storage_pool_lookup_by_volume_args); + ret_filter = (xdrproc_t) xdr_remote_storage_pool_lookup_by_volume_ret; + ret = (char *) &lv_remote_storage_pool_lookup_by_volume_ret; + memset (&lv_remote_storage_pool_lookup_by_volume_ret, 0, sizeof lv_remote_storage_pool_lookup_by_volume_ret); + break; +case REMOTE_PROC_STORAGE_POOL_NUM_OF_VOLUMES: + fn = (dispatch_fn) remoteDispatchStoragePoolNumOfVolumes; + args_filter = (xdrproc_t) xdr_remote_storage_pool_num_of_volumes_args; + args = (char *) &lv_remote_storage_pool_num_of_volumes_args; + memset (&lv_remote_storage_pool_num_of_volumes_args, 0, sizeof lv_remote_storage_pool_num_of_volumes_args); + ret_filter = (xdrproc_t) xdr_remote_storage_pool_num_of_volumes_ret; + ret = (char *) &lv_remote_storage_pool_num_of_volumes_ret; + memset (&lv_remote_storage_pool_num_of_volumes_ret, 0, sizeof lv_remote_storage_pool_num_of_volumes_ret); + break; +case REMOTE_PROC_STORAGE_POOL_REFRESH: + fn = (dispatch_fn) remoteDispatchStoragePoolRefresh; + args_filter = (xdrproc_t) xdr_remote_storage_pool_refresh_args; + args = (char *) &lv_remote_storage_pool_refresh_args; + memset (&lv_remote_storage_pool_refresh_args, 0, sizeof lv_remote_storage_pool_refresh_args); + break; +case REMOTE_PROC_STORAGE_POOL_SET_AUTOSTART: + fn = (dispatch_fn) remoteDispatchStoragePoolSetAutostart; + args_filter = (xdrproc_t) xdr_remote_storage_pool_set_autostart_args; + args = (char *) &lv_remote_storage_pool_set_autostart_args; + memset (&lv_remote_storage_pool_set_autostart_args, 0, sizeof lv_remote_storage_pool_set_autostart_args); + break; +case REMOTE_PROC_STORAGE_POOL_UNDEFINE: + fn = (dispatch_fn) remoteDispatchStoragePoolUndefine; + args_filter = (xdrproc_t) xdr_remote_storage_pool_undefine_args; + args = (char *) &lv_remote_storage_pool_undefine_args; + memset (&lv_remote_storage_pool_undefine_args, 0, sizeof lv_remote_storage_pool_undefine_args); + break; +case REMOTE_PROC_STORAGE_VOL_CREATE_XML: + fn = (dispatch_fn) remoteDispatchStorageVolCreateXml; + args_filter = (xdrproc_t) xdr_remote_storage_vol_create_xml_args; + args = (char *) &lv_remote_storage_vol_create_xml_args; + memset (&lv_remote_storage_vol_create_xml_args, 0, sizeof lv_remote_storage_vol_create_xml_args); + ret_filter = (xdrproc_t) xdr_remote_storage_vol_create_xml_ret; + ret = (char *) &lv_remote_storage_vol_create_xml_ret; + memset (&lv_remote_storage_vol_create_xml_ret, 0, sizeof lv_remote_storage_vol_create_xml_ret); + break; +case REMOTE_PROC_STORAGE_VOL_DELETE: + fn = (dispatch_fn) remoteDispatchStorageVolDelete; + args_filter = (xdrproc_t) xdr_remote_storage_vol_delete_args; + args = (char *) &lv_remote_storage_vol_delete_args; + memset (&lv_remote_storage_vol_delete_args, 0, sizeof lv_remote_storage_vol_delete_args); + break; +case REMOTE_PROC_STORAGE_VOL_DUMP_XML: + fn = (dispatch_fn) remoteDispatchStorageVolDumpXml; + args_filter = (xdrproc_t) xdr_remote_storage_vol_dump_xml_args; + args = (char *) &lv_remote_storage_vol_dump_xml_args; + memset (&lv_remote_storage_vol_dump_xml_args, 0, sizeof lv_remote_storage_vol_dump_xml_args); + ret_filter = (xdrproc_t) xdr_remote_storage_vol_dump_xml_ret; + ret = (char *) &lv_remote_storage_vol_dump_xml_ret; + memset (&lv_remote_storage_vol_dump_xml_ret, 0, sizeof lv_remote_storage_vol_dump_xml_ret); + break; +case REMOTE_PROC_STORAGE_VOL_GET_INFO: + fn = (dispatch_fn) remoteDispatchStorageVolGetInfo; + args_filter = (xdrproc_t) xdr_remote_storage_vol_get_info_args; + args = (char *) &lv_remote_storage_vol_get_info_args; + memset (&lv_remote_storage_vol_get_info_args, 0, sizeof lv_remote_storage_vol_get_info_args); + ret_filter = (xdrproc_t) xdr_remote_storage_vol_get_info_ret; + ret = (char *) &lv_remote_storage_vol_get_info_ret; + memset (&lv_remote_storage_vol_get_info_ret, 0, sizeof lv_remote_storage_vol_get_info_ret); + break; +case REMOTE_PROC_STORAGE_VOL_GET_PATH: + fn = (dispatch_fn) remoteDispatchStorageVolGetPath; + args_filter = (xdrproc_t) xdr_remote_storage_vol_get_path_args; + args = (char *) &lv_remote_storage_vol_get_path_args; + memset (&lv_remote_storage_vol_get_path_args, 0, sizeof lv_remote_storage_vol_get_path_args); + ret_filter = (xdrproc_t) xdr_remote_storage_vol_get_path_ret; + ret = (char *) &lv_remote_storage_vol_get_path_ret; + memset (&lv_remote_storage_vol_get_path_ret, 0, sizeof lv_remote_storage_vol_get_path_ret); + break; +case REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_KEY: + fn = (dispatch_fn) remoteDispatchStorageVolLookupByKey; + args_filter = (xdrproc_t) xdr_remote_storage_vol_lookup_by_key_args; + args = (char *) &lv_remote_storage_vol_lookup_by_key_args; + memset (&lv_remote_storage_vol_lookup_by_key_args, 0, sizeof lv_remote_storage_vol_lookup_by_key_args); + ret_filter = (xdrproc_t) xdr_remote_storage_vol_lookup_by_key_ret; + ret = (char *) &lv_remote_storage_vol_lookup_by_key_ret; + memset (&lv_remote_storage_vol_lookup_by_key_ret, 0, sizeof lv_remote_storage_vol_lookup_by_key_ret); + break; +case REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_NAME: + fn = (dispatch_fn) remoteDispatchStorageVolLookupByName; + args_filter = (xdrproc_t) xdr_remote_storage_vol_lookup_by_name_args; + args = (char *) &lv_remote_storage_vol_lookup_by_name_args; + memset (&lv_remote_storage_vol_lookup_by_name_args, 0, sizeof lv_remote_storage_vol_lookup_by_name_args); + ret_filter = (xdrproc_t) xdr_remote_storage_vol_lookup_by_name_ret; + ret = (char *) &lv_remote_storage_vol_lookup_by_name_ret; + memset (&lv_remote_storage_vol_lookup_by_name_ret, 0, sizeof lv_remote_storage_vol_lookup_by_name_ret); + break; +case REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_PATH: + fn = (dispatch_fn) remoteDispatchStorageVolLookupByPath; + args_filter = (xdrproc_t) xdr_remote_storage_vol_lookup_by_path_args; + args = (char *) &lv_remote_storage_vol_lookup_by_path_args; + memset (&lv_remote_storage_vol_lookup_by_path_args, 0, sizeof lv_remote_storage_vol_lookup_by_path_args); + ret_filter = (xdrproc_t) xdr_remote_storage_vol_lookup_by_path_ret; + ret = (char *) &lv_remote_storage_vol_lookup_by_path_ret; + memset (&lv_remote_storage_vol_lookup_by_path_ret, 0, sizeof lv_remote_storage_vol_lookup_by_path_ret); break; case REMOTE_PROC_SUPPORTS_FEATURE: fn = (dispatch_fn) remoteDispatchSupportsFeature; diff -r 871911d8862d qemud/remote_dispatch_prototypes.h --- a/qemud/remote_dispatch_prototypes.h Thu Feb 07 12:33:16 2008 -0500 +++ b/qemud/remote_dispatch_prototypes.h Thu Feb 07 12:33:21 2008 -0500 @@ -8,6 +8,7 @@ static int remoteDispatchAuthSaslStart ( static int remoteDispatchAuthSaslStart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); static int remoteDispatchAuthSaslStep (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); static int remoteDispatchClose (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, void *ret); +static int remoteDispatchDiscoverStoragePools (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_discover_storage_pools_args *args, remote_discover_storage_pools_ret *ret); static int remoteDispatchDomainAttachDevice (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_attach_device_args *args, void *ret); static int remoteDispatchDomainBlockStats (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret); static int remoteDispatchDomainCoreDump (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_core_dump_args *args, void *ret); @@ -52,8 +53,10 @@ static int remoteDispatchGetVersion (str static int remoteDispatchGetVersion (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_version_ret *ret); static int remoteDispatchListDefinedDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_defined_domains_args *args, remote_list_defined_domains_ret *ret); static int remoteDispatchListDefinedNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_defined_networks_args *args, remote_list_defined_networks_ret *ret); +static int remoteDispatchListDefinedStoragePools (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_defined_storage_pools_args *args, remote_list_defined_storage_pools_ret *ret); static int remoteDispatchListDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_domains_args *args, remote_list_domains_ret *ret); static int remoteDispatchListNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_networks_args *args, remote_list_networks_ret *ret); +static int remoteDispatchListStoragePools (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_storage_pools_args *args, remote_list_storage_pools_ret *ret); static int remoteDispatchNetworkCreate (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_create_args *args, void *ret); static int remoteDispatchNetworkCreateXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_create_xml_args *args, remote_network_create_xml_ret *ret); static int remoteDispatchNetworkDefineXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_define_xml_args *args, remote_network_define_xml_ret *ret); @@ -68,7 +71,34 @@ static int remoteDispatchNodeGetInfo (st 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 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); static int remoteDispatchNumOfDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_domains_ret *ret); static int remoteDispatchNumOfNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_networks_ret *ret); +static int remoteDispatchNumOfStoragePools (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_storage_pools_ret *ret); static int remoteDispatchOpen (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_open_args *args, void *ret); +static int remoteDispatchStoragePoolBuild (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_build_args *args, void *ret); +static int remoteDispatchStoragePoolCreate (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_create_args *args, void *ret); +static int remoteDispatchStoragePoolCreateXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_create_xml_args *args, remote_storage_pool_create_xml_ret *ret); +static int remoteDispatchStoragePoolDefineXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_define_xml_args *args, remote_storage_pool_define_xml_ret *ret); +static int remoteDispatchStoragePoolDelete (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_delete_args *args, void *ret); +static int remoteDispatchStoragePoolDestroy (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_destroy_args *args, void *ret); +static int remoteDispatchStoragePoolDumpXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_dump_xml_args *args, remote_storage_pool_dump_xml_ret *ret); +static int remoteDispatchStoragePoolGetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_get_autostart_args *args, remote_storage_pool_get_autostart_ret *ret); +static int remoteDispatchStoragePoolGetInfo (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_get_info_args *args, remote_storage_pool_get_info_ret *ret); +static int remoteDispatchStoragePoolListVolumes (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_list_volumes_args *args, remote_storage_pool_list_volumes_ret *ret); +static int remoteDispatchStoragePoolLookupByName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_lookup_by_name_args *args, remote_storage_pool_lookup_by_name_ret *ret); +static int remoteDispatchStoragePoolLookupByUuid (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_lookup_by_uuid_args *args, remote_storage_pool_lookup_by_uuid_ret *ret); +static int remoteDispatchStoragePoolLookupByVolume (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_lookup_by_volume_args *args, remote_storage_pool_lookup_by_volume_ret *ret); +static int remoteDispatchStoragePoolNumOfVolumes (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_num_of_volumes_args *args, remote_storage_pool_num_of_volumes_ret *ret); +static int remoteDispatchStoragePoolRefresh (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_refresh_args *args, void *ret); +static int remoteDispatchStoragePoolSetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_set_autostart_args *args, void *ret); +static int remoteDispatchStoragePoolUndefine (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_pool_undefine_args *args, void *ret); +static int remoteDispatchStorageVolCreateXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_vol_create_xml_args *args, remote_storage_vol_create_xml_ret *ret); +static int remoteDispatchStorageVolDelete (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_vol_delete_args *args, void *ret); +static int remoteDispatchStorageVolDumpXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_vol_dump_xml_args *args, remote_storage_vol_dump_xml_ret *ret); +static int remoteDispatchStorageVolGetInfo (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_vol_get_info_args *args, remote_storage_vol_get_info_ret *ret); +static int remoteDispatchStorageVolGetPath (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_vol_get_path_args *args, remote_storage_vol_get_path_ret *ret); +static int remoteDispatchStorageVolLookupByKey (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_vol_lookup_by_key_args *args, remote_storage_vol_lookup_by_key_ret *ret); +static int remoteDispatchStorageVolLookupByName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_vol_lookup_by_name_args *args, remote_storage_vol_lookup_by_name_ret *ret); +static int remoteDispatchStorageVolLookupByPath (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_storage_vol_lookup_by_path_args *args, remote_storage_vol_lookup_by_path_ret *ret); static int remoteDispatchSupportsFeature (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_supports_feature_args *args, remote_supports_feature_ret *ret); diff -r 871911d8862d qemud/remote_protocol.c --- a/qemud/remote_protocol.c Thu Feb 07 12:33:16 2008 -0500 +++ b/qemud/remote_protocol.c Thu Feb 07 12:33:21 2008 -0500 @@ -58,6 +58,30 @@ xdr_remote_nonnull_network (XDR *xdrs, r } bool_t +xdr_remote_nonnull_storage_pool (XDR *xdrs, remote_nonnull_storage_pool *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + if (!xdr_remote_uuid (xdrs, objp->uuid)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_nonnull_storage_vol (XDR *xdrs, remote_nonnull_storage_vol *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->pool)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->key)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_domain (XDR *xdrs, remote_domain *objp) { @@ -71,6 +95,24 @@ xdr_remote_network (XDR *xdrs, remote_ne { if (!xdr_pointer (xdrs, (char **)objp, sizeof (remote_nonnull_network), (xdrproc_t) xdr_remote_nonnull_network)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool (XDR *xdrs, remote_storage_pool *objp) +{ + + if (!xdr_pointer (xdrs, (char **)objp, sizeof (remote_nonnull_storage_pool), (xdrproc_t) xdr_remote_nonnull_storage_pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol (XDR *xdrs, remote_storage_vol *objp) +{ + + if (!xdr_pointer (xdrs, (char **)objp, sizeof (remote_nonnull_storage_vol), (xdrproc_t) xdr_remote_nonnull_storage_vol)) return FALSE; return TRUE; } @@ -1318,6 +1360,500 @@ xdr_remote_auth_polkit_ret (XDR *xdrs, r } bool_t +xdr_remote_num_of_storage_pools_ret (XDR *xdrs, remote_num_of_storage_pools_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->num)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_list_storage_pools_args (XDR *xdrs, remote_list_storage_pools_args *objp) +{ + + if (!xdr_int (xdrs, &objp->maxnames)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_list_storage_pools_ret (XDR *xdrs, remote_list_storage_pools_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->names.names_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->names.names_len, REMOTE_STORAGE_POOL_NAME_LIST_MAX, + sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_num_of_defined_storage_pools_ret (XDR *xdrs, remote_num_of_defined_storage_pools_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->num)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_list_defined_storage_pools_args (XDR *xdrs, remote_list_defined_storage_pools_args *objp) +{ + + if (!xdr_int (xdrs, &objp->maxnames)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_list_defined_storage_pools_ret (XDR *xdrs, remote_list_defined_storage_pools_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->names.names_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->names.names_len, REMOTE_STORAGE_POOL_NAME_LIST_MAX, + sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_discover_storage_pools_args (XDR *xdrs, remote_discover_storage_pools_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->hostname)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->type)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_discover_storage_pools_ret (XDR *xdrs, remote_discover_storage_pools_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->xml.xml_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->xml.xml_len, REMOTE_STORAGE_POOL_NAME_LIST_MAX, + sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_lookup_by_uuid_args (XDR *xdrs, remote_storage_pool_lookup_by_uuid_args *objp) +{ + + if (!xdr_remote_uuid (xdrs, objp->uuid)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_lookup_by_uuid_ret (XDR *xdrs, remote_storage_pool_lookup_by_uuid_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_lookup_by_name_args (XDR *xdrs, remote_storage_pool_lookup_by_name_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_lookup_by_name_ret (XDR *xdrs, remote_storage_pool_lookup_by_name_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_lookup_by_volume_args (XDR *xdrs, remote_storage_pool_lookup_by_volume_args *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_lookup_by_volume_ret (XDR *xdrs, remote_storage_pool_lookup_by_volume_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_create_xml_args (XDR *xdrs, remote_storage_pool_create_xml_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->xml)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_create_xml_ret (XDR *xdrs, remote_storage_pool_create_xml_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_define_xml_args (XDR *xdrs, remote_storage_pool_define_xml_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->xml)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_define_xml_ret (XDR *xdrs, remote_storage_pool_define_xml_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_build_args (XDR *xdrs, remote_storage_pool_build_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_undefine_args (XDR *xdrs, remote_storage_pool_undefine_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_create_args (XDR *xdrs, remote_storage_pool_create_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_destroy_args (XDR *xdrs, remote_storage_pool_destroy_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_delete_args (XDR *xdrs, remote_storage_pool_delete_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_refresh_args (XDR *xdrs, remote_storage_pool_refresh_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_dump_xml_args (XDR *xdrs, remote_storage_pool_dump_xml_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_dump_xml_ret (XDR *xdrs, remote_storage_pool_dump_xml_ret *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->xml)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_get_info_args (XDR *xdrs, remote_storage_pool_get_info_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_get_info_ret (XDR *xdrs, remote_storage_pool_get_info_ret *objp) +{ + + if (!xdr_u_char (xdrs, &objp->state)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->capacity)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->allocation)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->available)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_get_autostart_args (XDR *xdrs, remote_storage_pool_get_autostart_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_get_autostart_ret (XDR *xdrs, remote_storage_pool_get_autostart_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->autostart)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_set_autostart_args (XDR *xdrs, remote_storage_pool_set_autostart_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + if (!xdr_int (xdrs, &objp->autostart)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_num_of_volumes_args (XDR *xdrs, remote_storage_pool_num_of_volumes_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_num_of_volumes_ret (XDR *xdrs, remote_storage_pool_num_of_volumes_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->num)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_list_volumes_args (XDR *xdrs, remote_storage_pool_list_volumes_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + if (!xdr_int (xdrs, &objp->maxnames)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_pool_list_volumes_ret (XDR *xdrs, remote_storage_pool_list_volumes_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->names.names_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->names.names_len, REMOTE_STORAGE_VOL_NAME_LIST_MAX, + sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_lookup_by_name_args (XDR *xdrs, remote_storage_vol_lookup_by_name_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_lookup_by_name_ret (XDR *xdrs, remote_storage_vol_lookup_by_name_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_lookup_by_key_args (XDR *xdrs, remote_storage_vol_lookup_by_key_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->key)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_lookup_by_key_ret (XDR *xdrs, remote_storage_vol_lookup_by_key_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_lookup_by_path_args (XDR *xdrs, remote_storage_vol_lookup_by_path_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->path)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_lookup_by_path_ret (XDR *xdrs, remote_storage_vol_lookup_by_path_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_create_xml_args (XDR *xdrs, remote_storage_vol_create_xml_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->xml)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_create_xml_ret (XDR *xdrs, remote_storage_vol_create_xml_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_delete_args (XDR *xdrs, remote_storage_vol_delete_args *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_dump_xml_args (XDR *xdrs, remote_storage_vol_dump_xml_args *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_dump_xml_ret (XDR *xdrs, remote_storage_vol_dump_xml_ret *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->xml)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_get_info_args (XDR *xdrs, remote_storage_vol_get_info_args *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_get_info_ret (XDR *xdrs, remote_storage_vol_get_info_ret *objp) +{ + + if (!xdr_char (xdrs, &objp->type)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->capacity)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->allocation)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_get_path_args (XDR *xdrs, remote_storage_vol_get_path_args *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_get_path_ret (XDR *xdrs, remote_storage_vol_get_path_ret *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff -r 871911d8862d qemud/remote_protocol.h --- a/qemud/remote_protocol.h Thu Feb 07 12:33:16 2008 -0500 +++ b/qemud/remote_protocol.h Thu Feb 07 12:33:21 2008 -0500 @@ -27,6 +27,8 @@ typedef remote_nonnull_string *remote_st #define REMOTE_CPUMAPS_MAX 16384 #define REMOTE_MIGRATE_COOKIE_MAX 256 #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_DOMAIN_SCHEDULER_PARAMETERS_MAX 16 #define REMOTE_AUTH_SASL_DATA_MAX 65536 #define REMOTE_AUTH_TYPE_LIST_MAX 20 @@ -46,9 +48,26 @@ struct remote_nonnull_network { }; typedef struct remote_nonnull_network remote_nonnull_network; +struct remote_nonnull_storage_pool { + remote_nonnull_string name; + remote_uuid uuid; +}; +typedef struct remote_nonnull_storage_pool remote_nonnull_storage_pool; + +struct remote_nonnull_storage_vol { + remote_nonnull_string pool; + remote_nonnull_string name; + remote_nonnull_string key; +}; +typedef struct remote_nonnull_storage_vol remote_nonnull_storage_vol; + 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; struct remote_error { int code; @@ -725,6 +744,286 @@ struct remote_auth_polkit_ret { int complete; }; typedef struct remote_auth_polkit_ret remote_auth_polkit_ret; + +struct remote_num_of_storage_pools_ret { + int num; +}; +typedef struct remote_num_of_storage_pools_ret remote_num_of_storage_pools_ret; + +struct remote_list_storage_pools_args { + int maxnames; +}; +typedef struct remote_list_storage_pools_args remote_list_storage_pools_args; + +struct remote_list_storage_pools_ret { + struct { + u_int names_len; + remote_nonnull_string *names_val; + } names; +}; +typedef struct remote_list_storage_pools_ret remote_list_storage_pools_ret; + +struct remote_num_of_defined_storage_pools_ret { + int num; +}; +typedef struct remote_num_of_defined_storage_pools_ret remote_num_of_defined_storage_pools_ret; + +struct remote_list_defined_storage_pools_args { + int maxnames; +}; +typedef struct remote_list_defined_storage_pools_args remote_list_defined_storage_pools_args; + +struct remote_list_defined_storage_pools_ret { + struct { + u_int names_len; + remote_nonnull_string *names_val; + } names; +}; +typedef struct remote_list_defined_storage_pools_ret remote_list_defined_storage_pools_ret; + +struct remote_discover_storage_pools_args { + remote_nonnull_string hostname; + remote_nonnull_string type; + u_int flags; +}; +typedef struct remote_discover_storage_pools_args remote_discover_storage_pools_args; + +struct remote_discover_storage_pools_ret { + struct { + u_int xml_len; + remote_nonnull_string *xml_val; + } xml; +}; +typedef struct remote_discover_storage_pools_ret remote_discover_storage_pools_ret; + +struct remote_storage_pool_lookup_by_uuid_args { + remote_uuid uuid; +}; +typedef struct remote_storage_pool_lookup_by_uuid_args remote_storage_pool_lookup_by_uuid_args; + +struct remote_storage_pool_lookup_by_uuid_ret { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_lookup_by_uuid_ret remote_storage_pool_lookup_by_uuid_ret; + +struct remote_storage_pool_lookup_by_name_args { + remote_nonnull_string name; +}; +typedef struct remote_storage_pool_lookup_by_name_args remote_storage_pool_lookup_by_name_args; + +struct remote_storage_pool_lookup_by_name_ret { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_lookup_by_name_ret remote_storage_pool_lookup_by_name_ret; + +struct remote_storage_pool_lookup_by_volume_args { + remote_nonnull_storage_vol vol; +}; +typedef struct remote_storage_pool_lookup_by_volume_args remote_storage_pool_lookup_by_volume_args; + +struct remote_storage_pool_lookup_by_volume_ret { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_lookup_by_volume_ret remote_storage_pool_lookup_by_volume_ret; + +struct remote_storage_pool_create_xml_args { + remote_nonnull_string xml; +}; +typedef struct remote_storage_pool_create_xml_args remote_storage_pool_create_xml_args; + +struct remote_storage_pool_create_xml_ret { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_create_xml_ret remote_storage_pool_create_xml_ret; + +struct remote_storage_pool_define_xml_args { + remote_nonnull_string xml; +}; +typedef struct remote_storage_pool_define_xml_args remote_storage_pool_define_xml_args; + +struct remote_storage_pool_define_xml_ret { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_define_xml_ret remote_storage_pool_define_xml_ret; + +struct remote_storage_pool_build_args { + remote_nonnull_storage_pool pool; + u_int flags; +}; +typedef struct remote_storage_pool_build_args remote_storage_pool_build_args; + +struct remote_storage_pool_undefine_args { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_undefine_args remote_storage_pool_undefine_args; + +struct remote_storage_pool_create_args { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_create_args remote_storage_pool_create_args; + +struct remote_storage_pool_destroy_args { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_destroy_args remote_storage_pool_destroy_args; + +struct remote_storage_pool_delete_args { + remote_nonnull_storage_pool pool; + u_int flags; +}; +typedef struct remote_storage_pool_delete_args remote_storage_pool_delete_args; + +struct remote_storage_pool_refresh_args { + remote_nonnull_storage_pool pool; + u_int flags; +}; +typedef struct remote_storage_pool_refresh_args remote_storage_pool_refresh_args; + +struct remote_storage_pool_dump_xml_args { + remote_nonnull_storage_pool pool; + u_int flags; +}; +typedef struct remote_storage_pool_dump_xml_args remote_storage_pool_dump_xml_args; + +struct remote_storage_pool_dump_xml_ret { + remote_nonnull_string xml; +}; +typedef struct remote_storage_pool_dump_xml_ret remote_storage_pool_dump_xml_ret; + +struct remote_storage_pool_get_info_args { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_get_info_args remote_storage_pool_get_info_args; + +struct remote_storage_pool_get_info_ret { + u_char state; + u_quad_t capacity; + u_quad_t allocation; + u_quad_t available; +}; +typedef struct remote_storage_pool_get_info_ret remote_storage_pool_get_info_ret; + +struct remote_storage_pool_get_autostart_args { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_get_autostart_args remote_storage_pool_get_autostart_args; + +struct remote_storage_pool_get_autostart_ret { + int autostart; +}; +typedef struct remote_storage_pool_get_autostart_ret remote_storage_pool_get_autostart_ret; + +struct remote_storage_pool_set_autostart_args { + remote_nonnull_storage_pool pool; + int autostart; +}; +typedef struct remote_storage_pool_set_autostart_args remote_storage_pool_set_autostart_args; + +struct remote_storage_pool_num_of_volumes_args { + remote_nonnull_storage_pool pool; +}; +typedef struct remote_storage_pool_num_of_volumes_args remote_storage_pool_num_of_volumes_args; + +struct remote_storage_pool_num_of_volumes_ret { + int num; +}; +typedef struct remote_storage_pool_num_of_volumes_ret remote_storage_pool_num_of_volumes_ret; + +struct remote_storage_pool_list_volumes_args { + remote_nonnull_storage_pool pool; + int maxnames; +}; +typedef struct remote_storage_pool_list_volumes_args remote_storage_pool_list_volumes_args; + +struct remote_storage_pool_list_volumes_ret { + struct { + u_int names_len; + remote_nonnull_string *names_val; + } names; +}; +typedef struct remote_storage_pool_list_volumes_ret remote_storage_pool_list_volumes_ret; + +struct remote_storage_vol_lookup_by_name_args { + remote_nonnull_storage_pool pool; + remote_nonnull_string name; +}; +typedef struct remote_storage_vol_lookup_by_name_args remote_storage_vol_lookup_by_name_args; + +struct remote_storage_vol_lookup_by_name_ret { + remote_nonnull_storage_vol vol; +}; +typedef struct remote_storage_vol_lookup_by_name_ret remote_storage_vol_lookup_by_name_ret; + +struct remote_storage_vol_lookup_by_key_args { + remote_nonnull_string key; +}; +typedef struct remote_storage_vol_lookup_by_key_args remote_storage_vol_lookup_by_key_args; + +struct remote_storage_vol_lookup_by_key_ret { + remote_nonnull_storage_vol vol; +}; +typedef struct remote_storage_vol_lookup_by_key_ret remote_storage_vol_lookup_by_key_ret; + +struct remote_storage_vol_lookup_by_path_args { + remote_nonnull_string path; +}; +typedef struct remote_storage_vol_lookup_by_path_args remote_storage_vol_lookup_by_path_args; + +struct remote_storage_vol_lookup_by_path_ret { + remote_nonnull_storage_vol vol; +}; +typedef struct remote_storage_vol_lookup_by_path_ret remote_storage_vol_lookup_by_path_ret; + +struct remote_storage_vol_create_xml_args { + remote_nonnull_storage_pool pool; + remote_nonnull_string xml; + u_int flags; +}; +typedef struct remote_storage_vol_create_xml_args remote_storage_vol_create_xml_args; + +struct remote_storage_vol_create_xml_ret { + remote_nonnull_storage_vol vol; +}; +typedef struct remote_storage_vol_create_xml_ret remote_storage_vol_create_xml_ret; + +struct remote_storage_vol_delete_args { + remote_nonnull_storage_vol vol; + u_int flags; +}; +typedef struct remote_storage_vol_delete_args remote_storage_vol_delete_args; + +struct remote_storage_vol_dump_xml_args { + remote_nonnull_storage_vol vol; + u_int flags; +}; +typedef struct remote_storage_vol_dump_xml_args remote_storage_vol_dump_xml_args; + +struct remote_storage_vol_dump_xml_ret { + remote_nonnull_string xml; +}; +typedef struct remote_storage_vol_dump_xml_ret remote_storage_vol_dump_xml_ret; + +struct remote_storage_vol_get_info_args { + remote_nonnull_storage_vol vol; +}; +typedef struct remote_storage_vol_get_info_args remote_storage_vol_get_info_args; + +struct remote_storage_vol_get_info_ret { + char type; + u_quad_t capacity; + u_quad_t allocation; +}; +typedef struct remote_storage_vol_get_info_ret remote_storage_vol_get_info_ret; + +struct remote_storage_vol_get_path_args { + remote_nonnull_storage_vol vol; +}; +typedef struct remote_storage_vol_get_path_args remote_storage_vol_get_path_args; + +struct remote_storage_vol_get_path_ret { + remote_nonnull_string name; +}; +typedef struct remote_storage_vol_get_path_ret remote_storage_vol_get_path_ret; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -799,6 +1098,36 @@ enum remote_procedure { REMOTE_PROC_AUTH_SASL_START = 68, REMOTE_PROC_AUTH_SASL_STEP = 69, REMOTE_PROC_AUTH_POLKIT = 70, + REMOTE_PROC_NUM_OF_STORAGE_POOLS = 71, + REMOTE_PROC_LIST_STORAGE_POOLS = 72, + REMOTE_PROC_NUM_OF_DEFINED_STORAGE_POOLS = 73, + REMOTE_PROC_LIST_DEFINED_STORAGE_POOLS = 74, + REMOTE_PROC_DISCOVER_STORAGE_POOLS = 75, + REMOTE_PROC_STORAGE_POOL_CREATE_XML = 76, + REMOTE_PROC_STORAGE_POOL_DEFINE_XML = 77, + REMOTE_PROC_STORAGE_POOL_CREATE = 78, + REMOTE_PROC_STORAGE_POOL_BUILD = 79, + REMOTE_PROC_STORAGE_POOL_DESTROY = 80, + REMOTE_PROC_STORAGE_POOL_DELETE = 81, + REMOTE_PROC_STORAGE_POOL_UNDEFINE = 82, + REMOTE_PROC_STORAGE_POOL_REFRESH = 83, + REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_NAME = 84, + REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_UUID = 85, + REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_VOLUME = 86, + REMOTE_PROC_STORAGE_POOL_GET_INFO = 87, + REMOTE_PROC_STORAGE_POOL_DUMP_XML = 88, + REMOTE_PROC_STORAGE_POOL_GET_AUTOSTART = 89, + REMOTE_PROC_STORAGE_POOL_SET_AUTOSTART = 90, + REMOTE_PROC_STORAGE_POOL_NUM_OF_VOLUMES = 91, + REMOTE_PROC_STORAGE_POOL_LIST_VOLUMES = 92, + REMOTE_PROC_STORAGE_VOL_CREATE_XML = 93, + REMOTE_PROC_STORAGE_VOL_DELETE = 94, + REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_NAME = 95, + REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_KEY = 96, + REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_PATH = 97, + REMOTE_PROC_STORAGE_VOL_GET_INFO = 98, + REMOTE_PROC_STORAGE_VOL_DUMP_XML = 99, + REMOTE_PROC_STORAGE_VOL_GET_PATH = 100, }; typedef enum remote_procedure remote_procedure; @@ -834,8 +1163,12 @@ extern bool_t xdr_remote_uuid (XDR *, r extern bool_t xdr_remote_uuid (XDR *, remote_uuid); extern bool_t xdr_remote_nonnull_domain (XDR *, remote_nonnull_domain*); extern bool_t xdr_remote_nonnull_network (XDR *, remote_nonnull_network*); +extern bool_t xdr_remote_nonnull_storage_pool (XDR *, remote_nonnull_storage_pool*); +extern bool_t xdr_remote_nonnull_storage_vol (XDR *, remote_nonnull_storage_vol*); extern bool_t xdr_remote_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_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*); @@ -943,6 +1276,56 @@ extern bool_t xdr_remote_auth_sasl_step extern bool_t xdr_remote_auth_sasl_step_args (XDR *, remote_auth_sasl_step_args*); extern bool_t xdr_remote_auth_sasl_step_ret (XDR *, remote_auth_sasl_step_ret*); extern bool_t xdr_remote_auth_polkit_ret (XDR *, remote_auth_polkit_ret*); +extern bool_t xdr_remote_num_of_storage_pools_ret (XDR *, remote_num_of_storage_pools_ret*); +extern bool_t xdr_remote_list_storage_pools_args (XDR *, remote_list_storage_pools_args*); +extern bool_t xdr_remote_list_storage_pools_ret (XDR *, remote_list_storage_pools_ret*); +extern bool_t xdr_remote_num_of_defined_storage_pools_ret (XDR *, remote_num_of_defined_storage_pools_ret*); +extern bool_t xdr_remote_list_defined_storage_pools_args (XDR *, remote_list_defined_storage_pools_args*); +extern bool_t xdr_remote_list_defined_storage_pools_ret (XDR *, remote_list_defined_storage_pools_ret*); +extern bool_t xdr_remote_discover_storage_pools_args (XDR *, remote_discover_storage_pools_args*); +extern bool_t xdr_remote_discover_storage_pools_ret (XDR *, remote_discover_storage_pools_ret*); +extern bool_t xdr_remote_storage_pool_lookup_by_uuid_args (XDR *, remote_storage_pool_lookup_by_uuid_args*); +extern bool_t xdr_remote_storage_pool_lookup_by_uuid_ret (XDR *, remote_storage_pool_lookup_by_uuid_ret*); +extern bool_t xdr_remote_storage_pool_lookup_by_name_args (XDR *, remote_storage_pool_lookup_by_name_args*); +extern bool_t xdr_remote_storage_pool_lookup_by_name_ret (XDR *, remote_storage_pool_lookup_by_name_ret*); +extern bool_t xdr_remote_storage_pool_lookup_by_volume_args (XDR *, remote_storage_pool_lookup_by_volume_args*); +extern bool_t xdr_remote_storage_pool_lookup_by_volume_ret (XDR *, remote_storage_pool_lookup_by_volume_ret*); +extern bool_t xdr_remote_storage_pool_create_xml_args (XDR *, remote_storage_pool_create_xml_args*); +extern bool_t xdr_remote_storage_pool_create_xml_ret (XDR *, remote_storage_pool_create_xml_ret*); +extern bool_t xdr_remote_storage_pool_define_xml_args (XDR *, remote_storage_pool_define_xml_args*); +extern bool_t xdr_remote_storage_pool_define_xml_ret (XDR *, remote_storage_pool_define_xml_ret*); +extern bool_t xdr_remote_storage_pool_build_args (XDR *, remote_storage_pool_build_args*); +extern bool_t xdr_remote_storage_pool_undefine_args (XDR *, remote_storage_pool_undefine_args*); +extern bool_t xdr_remote_storage_pool_create_args (XDR *, remote_storage_pool_create_args*); +extern bool_t xdr_remote_storage_pool_destroy_args (XDR *, remote_storage_pool_destroy_args*); +extern bool_t xdr_remote_storage_pool_delete_args (XDR *, remote_storage_pool_delete_args*); +extern bool_t xdr_remote_storage_pool_refresh_args (XDR *, remote_storage_pool_refresh_args*); +extern bool_t xdr_remote_storage_pool_dump_xml_args (XDR *, remote_storage_pool_dump_xml_args*); +extern bool_t xdr_remote_storage_pool_dump_xml_ret (XDR *, remote_storage_pool_dump_xml_ret*); +extern bool_t xdr_remote_storage_pool_get_info_args (XDR *, remote_storage_pool_get_info_args*); +extern bool_t xdr_remote_storage_pool_get_info_ret (XDR *, remote_storage_pool_get_info_ret*); +extern bool_t xdr_remote_storage_pool_get_autostart_args (XDR *, remote_storage_pool_get_autostart_args*); +extern bool_t xdr_remote_storage_pool_get_autostart_ret (XDR *, remote_storage_pool_get_autostart_ret*); +extern bool_t xdr_remote_storage_pool_set_autostart_args (XDR *, remote_storage_pool_set_autostart_args*); +extern bool_t xdr_remote_storage_pool_num_of_volumes_args (XDR *, remote_storage_pool_num_of_volumes_args*); +extern bool_t xdr_remote_storage_pool_num_of_volumes_ret (XDR *, remote_storage_pool_num_of_volumes_ret*); +extern bool_t xdr_remote_storage_pool_list_volumes_args (XDR *, remote_storage_pool_list_volumes_args*); +extern bool_t xdr_remote_storage_pool_list_volumes_ret (XDR *, remote_storage_pool_list_volumes_ret*); +extern bool_t xdr_remote_storage_vol_lookup_by_name_args (XDR *, remote_storage_vol_lookup_by_name_args*); +extern bool_t xdr_remote_storage_vol_lookup_by_name_ret (XDR *, remote_storage_vol_lookup_by_name_ret*); +extern bool_t xdr_remote_storage_vol_lookup_by_key_args (XDR *, remote_storage_vol_lookup_by_key_args*); +extern bool_t xdr_remote_storage_vol_lookup_by_key_ret (XDR *, remote_storage_vol_lookup_by_key_ret*); +extern bool_t xdr_remote_storage_vol_lookup_by_path_args (XDR *, remote_storage_vol_lookup_by_path_args*); +extern bool_t xdr_remote_storage_vol_lookup_by_path_ret (XDR *, remote_storage_vol_lookup_by_path_ret*); +extern bool_t xdr_remote_storage_vol_create_xml_args (XDR *, remote_storage_vol_create_xml_args*); +extern bool_t xdr_remote_storage_vol_create_xml_ret (XDR *, remote_storage_vol_create_xml_ret*); +extern bool_t xdr_remote_storage_vol_delete_args (XDR *, remote_storage_vol_delete_args*); +extern bool_t xdr_remote_storage_vol_dump_xml_args (XDR *, remote_storage_vol_dump_xml_args*); +extern bool_t xdr_remote_storage_vol_dump_xml_ret (XDR *, remote_storage_vol_dump_xml_ret*); +extern bool_t xdr_remote_storage_vol_get_info_args (XDR *, remote_storage_vol_get_info_args*); +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_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_direction (XDR *, remote_message_direction*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -954,8 +1337,12 @@ extern bool_t xdr_remote_uuid (); extern bool_t xdr_remote_uuid (); extern bool_t xdr_remote_nonnull_domain (); extern bool_t xdr_remote_nonnull_network (); +extern bool_t xdr_remote_nonnull_storage_pool (); +extern bool_t xdr_remote_nonnull_storage_vol (); extern bool_t xdr_remote_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_error (); extern bool_t xdr_remote_auth_type (); extern bool_t xdr_remote_vcpu_info (); @@ -1063,6 +1450,56 @@ extern bool_t xdr_remote_auth_sasl_step_ extern bool_t xdr_remote_auth_sasl_step_args (); extern bool_t xdr_remote_auth_sasl_step_ret (); extern bool_t xdr_remote_auth_polkit_ret (); +extern bool_t xdr_remote_num_of_storage_pools_ret (); +extern bool_t xdr_remote_list_storage_pools_args (); +extern bool_t xdr_remote_list_storage_pools_ret (); +extern bool_t xdr_remote_num_of_defined_storage_pools_ret (); +extern bool_t xdr_remote_list_defined_storage_pools_args (); +extern bool_t xdr_remote_list_defined_storage_pools_ret (); +extern bool_t xdr_remote_discover_storage_pools_args (); +extern bool_t xdr_remote_discover_storage_pools_ret (); +extern bool_t xdr_remote_storage_pool_lookup_by_uuid_args (); +extern bool_t xdr_remote_storage_pool_lookup_by_uuid_ret (); +extern bool_t xdr_remote_storage_pool_lookup_by_name_args (); +extern bool_t xdr_remote_storage_pool_lookup_by_name_ret (); +extern bool_t xdr_remote_storage_pool_lookup_by_volume_args (); +extern bool_t xdr_remote_storage_pool_lookup_by_volume_ret (); +extern bool_t xdr_remote_storage_pool_create_xml_args (); +extern bool_t xdr_remote_storage_pool_create_xml_ret (); +extern bool_t xdr_remote_storage_pool_define_xml_args (); +extern bool_t xdr_remote_storage_pool_define_xml_ret (); +extern bool_t xdr_remote_storage_pool_build_args (); +extern bool_t xdr_remote_storage_pool_undefine_args (); +extern bool_t xdr_remote_storage_pool_create_args (); +extern bool_t xdr_remote_storage_pool_destroy_args (); +extern bool_t xdr_remote_storage_pool_delete_args (); +extern bool_t xdr_remote_storage_pool_refresh_args (); +extern bool_t xdr_remote_storage_pool_dump_xml_args (); +extern bool_t xdr_remote_storage_pool_dump_xml_ret (); +extern bool_t xdr_remote_storage_pool_get_info_args (); +extern bool_t xdr_remote_storage_pool_get_info_ret (); +extern bool_t xdr_remote_storage_pool_get_autostart_args (); +extern bool_t xdr_remote_storage_pool_get_autostart_ret (); +extern bool_t xdr_remote_storage_pool_set_autostart_args (); +extern bool_t xdr_remote_storage_pool_num_of_volumes_args (); +extern bool_t xdr_remote_storage_pool_num_of_volumes_ret (); +extern bool_t xdr_remote_storage_pool_list_volumes_args (); +extern bool_t xdr_remote_storage_pool_list_volumes_ret (); +extern bool_t xdr_remote_storage_vol_lookup_by_name_args (); +extern bool_t xdr_remote_storage_vol_lookup_by_name_ret (); +extern bool_t xdr_remote_storage_vol_lookup_by_key_args (); +extern bool_t xdr_remote_storage_vol_lookup_by_key_ret (); +extern bool_t xdr_remote_storage_vol_lookup_by_path_args (); +extern bool_t xdr_remote_storage_vol_lookup_by_path_ret (); +extern bool_t xdr_remote_storage_vol_create_xml_args (); +extern bool_t xdr_remote_storage_vol_create_xml_ret (); +extern bool_t xdr_remote_storage_vol_delete_args (); +extern bool_t xdr_remote_storage_vol_dump_xml_args (); +extern bool_t xdr_remote_storage_vol_dump_xml_ret (); +extern bool_t xdr_remote_storage_vol_get_info_args (); +extern bool_t xdr_remote_storage_vol_get_info_ret (); +extern bool_t xdr_remote_storage_vol_get_path_args (); +extern bool_t xdr_remote_storage_vol_get_path_ret (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_direction (); extern bool_t xdr_remote_message_status (); diff -r 871911d8862d qemud/remote_protocol.x --- a/qemud/remote_protocol.x Thu Feb 07 12:33:16 2008 -0500 +++ b/qemud/remote_protocol.x Thu Feb 07 12:33:21 2008 -0500 @@ -78,6 +78,12 @@ const REMOTE_MIGRATE_COOKIE_MAX = 256; /* Upper limit on lists of network names. */ const REMOTE_NETWORK_NAME_LIST_MAX = 256; +/* Upper limit on lists of storage pool names. */ +const REMOTE_STORAGE_POOL_NAME_LIST_MAX = 256; + +/* Upper limit on lists of storage vol names. */ +const REMOTE_STORAGE_VOL_NAME_LIST_MAX = 1024; + /* Upper limit on list of scheduler parameters. */ const REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX = 16; @@ -103,9 +109,24 @@ struct remote_nonnull_network { remote_uuid uuid; }; +/* A storage pool which may not be NULL. */ +struct remote_nonnull_storage_pool { + remote_nonnull_string name; + remote_uuid uuid; +}; + +/* A storage vol which may not be NULL. */ +struct remote_nonnull_storage_vol { + remote_nonnull_string pool; + remote_nonnull_string name; + remote_nonnull_string key; +}; + /* 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; /* Error message. See <virterror.h> for explanation of fields. */ @@ -626,6 +647,7 @@ struct remote_network_set_autostart_args int autostart; }; + struct remote_auth_list_ret { remote_auth_type types<REMOTE_AUTH_TYPE_LIST_MAX>; }; @@ -659,6 +681,232 @@ struct remote_auth_sasl_step_ret { struct remote_auth_polkit_ret { int complete; +}; + + + +/* Storage pool calls: */ + +struct remote_num_of_storage_pools_ret { + int num; +}; + +struct remote_list_storage_pools_args { + int maxnames; +}; + +struct remote_list_storage_pools_ret { + remote_nonnull_string names<REMOTE_STORAGE_POOL_NAME_LIST_MAX>; +}; + +struct remote_num_of_defined_storage_pools_ret { + int num; +}; + +struct remote_list_defined_storage_pools_args { + int maxnames; +}; + +struct remote_list_defined_storage_pools_ret { + remote_nonnull_string names<REMOTE_STORAGE_POOL_NAME_LIST_MAX>; +}; + +struct remote_discover_storage_pools_args { + remote_nonnull_string hostname; + remote_nonnull_string type; + unsigned flags; +}; + +struct remote_discover_storage_pools_ret { + remote_nonnull_string xml<REMOTE_STORAGE_POOL_NAME_LIST_MAX>; +}; + +struct remote_storage_pool_lookup_by_uuid_args { + remote_uuid uuid; +}; + +struct remote_storage_pool_lookup_by_uuid_ret { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_lookup_by_name_args { + remote_nonnull_string name; +}; + +struct remote_storage_pool_lookup_by_name_ret { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_lookup_by_volume_args { + remote_nonnull_storage_vol vol; +}; + +struct remote_storage_pool_lookup_by_volume_ret { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_create_xml_args { + remote_nonnull_string xml; +}; + +struct remote_storage_pool_create_xml_ret { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_define_xml_args { + remote_nonnull_string xml; +}; + +struct remote_storage_pool_define_xml_ret { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_build_args { + remote_nonnull_storage_pool pool; + unsigned flags; +}; + +struct remote_storage_pool_undefine_args { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_create_args { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_destroy_args { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_delete_args { + remote_nonnull_storage_pool pool; + unsigned flags; +}; + +struct remote_storage_pool_refresh_args { + remote_nonnull_storage_pool pool; + unsigned flags; +}; + +struct remote_storage_pool_dump_xml_args { + remote_nonnull_storage_pool pool; + unsigned flags; +}; + +struct remote_storage_pool_dump_xml_ret { + remote_nonnull_string xml; +}; + +struct remote_storage_pool_get_info_args { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_get_info_ret { + unsigned char state; + unsigned hyper capacity; + unsigned hyper allocation; + unsigned hyper available; +}; + +struct remote_storage_pool_get_autostart_args { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_get_autostart_ret { + int autostart; +}; + +struct remote_storage_pool_set_autostart_args { + remote_nonnull_storage_pool pool; + int autostart; +}; + +struct remote_storage_pool_num_of_volumes_args { + remote_nonnull_storage_pool pool; +}; + +struct remote_storage_pool_num_of_volumes_ret { + int num; +}; + +struct remote_storage_pool_list_volumes_args { + remote_nonnull_storage_pool pool; + int maxnames; +}; + +struct remote_storage_pool_list_volumes_ret { + remote_nonnull_string names<REMOTE_STORAGE_VOL_NAME_LIST_MAX>; +}; + + + +/* Storage vol calls: */ + +struct remote_storage_vol_lookup_by_name_args { + remote_nonnull_storage_pool pool; + remote_nonnull_string name; +}; + +struct remote_storage_vol_lookup_by_name_ret { + remote_nonnull_storage_vol vol; +}; + +struct remote_storage_vol_lookup_by_key_args { + remote_nonnull_string key; +}; + +struct remote_storage_vol_lookup_by_key_ret { + remote_nonnull_storage_vol vol; +}; + +struct remote_storage_vol_lookup_by_path_args { + remote_nonnull_string path; +}; + +struct remote_storage_vol_lookup_by_path_ret { + remote_nonnull_storage_vol vol; +}; + +struct remote_storage_vol_create_xml_args { + remote_nonnull_storage_pool pool; + remote_nonnull_string xml; + unsigned flags; +}; + +struct remote_storage_vol_create_xml_ret { + remote_nonnull_storage_vol vol; +}; + +struct remote_storage_vol_delete_args { + remote_nonnull_storage_vol vol; + unsigned flags; +}; + +struct remote_storage_vol_dump_xml_args { + remote_nonnull_storage_vol vol; + unsigned flags; +}; + +struct remote_storage_vol_dump_xml_ret { + remote_nonnull_string xml; +}; + +struct remote_storage_vol_get_info_args { + remote_nonnull_storage_vol vol; +}; + +struct remote_storage_vol_get_info_ret { + char type; + unsigned hyper capacity; + unsigned hyper allocation; +}; + +struct remote_storage_vol_get_path_args { + remote_nonnull_storage_vol vol; +}; + +struct remote_storage_vol_get_path_ret { + remote_nonnull_string name; }; /*----- Protocol. -----*/ @@ -678,6 +926,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_ATTACH_DEVICE = 8, REMOTE_PROC_DOMAIN_CREATE = 9, REMOTE_PROC_DOMAIN_CREATE_LINUX = 10, + REMOTE_PROC_DOMAIN_DEFINE_XML = 11, REMOTE_PROC_DOMAIN_DESTROY = 12, REMOTE_PROC_DOMAIN_DETACH_DEVICE = 13, @@ -688,6 +937,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_MAX_VCPUS = 18, REMOTE_PROC_DOMAIN_GET_OS_TYPE = 19, REMOTE_PROC_DOMAIN_GET_VCPUS = 20, + REMOTE_PROC_LIST_DEFINED_DOMAINS = 21, REMOTE_PROC_DOMAIN_LOOKUP_BY_ID = 22, REMOTE_PROC_DOMAIN_LOOKUP_BY_NAME = 23, @@ -698,6 +948,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_RESUME = 28, REMOTE_PROC_DOMAIN_SET_AUTOSTART = 29, REMOTE_PROC_DOMAIN_SET_MAX_MEMORY = 30, + REMOTE_PROC_DOMAIN_SET_MEMORY = 31, REMOTE_PROC_DOMAIN_SET_VCPUS = 32, REMOTE_PROC_DOMAIN_SHUTDOWN = 33, @@ -708,6 +959,7 @@ enum remote_procedure { REMOTE_PROC_LIST_NETWORKS = 38, REMOTE_PROC_NETWORK_CREATE = 39, REMOTE_PROC_NETWORK_CREATE_XML = 40, + REMOTE_PROC_NETWORK_DEFINE_XML = 41, REMOTE_PROC_NETWORK_DESTROY = 42, REMOTE_PROC_NETWORK_DUMP_XML = 43, @@ -718,6 +970,7 @@ enum remote_procedure { REMOTE_PROC_NETWORK_SET_AUTOSTART = 48, REMOTE_PROC_NETWORK_UNDEFINE = 49, REMOTE_PROC_NUM_OF_DEFINED_NETWORKS = 50, + REMOTE_PROC_NUM_OF_DOMAINS = 51, REMOTE_PROC_NUM_OF_NETWORKS = 52, REMOTE_PROC_DOMAIN_CORE_DUMP = 53, @@ -728,6 +981,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS = 58, REMOTE_PROC_GET_HOSTNAME = 59, REMOTE_PROC_SUPPORTS_FEATURE = 60, + REMOTE_PROC_DOMAIN_MIGRATE_PREPARE = 61, REMOTE_PROC_DOMAIN_MIGRATE_PERFORM = 62, REMOTE_PROC_DOMAIN_MIGRATE_FINISH = 63, @@ -737,7 +991,40 @@ enum remote_procedure { REMOTE_PROC_AUTH_SASL_INIT = 67, REMOTE_PROC_AUTH_SASL_START = 68, REMOTE_PROC_AUTH_SASL_STEP = 69, - REMOTE_PROC_AUTH_POLKIT = 70 + REMOTE_PROC_AUTH_POLKIT = 70, + + REMOTE_PROC_NUM_OF_STORAGE_POOLS = 71, + REMOTE_PROC_LIST_STORAGE_POOLS = 72, + REMOTE_PROC_NUM_OF_DEFINED_STORAGE_POOLS = 73, + REMOTE_PROC_LIST_DEFINED_STORAGE_POOLS = 74, + REMOTE_PROC_DISCOVER_STORAGE_POOLS = 75, + REMOTE_PROC_STORAGE_POOL_CREATE_XML = 76, + REMOTE_PROC_STORAGE_POOL_DEFINE_XML = 77, + REMOTE_PROC_STORAGE_POOL_CREATE = 78, + REMOTE_PROC_STORAGE_POOL_BUILD = 79, + REMOTE_PROC_STORAGE_POOL_DESTROY = 80, + + REMOTE_PROC_STORAGE_POOL_DELETE = 81, + REMOTE_PROC_STORAGE_POOL_UNDEFINE = 82, + REMOTE_PROC_STORAGE_POOL_REFRESH = 83, + REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_NAME = 84, + REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_UUID = 85, + REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_VOLUME = 86, + REMOTE_PROC_STORAGE_POOL_GET_INFO = 87, + REMOTE_PROC_STORAGE_POOL_DUMP_XML = 88, + REMOTE_PROC_STORAGE_POOL_GET_AUTOSTART = 89, + REMOTE_PROC_STORAGE_POOL_SET_AUTOSTART = 90, + + REMOTE_PROC_STORAGE_POOL_NUM_OF_VOLUMES = 91, + REMOTE_PROC_STORAGE_POOL_LIST_VOLUMES = 92, + REMOTE_PROC_STORAGE_VOL_CREATE_XML = 93, + REMOTE_PROC_STORAGE_VOL_DELETE = 94, + REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_NAME = 95, + REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_KEY = 96, + REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_PATH = 97, + REMOTE_PROC_STORAGE_VOL_GET_INFO = 98, + REMOTE_PROC_STORAGE_VOL_DUMP_XML = 99, + REMOTE_PROC_STORAGE_VOL_GET_PATH = 100 }; /* Custom RPC structure. */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:32:20AM +0000, Daniel P. Berrange wrote:
This patch implements support for the storage APIs in the remote driver, server end. This is just the boilerplate code glueing the XDR handlers into the libvirt APIs.
Seems a direct result of the APIs, then +1 after regeneration Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

This patch adds support for the storage APIs to the remote driver client end. As with the network driver, things are configured such that all virtualization drivers will end up delegating to the remote driver for storage APIs. There's not much interesting to say about this patch since it just follows pattern for the existing APis. src/remote_internal.c | 840 +++++++++++++++++++++++++++++++++++- 1 files changed, 840 insertions(+), 10 deletions(-) diff -r f9eaa2e376e0 src/remote_internal.c --- a/src/remote_internal.c Tue Feb 05 13:29:02 2008 -0500 +++ b/src/remote_internal.c Tue Feb 05 16:26:37 2008 -0500 @@ -89,7 +89,7 @@ struct private_data { gnutls_session_t session; /* GnuTLS session (if uses_tls != 0). */ char *type; /* Cached return from remoteType. */ int counter; /* Generates serial numbers for RPC. */ - int networkOnly; /* Only used for network API */ + int localUses; /* Ref count for private data */ char *hostname; /* Original hostname */ FILE *debugLog; /* Debug remote protocol */ #if HAVE_SASL @@ -115,6 +115,15 @@ struct private_data { _("tried to use a closed or uninitialised handle")); \ return (retcode); \ } + +#define GET_STORAGE_PRIVATE(conn,retcode) \ + struct private_data *priv = (struct private_data *) (conn)->storagePrivateData; \ + 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, @@ -140,8 +149,12 @@ static void server_error (virConnectPtr static void server_error (virConnectPtr conn, remote_error *err); static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domain domain); 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 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); +static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); /*----------------------------------------------------------------------*/ @@ -2324,7 +2337,7 @@ remoteNetworkOpen (virConnectPtr conn, free(priv); } else { priv->magic = MAGIC; - priv->networkOnly = 1; + priv->localUses = 1; conn->networkPrivateData = priv; } return ret; @@ -2336,10 +2349,13 @@ remoteNetworkClose (virConnectPtr conn) { int ret = 0; GET_NETWORK_PRIVATE (conn, -1); - if (priv->networkOnly) { - ret = doRemoteClose(conn, priv); - free(priv); - conn->networkPrivateData = NULL; + if (priv->localUses) { + priv->localUses--; + if (!priv->localUses) { + ret = doRemoteClose(conn, priv); + free(priv); + conn->networkPrivateData = NULL; + } } return ret; } @@ -2666,6 +2682,753 @@ remoteNetworkSetAutostart (virNetworkPtr return 0; } + + + + +/*----------------------------------------------------------------------*/ + +static int +remoteStorageOpen (virConnectPtr conn, + xmlURIPtr uri, + virConnectAuthPtr auth, + int flags) +{ + if (inside_daemon) + return VIR_DRV_OPEN_DECLINED; + + if (conn && + conn->driver && + strcmp (conn->driver->name, "remote") == 0) { + /* 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->storagePrivateData = conn->privateData; + return VIR_DRV_OPEN_SUCCESS; + } else if (conn->networkDriver && + strcmp (conn->networkDriver->name, "remote") == 0) { + conn->storagePrivateData = conn->networkPrivateData; + ((struct private_data *)conn->storagePrivateData)->localUses++; + return VIR_DRV_OPEN_SUCCESS; + } else { + /* Using a non-remote driver, so we need to open a + * new connection for network APIs, forcing it to + * use the UNIX transport. This handles Xen driver + * which doesn't have its own impl of the network APIs. + */ + struct private_data *priv = malloc (sizeof(struct private_data)); + int ret, rflags = 0; + if (!priv) { + error (NULL, VIR_ERR_NO_MEMORY, _("struct private_data")); + return VIR_DRV_OPEN_ERROR; + } + if (flags & VIR_CONNECT_RO) + rflags |= VIR_DRV_OPEN_REMOTE_RO; + rflags |= VIR_DRV_OPEN_REMOTE_UNIX; + + memset(priv, 0, sizeof(struct private_data)); + priv->magic = DEAD; + priv->sock = -1; + ret = doRemoteOpen(conn, priv, uri, auth, rflags); + if (ret != VIR_DRV_OPEN_SUCCESS) { + conn->storagePrivateData = NULL; + free(priv); + } else { + priv->magic = MAGIC; + priv->localUses = 1; + conn->storagePrivateData = priv; + } + return ret; + } +} + +static int +remoteStorageClose (virConnectPtr conn) +{ + int ret = 0; + GET_STORAGE_PRIVATE (conn, -1); + if (priv->localUses) { + priv->localUses--; + if (!priv->localUses) { + ret = doRemoteClose(conn, priv); + free(priv); + conn->storagePrivateData = NULL; + } + } + return ret; +} + +static int +remoteNumOfStoragePools (virConnectPtr conn) +{ + remote_num_of_storage_pools_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_STORAGE_POOLS, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_num_of_storage_pools_ret, (char *) &ret) == -1) + return -1; + + return ret.num; +} + +static int +remoteListStoragePools (virConnectPtr conn, char **const names, int maxnames) +{ + int i; + remote_list_storage_pools_args args; + remote_list_storage_pools_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + + if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { + error (conn, VIR_ERR_RPC, _("too many storage pools requested")); + return -1; + } + args.maxnames = maxnames; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_LIST_STORAGE_POOLS, + (xdrproc_t) xdr_remote_list_storage_pools_args, (char *) &args, + (xdrproc_t) xdr_remote_list_storage_pools_ret, (char *) &ret) == -1) + return -1; + + if (ret.names.names_len > maxnames) { + error (conn, VIR_ERR_RPC, _("too many storage pools received")); + xdr_free ((xdrproc_t) xdr_remote_list_storage_pools_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_list_storage_pools_ret, (char *) &ret); + + return ret.names.names_len; +} + +static int +remoteNumOfDefinedStoragePools (virConnectPtr conn) +{ + remote_num_of_defined_storage_pools_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_STORAGE_POOLS, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_num_of_defined_storage_pools_ret, (char *) &ret) == -1) + return -1; + + return ret.num; +} + +static int +remoteListDefinedStoragePools (virConnectPtr conn, + char **const names, int maxnames) +{ + int i; + remote_list_defined_storage_pools_args args; + remote_list_defined_storage_pools_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + + if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { + error (conn, VIR_ERR_RPC, _("too many storage pools requested")); + return -1; + } + args.maxnames = maxnames; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_LIST_DEFINED_STORAGE_POOLS, + (xdrproc_t) xdr_remote_list_defined_storage_pools_args, (char *) &args, + (xdrproc_t) xdr_remote_list_defined_storage_pools_ret, (char *) &ret) == -1) + return -1; + + if (ret.names.names_len > maxnames) { + error (conn, VIR_ERR_RPC, _("too many storage pools received")); + xdr_free ((xdrproc_t) xdr_remote_list_defined_storage_pools_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_list_defined_storage_pools_ret, (char *) &ret); + + return ret.names.names_len; +} + +static int +remoteDiscoverStoragePools (virConnectPtr conn, + const char *hostname, + const char *type, + unsigned int flags, + char ***xmlDesc) +{ + remote_discover_storage_pools_args args; + remote_discover_storage_pools_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + + args.hostname = (char*)hostname; + args.type = (char*)type; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_DISCOVER_STORAGE_POOLS, + (xdrproc_t) xdr_remote_discover_storage_pools_args, (char *) &args, + (xdrproc_t) xdr_remote_discover_storage_pools_ret, (char *) &ret) == -1) + return -1; + + *xmlDesc = ret.xml.xml_val; + ret.xml.xml_val = NULL; /* To stop xdr_free free'ing it */ + + xdr_free ((xdrproc_t) xdr_remote_discover_storage_pools_ret, (char *) &ret); + + return ret.xml.xml_len; +} + +static virStoragePoolPtr +remoteStoragePoolLookupByUUID (virConnectPtr conn, + const unsigned char *uuid) +{ + virStoragePoolPtr pool; + remote_storage_pool_lookup_by_uuid_args args; + remote_storage_pool_lookup_by_uuid_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + memcpy (args.uuid, uuid, VIR_UUID_BUFLEN); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_UUID, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_uuid_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_uuid_ret, (char *) &ret) == -1) + return NULL; + + pool = get_nonnull_storage_pool (conn, ret.pool); + xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_uuid_ret, (char *) &ret); + + return pool; +} + +static virStoragePoolPtr +remoteStoragePoolLookupByName (virConnectPtr conn, + const char *name) +{ + virStoragePoolPtr pool; + remote_storage_pool_lookup_by_name_args args; + remote_storage_pool_lookup_by_name_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + args.name = (char *) name; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_NAME, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_name_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_name_ret, (char *) &ret) == -1) + return NULL; + + pool = get_nonnull_storage_pool (conn, ret.pool); + xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_name_ret, (char *) &ret); + + return pool; +} + +static virStoragePoolPtr +remoteStoragePoolLookupByVolume (virStorageVolPtr vol) +{ + virStoragePoolPtr pool; + remote_storage_pool_lookup_by_volume_args args; + remote_storage_pool_lookup_by_volume_ret ret; + GET_STORAGE_PRIVATE (vol->conn, NULL); + + make_nonnull_storage_vol (&args.vol, vol); + + memset (&ret, 0, sizeof ret); + if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_VOLUME, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_volume_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_volume_ret, (char *) &ret) == -1) + return NULL; + + pool = get_nonnull_storage_pool (vol->conn, ret.pool); + xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_volume_ret, (char *) &ret); + + return pool; +} + + +static virStoragePoolPtr +remoteStoragePoolCreateXML (virConnectPtr conn, const char *xmlDesc) +{ + virStoragePoolPtr pool; + remote_storage_pool_create_xml_args args; + remote_storage_pool_create_xml_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + args.xml = (char *) xmlDesc; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_CREATE_XML, + (xdrproc_t) xdr_remote_storage_pool_create_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_create_xml_ret, (char *) &ret) == -1) + return NULL; + + pool = get_nonnull_storage_pool (conn, ret.pool); + xdr_free ((xdrproc_t) &xdr_remote_storage_pool_create_xml_ret, (char *) &ret); + + return pool; +} + +static virStoragePoolPtr +remoteStoragePoolDefineXML (virConnectPtr conn, const char *xml) +{ + virStoragePoolPtr pool; + remote_storage_pool_define_xml_args args; + remote_storage_pool_define_xml_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + args.xml = (char *) xml; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DEFINE_XML, + (xdrproc_t) xdr_remote_storage_pool_define_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_define_xml_ret, (char *) &ret) == -1) + return NULL; + + pool = get_nonnull_storage_pool (conn, ret.pool); + xdr_free ((xdrproc_t) &xdr_remote_storage_pool_define_xml_ret, (char *) &ret); + + return pool; +} + +static int +remoteStoragePoolUndefine (virStoragePoolPtr pool) +{ + remote_storage_pool_undefine_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_UNDEFINE, + (xdrproc_t) xdr_remote_storage_pool_undefine_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolCreate (virStoragePoolPtr pool) +{ + remote_storage_pool_create_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_CREATE, + (xdrproc_t) xdr_remote_storage_pool_create_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolBuild (virStoragePoolPtr pool, + unsigned int flags) +{ + remote_storage_pool_build_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + args.flags = flags; + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_BUILD, + (xdrproc_t) xdr_remote_storage_pool_build_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolDestroy (virStoragePoolPtr pool) +{ + remote_storage_pool_destroy_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DESTROY, + (xdrproc_t) xdr_remote_storage_pool_destroy_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolDelete (virStoragePoolPtr pool, + unsigned int flags) +{ + remote_storage_pool_delete_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + args.flags = flags; + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DELETE, + (xdrproc_t) xdr_remote_storage_pool_delete_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolRefresh (virStoragePoolPtr pool, + unsigned int flags) +{ + remote_storage_pool_refresh_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + args.flags = flags; + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_REFRESH, + (xdrproc_t) xdr_remote_storage_pool_refresh_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolGetInfo (virStoragePoolPtr pool, virStoragePoolInfoPtr info) +{ + remote_storage_pool_get_info_args args; + remote_storage_pool_get_info_ret ret; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_GET_INFO, + (xdrproc_t) xdr_remote_storage_pool_get_info_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_get_info_ret, (char *) &ret) == -1) + return -1; + + info->state = ret.state; + info->capacity = ret.capacity; + info->allocation = ret.allocation; + info->available = ret.available; + + return 0; +} + +static char * +remoteStoragePoolDumpXML (virStoragePoolPtr pool, + unsigned int flags) +{ + remote_storage_pool_dump_xml_args args; + remote_storage_pool_dump_xml_ret ret; + GET_STORAGE_PRIVATE (pool->conn, NULL); + + make_nonnull_storage_pool (&args.pool, pool); + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DUMP_XML, + (xdrproc_t) xdr_remote_storage_pool_dump_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_dump_xml_ret, (char *) &ret) == -1) + return NULL; + + /* Caller frees. */ + return ret.xml; +} + +static int +remoteStoragePoolGetAutostart (virStoragePoolPtr pool, int *autostart) +{ + remote_storage_pool_get_autostart_args args; + remote_storage_pool_get_autostart_ret ret; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_GET_AUTOSTART, + (xdrproc_t) xdr_remote_storage_pool_get_autostart_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_get_autostart_ret, (char *) &ret) == -1) + return -1; + + if (autostart) *autostart = ret.autostart; + + return 0; +} + +static int +remoteStoragePoolSetAutostart (virStoragePoolPtr pool, int autostart) +{ + remote_storage_pool_set_autostart_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + args.autostart = autostart; + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_SET_AUTOSTART, + (xdrproc_t) xdr_remote_storage_pool_set_autostart_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + + +static int +remoteStoragePoolNumOfVolumes (virStoragePoolPtr pool) +{ + remote_storage_pool_num_of_volumes_args args; + remote_storage_pool_num_of_volumes_ret ret; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool(&args.pool, pool); + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_NUM_OF_VOLUMES, + (xdrproc_t) xdr_remote_storage_pool_num_of_volumes_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_num_of_volumes_ret, (char *) &ret) == -1) + return -1; + + return ret.num; +} + +static int +remoteStoragePoolListVolumes (virStoragePoolPtr pool, char **const names, int maxnames) +{ + int i; + remote_storage_pool_list_volumes_args args; + remote_storage_pool_list_volumes_ret ret; + GET_STORAGE_PRIVATE (pool->conn, -1); + + if (maxnames > REMOTE_STORAGE_VOL_NAME_LIST_MAX) { + error (pool->conn, VIR_ERR_RPC, _("too many storage volumes requested")); + return -1; + } + args.maxnames = maxnames; + make_nonnull_storage_pool(&args.pool, pool); + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LIST_VOLUMES, + (xdrproc_t) xdr_remote_storage_pool_list_volumes_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_list_volumes_ret, (char *) &ret) == -1) + return -1; + + if (ret.names.names_len > maxnames) { + error (pool->conn, VIR_ERR_RPC, _("too many storage volumes received")); + xdr_free ((xdrproc_t) xdr_remote_storage_pool_list_volumes_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_storage_pool_list_volumes_ret, (char *) &ret); + + return ret.names.names_len; +} + + + +static virStorageVolPtr +remoteStorageVolLookupByName (virStoragePoolPtr pool, + const char *name) +{ + virStorageVolPtr vol; + remote_storage_vol_lookup_by_name_args args; + remote_storage_vol_lookup_by_name_ret ret; + GET_STORAGE_PRIVATE (pool->conn, NULL); + + make_nonnull_storage_pool(&args.pool, pool); + args.name = (char *) name; + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_NAME, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_name_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_name_ret, (char *) &ret) == -1) + return NULL; + + vol = get_nonnull_storage_vol (pool->conn, ret.vol); + xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_name_ret, (char *) &ret); + + return vol; +} + +static virStorageVolPtr +remoteStorageVolLookupByKey (virConnectPtr conn, + const char *key) +{ + virStorageVolPtr vol; + remote_storage_vol_lookup_by_key_args args; + remote_storage_vol_lookup_by_key_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + args.key = (char *) key; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_KEY, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_key_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_key_ret, (char *) &ret) == -1) + return NULL; + + vol = get_nonnull_storage_vol (conn, ret.vol); + xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_key_ret, (char *) &ret); + + return vol; +} + +static virStorageVolPtr +remoteStorageVolLookupByPath (virConnectPtr conn, + const char *path) +{ + virStorageVolPtr vol; + remote_storage_vol_lookup_by_path_args args; + remote_storage_vol_lookup_by_path_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + args.path = (char *) path; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_PATH, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_path_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_path_ret, (char *) &ret) == -1) + return NULL; + + vol = get_nonnull_storage_vol (conn, ret.vol); + xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_path_ret, (char *) &ret); + + return vol; +} + +static virStorageVolPtr +remoteStorageVolCreateXML (virStoragePoolPtr pool, const char *xmlDesc, + unsigned int flags) +{ + virStorageVolPtr vol; + remote_storage_vol_create_xml_args args; + remote_storage_vol_create_xml_ret ret; + GET_STORAGE_PRIVATE (pool->conn, NULL); + + make_nonnull_storage_pool (&args.pool, pool); + args.xml = (char *) xmlDesc; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_CREATE_XML, + (xdrproc_t) xdr_remote_storage_vol_create_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_create_xml_ret, (char *) &ret) == -1) + return NULL; + + vol = get_nonnull_storage_vol (pool->conn, ret.vol); + xdr_free ((xdrproc_t) &xdr_remote_storage_vol_create_xml_ret, (char *) &ret); + + return vol; +} + +static int +remoteStorageVolDelete (virStorageVolPtr vol, + unsigned int flags) +{ + remote_storage_vol_delete_args args; + GET_STORAGE_PRIVATE (vol->conn, -1); + + make_nonnull_storage_vol (&args.vol, vol); + args.flags = flags; + + if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_DELETE, + (xdrproc_t) xdr_remote_storage_vol_delete_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStorageVolGetInfo (virStorageVolPtr vol, virStorageVolInfoPtr info) +{ + remote_storage_vol_get_info_args args; + remote_storage_vol_get_info_ret ret; + GET_STORAGE_PRIVATE (vol->conn, -1); + + make_nonnull_storage_vol (&args.vol, vol); + + memset (&ret, 0, sizeof ret); + if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_GET_INFO, + (xdrproc_t) xdr_remote_storage_vol_get_info_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_get_info_ret, (char *) &ret) == -1) + return -1; + + info->type = ret.type; + info->capacity = ret.capacity; + info->allocation = ret.allocation; + + return 0; +} + +static char * +remoteStorageVolDumpXML (virStorageVolPtr vol, + unsigned int flags) +{ + remote_storage_vol_dump_xml_args args; + remote_storage_vol_dump_xml_ret ret; + GET_STORAGE_PRIVATE (vol->conn, NULL); + + make_nonnull_storage_vol (&args.vol, vol); + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_DUMP_XML, + (xdrproc_t) xdr_remote_storage_vol_dump_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_dump_xml_ret, (char *) &ret) == -1) + return NULL; + + /* Caller frees. */ + return ret.xml; +} + +static char * +remoteStorageVolGetPath (virStorageVolPtr vol) +{ + remote_storage_vol_get_path_args args; + remote_storage_vol_get_path_ret ret; + GET_NETWORK_PRIVATE (vol->conn, NULL); + + make_nonnull_storage_vol (&args.vol, vol); + + memset (&ret, 0, sizeof ret); + if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_GET_PATH, + (xdrproc_t) xdr_remote_storage_vol_get_path_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_get_path_ret, (char *) &ret) == -1) + return NULL; + + /* Caller frees. */ + return ret.name; +} + /*----------------------------------------------------------------------*/ @@ -3816,6 +4579,18 @@ get_nonnull_network (virConnectPtr conn, return virGetNetwork (conn, network.name, BAD_CAST network.uuid); } +static virStoragePoolPtr +get_nonnull_storage_pool (virConnectPtr conn, remote_nonnull_storage_pool pool) +{ + return virGetStoragePool (conn, pool.name, BAD_CAST pool.uuid); +} + +static virStorageVolPtr +get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol) +{ + return virGetStorageVol (conn, vol.pool, vol.name, vol.key); +} + /* Make remote_nonnull_domain and remote_nonnull_network. */ static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src) @@ -3830,6 +4605,21 @@ make_nonnull_network (remote_nonnull_net { net_dst->name = net_src->name; memcpy (net_dst->uuid, net_src->uuid, VIR_UUID_BUFLEN); +} + +static void +make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src) +{ + pool_dst->name = pool_src->name; + memcpy (pool_dst->uuid, pool_src->uuid, VIR_UUID_BUFLEN); +} + +static void +make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src) +{ + vol_dst->pool = vol_src->pool; + vol_dst->name = vol_src->name; + vol_dst->key = vol_src->key; } /*----------------------------------------------------------------------*/ @@ -3912,6 +4702,43 @@ static virNetworkDriver network_driver = .networkSetAutostart = remoteNetworkSetAutostart, }; +static virStorageDriver storage_driver = { + .name = "remote", + .open = remoteStorageOpen, + .close = remoteStorageClose, + .numOfPools = remoteNumOfStoragePools, + .listPools = remoteListStoragePools, + .numOfDefinedPools = remoteNumOfDefinedStoragePools, + .listDefinedPools = remoteListDefinedStoragePools, + .discoverPools = remoteDiscoverStoragePools, + .poolLookupByUUID = remoteStoragePoolLookupByUUID, + .poolLookupByName = remoteStoragePoolLookupByName, + .poolLookupByVolume = remoteStoragePoolLookupByVolume, + .poolCreateXML = remoteStoragePoolCreateXML, + .poolDefineXML = remoteStoragePoolDefineXML, + .poolUndefine = remoteStoragePoolUndefine, + .poolCreate = remoteStoragePoolCreate, + .poolBuild = remoteStoragePoolBuild, + .poolDestroy = remoteStoragePoolDestroy, + .poolDelete = remoteStoragePoolDelete, + .poolRefresh = remoteStoragePoolRefresh, + .poolGetInfo = remoteStoragePoolGetInfo, + .poolGetXMLDesc = remoteStoragePoolDumpXML, + .poolGetAutostart = remoteStoragePoolGetAutostart, + .poolSetAutostart = remoteStoragePoolSetAutostart, + .poolNumOfVolumes = remoteStoragePoolNumOfVolumes, + .poolListVolumes = remoteStoragePoolListVolumes, + + .volLookupByName = remoteStorageVolLookupByName, + .volLookupByKey = remoteStorageVolLookupByKey, + .volLookupByPath = remoteStorageVolLookupByPath, + .volCreateXML = remoteStorageVolCreateXML, + .volDelete = remoteStorageVolDelete, + .volGetInfo = remoteStorageVolGetInfo, + .volGetXMLDesc = remoteStorageVolDumpXML, + .volGetPath = remoteStorageVolGetPath, +}; + static virStateDriver state_driver = { remoteStartup, NULL, @@ -3931,6 +4758,7 @@ remoteRegister (void) { if (virRegisterDriver (&driver) == -1) return -1; if (virRegisterNetworkDriver (&network_driver) == -1) return -1; + if (virRegisterStorageDriver (&storage_driver) == -1) return -1; if (virRegisterStateDriver (&state_driver) == -1) return -1; return 0; -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:33:03AM +0000, Daniel P. Berrange wrote:
This patch adds support for the storage APIs to the remote driver client end. As with the network driver, things are configured such that all virtualization drivers will end up delegating to the remote driver for storage APIs. There's not much interesting to say about this patch since it just follows pattern for the existing APis.
[...]
+ if (conn && + conn->driver && + strcmp (conn->driver->name, "remote") == 0) {
aren't we supposed to use some STREQU macros for lisibility ?
+ /* 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->storagePrivateData = conn->privateData; + return VIR_DRV_OPEN_SUCCESS; + } else if (conn->networkDriver && + strcmp (conn->networkDriver->name, "remote") == 0) {
same
+ conn->storagePrivateData = conn->networkPrivateData; + ((struct private_data *)conn->storagePrivateData)->localUses++; + return VIR_DRV_OPEN_SUCCESS; + } else { + /* Using a non-remote driver, so we need to open a + * new connection for network APIs, forcing it to + * use the UNIX transport. This handles Xen driver + * which doesn't have its own impl of the network APIs. + */ + struct private_data *priv = malloc (sizeof(struct private_data)); + int ret, rflags = 0; + if (!priv) { + error (NULL, VIR_ERR_NO_MEMORY, _("struct private_data")); + return VIR_DRV_OPEN_ERROR; + } + if (flags & VIR_CONNECT_RO) + rflags |= VIR_DRV_OPEN_REMOTE_RO; + rflags |= VIR_DRV_OPEN_REMOTE_UNIX; + + memset(priv, 0, sizeof(struct private_data));
hum, and use calloc to avoid malloc/memset ? Except that i could not spot anything special, except it's not very fun to read :-) Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Fri, Feb 15, 2008 at 10:10:24AM -0500, Daniel Veillard wrote:
On Tue, Feb 12, 2008 at 04:33:03AM +0000, Daniel P. Berrange wrote:
This patch adds support for the storage APIs to the remote driver client end. As with the network driver, things are configured such that all virtualization drivers will end up delegating to the remote driver for storage APIs. There's not much interesting to say about this patch since it just follows pattern for the existing APis.
[...]
+ if (conn && + conn->driver && + strcmp (conn->driver->name, "remote") == 0) {
aren't we supposed to use some STREQU macros for lisibility ?
+ /* 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->storagePrivateData = conn->privateData; + return VIR_DRV_OPEN_SUCCESS; + } else if (conn->networkDriver && + strcmp (conn->networkDriver->name, "remote") == 0) {
same
+ conn->storagePrivateData = conn->networkPrivateData; + ((struct private_data *)conn->storagePrivateData)->localUses++; + return VIR_DRV_OPEN_SUCCESS; + } else { + /* Using a non-remote driver, so we need to open a + * new connection for network APIs, forcing it to + * use the UNIX transport. This handles Xen driver + * which doesn't have its own impl of the network APIs. + */ + struct private_data *priv = malloc (sizeof(struct private_data)); + int ret, rflags = 0; + if (!priv) { + error (NULL, VIR_ERR_NO_MEMORY, _("struct private_data")); + return VIR_DRV_OPEN_ERROR; + } + if (flags & VIR_CONNECT_RO) + rflags |= VIR_DRV_OPEN_REMOTE_RO; + rflags |= VIR_DRV_OPEN_REMOTE_UNIX; + + memset(priv, 0, sizeof(struct private_data));
hum, and use calloc to avoid malloc/memset ?
Yep, those are all copy+paste from the equivalent network APIs. I'll fix these, and the originals at the same time. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:33:03AM +0000, Daniel P. Berrange wrote:
This patch adds support for the storage APIs to the remote driver client end. As with the network driver, things are configured such that all virtualization drivers will end up delegating to the remote driver for storage APIs. There's not much interesting to say about this patch since it just follows pattern for the existing APis.
remote_internal.c | 820 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 809 insertions(+), 11 deletions(-) diff -r d2291e4435c7 src/remote_internal.c --- a/src/remote_internal.c Tue Feb 19 12:45:55 2008 -0500 +++ b/src/remote_internal.c Tue Feb 19 12:47:55 2008 -0500 @@ -89,7 +89,7 @@ struct private_data { gnutls_session_t session; /* GnuTLS session (if uses_tls != 0). */ char *type; /* Cached return from remoteType. */ int counter; /* Generates serial numbers for RPC. */ - int networkOnly; /* Only used for network API */ + int localUses; /* Ref count for private data */ char *hostname; /* Original hostname */ FILE *debugLog; /* Debug remote protocol */ #if HAVE_SASL @@ -115,6 +115,15 @@ struct private_data { _("tried to use a closed or uninitialised handle")); \ return (retcode); \ } + +#define GET_STORAGE_PRIVATE(conn,retcode) \ + struct private_data *priv = (struct private_data *) (conn)->storagePrivateData; \ + 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, @@ -140,8 +149,12 @@ static void server_error (virConnectPtr static void server_error (virConnectPtr conn, remote_error *err); static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domain domain); 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 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); +static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); /*----------------------------------------------------------------------*/ @@ -797,7 +810,7 @@ remoteOpen (virConnectPtr conn, if (inside_daemon) return VIR_DRV_OPEN_DECLINED; - priv = malloc (sizeof(*priv)); + priv = calloc (1, sizeof(*priv)); if (!priv) { error (conn, VIR_ERR_NO_MEMORY, _("struct private_data")); return VIR_DRV_OPEN_ERROR; @@ -823,7 +836,6 @@ remoteOpen (virConnectPtr conn, } #endif - memset(priv, 0, sizeof(struct private_data)); priv->magic = DEAD; priv->sock = -1; ret = doRemoteOpen(conn, priv, uri, auth, rflags); @@ -2292,7 +2304,7 @@ remoteNetworkOpen (virConnectPtr conn, if (conn && conn->driver && - strcmp (conn->driver->name, "remote") == 0) { + 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 @@ -2305,7 +2317,7 @@ remoteNetworkOpen (virConnectPtr conn, * use the UNIX transport. This handles Xen driver * which doesn't have its own impl of the network APIs. */ - struct private_data *priv = malloc (sizeof(*priv)); + struct private_data *priv = calloc (1, sizeof(*priv)); int ret, rflags = 0; if (!priv) { error (conn, VIR_ERR_NO_MEMORY, _("struct private_data")); @@ -2315,7 +2327,6 @@ remoteNetworkOpen (virConnectPtr conn, rflags |= VIR_DRV_OPEN_REMOTE_RO; rflags |= VIR_DRV_OPEN_REMOTE_UNIX; - memset(priv, 0, sizeof(struct private_data)); priv->magic = DEAD; priv->sock = -1; ret = doRemoteOpen(conn, priv, uri, auth, rflags); @@ -2324,7 +2335,7 @@ remoteNetworkOpen (virConnectPtr conn, free(priv); } else { priv->magic = MAGIC; - priv->networkOnly = 1; + priv->localUses = 1; conn->networkPrivateData = priv; } return ret; @@ -2336,10 +2347,13 @@ remoteNetworkClose (virConnectPtr conn) { int ret = 0; GET_NETWORK_PRIVATE (conn, -1); - if (priv->networkOnly) { - ret = doRemoteClose(conn, priv); - free(priv); - conn->networkPrivateData = NULL; + if (priv->localUses) { + priv->localUses--; + if (!priv->localUses) { + ret = doRemoteClose(conn, priv); + free(priv); + conn->networkPrivateData = NULL; + } } return ret; } @@ -2666,6 +2680,726 @@ remoteNetworkSetAutostart (virNetworkPtr return 0; } + + + + +/*----------------------------------------------------------------------*/ + +static int +remoteStorageOpen (virConnectPtr conn, + xmlURIPtr uri, + virConnectAuthPtr auth, + int flags) +{ + if (inside_daemon) + return VIR_DRV_OPEN_DECLINED; + + 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->storagePrivateData = conn->privateData; + return VIR_DRV_OPEN_SUCCESS; + } else if (conn->networkDriver && + STREQ (conn->networkDriver->name, "remote")) { + conn->storagePrivateData = conn->networkPrivateData; + ((struct private_data *)conn->storagePrivateData)->localUses++; + return VIR_DRV_OPEN_SUCCESS; + } else { + /* Using a non-remote driver, so we need to open a + * new connection for network APIs, forcing it to + * use the UNIX transport. This handles Xen driver + * which doesn't have its own impl of the network APIs. + */ + struct private_data *priv = calloc (1, sizeof(struct private_data)); + int ret, rflags = 0; + if (!priv) { + error (NULL, VIR_ERR_NO_MEMORY, _("struct private_data")); + return VIR_DRV_OPEN_ERROR; + } + if (flags & VIR_CONNECT_RO) + rflags |= VIR_DRV_OPEN_REMOTE_RO; + rflags |= VIR_DRV_OPEN_REMOTE_UNIX; + + priv->magic = DEAD; + priv->sock = -1; + ret = doRemoteOpen(conn, priv, uri, auth, rflags); + if (ret != VIR_DRV_OPEN_SUCCESS) { + conn->storagePrivateData = NULL; + free(priv); + } else { + priv->magic = MAGIC; + priv->localUses = 1; + conn->storagePrivateData = priv; + } + return ret; + } +} + +static int +remoteStorageClose (virConnectPtr conn) +{ + int ret = 0; + GET_STORAGE_PRIVATE (conn, -1); + if (priv->localUses) { + priv->localUses--; + if (!priv->localUses) { + ret = doRemoteClose(conn, priv); + free(priv); + conn->storagePrivateData = NULL; + } + } + return ret; +} + +static int +remoteNumOfStoragePools (virConnectPtr conn) +{ + remote_num_of_storage_pools_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_STORAGE_POOLS, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_num_of_storage_pools_ret, (char *) &ret) == -1) + return -1; + + return ret.num; +} + +static int +remoteListStoragePools (virConnectPtr conn, char **const names, int maxnames) +{ + int i; + remote_list_storage_pools_args args; + remote_list_storage_pools_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + + if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { + error (conn, VIR_ERR_RPC, _("too many storage pools requested")); + return -1; + } + args.maxnames = maxnames; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_LIST_STORAGE_POOLS, + (xdrproc_t) xdr_remote_list_storage_pools_args, (char *) &args, + (xdrproc_t) xdr_remote_list_storage_pools_ret, (char *) &ret) == -1) + return -1; + + if (ret.names.names_len > maxnames) { + error (conn, VIR_ERR_RPC, _("too many storage pools received")); + xdr_free ((xdrproc_t) xdr_remote_list_storage_pools_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_list_storage_pools_ret, (char *) &ret); + + return ret.names.names_len; +} + +static int +remoteNumOfDefinedStoragePools (virConnectPtr conn) +{ + remote_num_of_defined_storage_pools_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_STORAGE_POOLS, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_num_of_defined_storage_pools_ret, (char *) &ret) == -1) + return -1; + + return ret.num; +} + +static int +remoteListDefinedStoragePools (virConnectPtr conn, + char **const names, int maxnames) +{ + int i; + remote_list_defined_storage_pools_args args; + remote_list_defined_storage_pools_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + + if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { + error (conn, VIR_ERR_RPC, _("too many storage pools requested")); + return -1; + } + args.maxnames = maxnames; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_LIST_DEFINED_STORAGE_POOLS, + (xdrproc_t) xdr_remote_list_defined_storage_pools_args, (char *) &args, + (xdrproc_t) xdr_remote_list_defined_storage_pools_ret, (char *) &ret) == -1) + return -1; + + if (ret.names.names_len > maxnames) { + error (conn, VIR_ERR_RPC, _("too many storage pools received")); + xdr_free ((xdrproc_t) xdr_remote_list_defined_storage_pools_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_list_defined_storage_pools_ret, (char *) &ret); + + return ret.names.names_len; +} + +static virStoragePoolPtr +remoteStoragePoolLookupByUUID (virConnectPtr conn, + const unsigned char *uuid) +{ + virStoragePoolPtr pool; + remote_storage_pool_lookup_by_uuid_args args; + remote_storage_pool_lookup_by_uuid_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + memcpy (args.uuid, uuid, VIR_UUID_BUFLEN); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_UUID, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_uuid_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_uuid_ret, (char *) &ret) == -1) + return NULL; + + pool = get_nonnull_storage_pool (conn, ret.pool); + xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_uuid_ret, (char *) &ret); + + return pool; +} + +static virStoragePoolPtr +remoteStoragePoolLookupByName (virConnectPtr conn, + const char *name) +{ + virStoragePoolPtr pool; + remote_storage_pool_lookup_by_name_args args; + remote_storage_pool_lookup_by_name_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + args.name = (char *) name; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_NAME, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_name_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_name_ret, (char *) &ret) == -1) + return NULL; + + pool = get_nonnull_storage_pool (conn, ret.pool); + xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_name_ret, (char *) &ret); + + return pool; +} + +static virStoragePoolPtr +remoteStoragePoolLookupByVolume (virStorageVolPtr vol) +{ + virStoragePoolPtr pool; + remote_storage_pool_lookup_by_volume_args args; + remote_storage_pool_lookup_by_volume_ret ret; + GET_STORAGE_PRIVATE (vol->conn, NULL); + + make_nonnull_storage_vol (&args.vol, vol); + + memset (&ret, 0, sizeof ret); + if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_VOLUME, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_volume_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_lookup_by_volume_ret, (char *) &ret) == -1) + return NULL; + + pool = get_nonnull_storage_pool (vol->conn, ret.pool); + xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_volume_ret, (char *) &ret); + + return pool; +} + + +static virStoragePoolPtr +remoteStoragePoolCreateXML (virConnectPtr conn, const char *xmlDesc, unsigned int flags) +{ + virStoragePoolPtr pool; + remote_storage_pool_create_xml_args args; + remote_storage_pool_create_xml_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + args.xml = (char *) xmlDesc; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_CREATE_XML, + (xdrproc_t) xdr_remote_storage_pool_create_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_create_xml_ret, (char *) &ret) == -1) + return NULL; + + pool = get_nonnull_storage_pool (conn, ret.pool); + xdr_free ((xdrproc_t) &xdr_remote_storage_pool_create_xml_ret, (char *) &ret); + + return pool; +} + +static virStoragePoolPtr +remoteStoragePoolDefineXML (virConnectPtr conn, const char *xml, unsigned int flags) +{ + virStoragePoolPtr pool; + remote_storage_pool_define_xml_args args; + remote_storage_pool_define_xml_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + args.xml = (char *) xml; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DEFINE_XML, + (xdrproc_t) xdr_remote_storage_pool_define_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_define_xml_ret, (char *) &ret) == -1) + return NULL; + + pool = get_nonnull_storage_pool (conn, ret.pool); + xdr_free ((xdrproc_t) &xdr_remote_storage_pool_define_xml_ret, (char *) &ret); + + return pool; +} + +static int +remoteStoragePoolUndefine (virStoragePoolPtr pool) +{ + remote_storage_pool_undefine_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_UNDEFINE, + (xdrproc_t) xdr_remote_storage_pool_undefine_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolCreate (virStoragePoolPtr pool, unsigned int flags) +{ + remote_storage_pool_create_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + args.flags = flags; + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_CREATE, + (xdrproc_t) xdr_remote_storage_pool_create_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolBuild (virStoragePoolPtr pool, + unsigned int flags) +{ + remote_storage_pool_build_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + args.flags = flags; + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_BUILD, + (xdrproc_t) xdr_remote_storage_pool_build_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolDestroy (virStoragePoolPtr pool) +{ + remote_storage_pool_destroy_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DESTROY, + (xdrproc_t) xdr_remote_storage_pool_destroy_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolDelete (virStoragePoolPtr pool, + unsigned int flags) +{ + remote_storage_pool_delete_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + args.flags = flags; + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DELETE, + (xdrproc_t) xdr_remote_storage_pool_delete_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolRefresh (virStoragePoolPtr pool, + unsigned int flags) +{ + remote_storage_pool_refresh_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + args.flags = flags; + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_REFRESH, + (xdrproc_t) xdr_remote_storage_pool_refresh_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStoragePoolGetInfo (virStoragePoolPtr pool, virStoragePoolInfoPtr info) +{ + remote_storage_pool_get_info_args args; + remote_storage_pool_get_info_ret ret; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_GET_INFO, + (xdrproc_t) xdr_remote_storage_pool_get_info_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_get_info_ret, (char *) &ret) == -1) + return -1; + + info->state = ret.state; + info->capacity = ret.capacity; + info->allocation = ret.allocation; + info->available = ret.available; + + return 0; +} + +static char * +remoteStoragePoolDumpXML (virStoragePoolPtr pool, + unsigned int flags) +{ + remote_storage_pool_dump_xml_args args; + remote_storage_pool_dump_xml_ret ret; + GET_STORAGE_PRIVATE (pool->conn, NULL); + + make_nonnull_storage_pool (&args.pool, pool); + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DUMP_XML, + (xdrproc_t) xdr_remote_storage_pool_dump_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_dump_xml_ret, (char *) &ret) == -1) + return NULL; + + /* Caller frees. */ + return ret.xml; +} + +static int +remoteStoragePoolGetAutostart (virStoragePoolPtr pool, int *autostart) +{ + remote_storage_pool_get_autostart_args args; + remote_storage_pool_get_autostart_ret ret; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_GET_AUTOSTART, + (xdrproc_t) xdr_remote_storage_pool_get_autostart_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_get_autostart_ret, (char *) &ret) == -1) + return -1; + + if (autostart) *autostart = ret.autostart; + + return 0; +} + +static int +remoteStoragePoolSetAutostart (virStoragePoolPtr pool, int autostart) +{ + remote_storage_pool_set_autostart_args args; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool (&args.pool, pool); + args.autostart = autostart; + + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_SET_AUTOSTART, + (xdrproc_t) xdr_remote_storage_pool_set_autostart_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + + +static int +remoteStoragePoolNumOfVolumes (virStoragePoolPtr pool) +{ + remote_storage_pool_num_of_volumes_args args; + remote_storage_pool_num_of_volumes_ret ret; + GET_STORAGE_PRIVATE (pool->conn, -1); + + make_nonnull_storage_pool(&args.pool, pool); + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_NUM_OF_VOLUMES, + (xdrproc_t) xdr_remote_storage_pool_num_of_volumes_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_num_of_volumes_ret, (char *) &ret) == -1) + return -1; + + return ret.num; +} + +static int +remoteStoragePoolListVolumes (virStoragePoolPtr pool, char **const names, int maxnames) +{ + int i; + remote_storage_pool_list_volumes_args args; + remote_storage_pool_list_volumes_ret ret; + GET_STORAGE_PRIVATE (pool->conn, -1); + + if (maxnames > REMOTE_STORAGE_VOL_NAME_LIST_MAX) { + error (pool->conn, VIR_ERR_RPC, _("too many storage volumes requested")); + return -1; + } + args.maxnames = maxnames; + make_nonnull_storage_pool(&args.pool, pool); + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LIST_VOLUMES, + (xdrproc_t) xdr_remote_storage_pool_list_volumes_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_list_volumes_ret, (char *) &ret) == -1) + return -1; + + if (ret.names.names_len > maxnames) { + error (pool->conn, VIR_ERR_RPC, _("too many storage volumes received")); + xdr_free ((xdrproc_t) xdr_remote_storage_pool_list_volumes_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_storage_pool_list_volumes_ret, (char *) &ret); + + return ret.names.names_len; +} + + + +static virStorageVolPtr +remoteStorageVolLookupByName (virStoragePoolPtr pool, + const char *name) +{ + virStorageVolPtr vol; + remote_storage_vol_lookup_by_name_args args; + remote_storage_vol_lookup_by_name_ret ret; + GET_STORAGE_PRIVATE (pool->conn, NULL); + + make_nonnull_storage_pool(&args.pool, pool); + args.name = (char *) name; + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_NAME, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_name_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_name_ret, (char *) &ret) == -1) + return NULL; + + vol = get_nonnull_storage_vol (pool->conn, ret.vol); + xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_name_ret, (char *) &ret); + + return vol; +} + +static virStorageVolPtr +remoteStorageVolLookupByKey (virConnectPtr conn, + const char *key) +{ + virStorageVolPtr vol; + remote_storage_vol_lookup_by_key_args args; + remote_storage_vol_lookup_by_key_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + args.key = (char *) key; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_KEY, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_key_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_key_ret, (char *) &ret) == -1) + return NULL; + + vol = get_nonnull_storage_vol (conn, ret.vol); + xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_key_ret, (char *) &ret); + + return vol; +} + +static virStorageVolPtr +remoteStorageVolLookupByPath (virConnectPtr conn, + const char *path) +{ + virStorageVolPtr vol; + remote_storage_vol_lookup_by_path_args args; + remote_storage_vol_lookup_by_path_ret ret; + GET_STORAGE_PRIVATE (conn, NULL); + + args.path = (char *) path; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_PATH, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_path_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_lookup_by_path_ret, (char *) &ret) == -1) + return NULL; + + vol = get_nonnull_storage_vol (conn, ret.vol); + xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_path_ret, (char *) &ret); + + return vol; +} + +static virStorageVolPtr +remoteStorageVolCreateXML (virStoragePoolPtr pool, const char *xmlDesc, + unsigned int flags) +{ + virStorageVolPtr vol; + remote_storage_vol_create_xml_args args; + remote_storage_vol_create_xml_ret ret; + GET_STORAGE_PRIVATE (pool->conn, NULL); + + make_nonnull_storage_pool (&args.pool, pool); + args.xml = (char *) xmlDesc; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_CREATE_XML, + (xdrproc_t) xdr_remote_storage_vol_create_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_create_xml_ret, (char *) &ret) == -1) + return NULL; + + vol = get_nonnull_storage_vol (pool->conn, ret.vol); + xdr_free ((xdrproc_t) &xdr_remote_storage_vol_create_xml_ret, (char *) &ret); + + return vol; +} + +static int +remoteStorageVolDelete (virStorageVolPtr vol, + unsigned int flags) +{ + remote_storage_vol_delete_args args; + GET_STORAGE_PRIVATE (vol->conn, -1); + + make_nonnull_storage_vol (&args.vol, vol); + args.flags = flags; + + if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_DELETE, + (xdrproc_t) xdr_remote_storage_vol_delete_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static int +remoteStorageVolGetInfo (virStorageVolPtr vol, virStorageVolInfoPtr info) +{ + remote_storage_vol_get_info_args args; + remote_storage_vol_get_info_ret ret; + GET_STORAGE_PRIVATE (vol->conn, -1); + + make_nonnull_storage_vol (&args.vol, vol); + + memset (&ret, 0, sizeof ret); + if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_GET_INFO, + (xdrproc_t) xdr_remote_storage_vol_get_info_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_get_info_ret, (char *) &ret) == -1) + return -1; + + info->type = ret.type; + info->capacity = ret.capacity; + info->allocation = ret.allocation; + + return 0; +} + +static char * +remoteStorageVolDumpXML (virStorageVolPtr vol, + unsigned int flags) +{ + remote_storage_vol_dump_xml_args args; + remote_storage_vol_dump_xml_ret ret; + GET_STORAGE_PRIVATE (vol->conn, NULL); + + make_nonnull_storage_vol (&args.vol, vol); + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_DUMP_XML, + (xdrproc_t) xdr_remote_storage_vol_dump_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_dump_xml_ret, (char *) &ret) == -1) + return NULL; + + /* Caller frees. */ + return ret.xml; +} + +static char * +remoteStorageVolGetPath (virStorageVolPtr vol) +{ + remote_storage_vol_get_path_args args; + remote_storage_vol_get_path_ret ret; + GET_NETWORK_PRIVATE (vol->conn, NULL); + + make_nonnull_storage_vol (&args.vol, vol); + + memset (&ret, 0, sizeof ret); + if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_GET_PATH, + (xdrproc_t) xdr_remote_storage_vol_get_path_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_get_path_ret, (char *) &ret) == -1) + return NULL; + + /* Caller frees. */ + return ret.name; +} + /*----------------------------------------------------------------------*/ @@ -3816,6 +4550,18 @@ get_nonnull_network (virConnectPtr conn, return virGetNetwork (conn, network.name, BAD_CAST network.uuid); } +static virStoragePoolPtr +get_nonnull_storage_pool (virConnectPtr conn, remote_nonnull_storage_pool pool) +{ + return virGetStoragePool (conn, pool.name, BAD_CAST pool.uuid); +} + +static virStorageVolPtr +get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol) +{ + return virGetStorageVol (conn, vol.pool, vol.name, vol.key); +} + /* Make remote_nonnull_domain and remote_nonnull_network. */ static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src) @@ -3830,6 +4576,21 @@ make_nonnull_network (remote_nonnull_net { net_dst->name = net_src->name; memcpy (net_dst->uuid, net_src->uuid, VIR_UUID_BUFLEN); +} + +static void +make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src) +{ + pool_dst->name = pool_src->name; + memcpy (pool_dst->uuid, pool_src->uuid, VIR_UUID_BUFLEN); +} + +static void +make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src) +{ + vol_dst->pool = vol_src->pool; + vol_dst->name = vol_src->name; + vol_dst->key = vol_src->key; } /*----------------------------------------------------------------------*/ @@ -3912,6 +4673,42 @@ static virNetworkDriver network_driver = .networkSetAutostart = remoteNetworkSetAutostart, }; +static virStorageDriver storage_driver = { + .name = "remote", + .open = remoteStorageOpen, + .close = remoteStorageClose, + .numOfPools = remoteNumOfStoragePools, + .listPools = remoteListStoragePools, + .numOfDefinedPools = remoteNumOfDefinedStoragePools, + .listDefinedPools = remoteListDefinedStoragePools, + .poolLookupByUUID = remoteStoragePoolLookupByUUID, + .poolLookupByName = remoteStoragePoolLookupByName, + .poolLookupByVolume = remoteStoragePoolLookupByVolume, + .poolCreateXML = remoteStoragePoolCreateXML, + .poolDefineXML = remoteStoragePoolDefineXML, + .poolUndefine = remoteStoragePoolUndefine, + .poolCreate = remoteStoragePoolCreate, + .poolBuild = remoteStoragePoolBuild, + .poolDestroy = remoteStoragePoolDestroy, + .poolDelete = remoteStoragePoolDelete, + .poolRefresh = remoteStoragePoolRefresh, + .poolGetInfo = remoteStoragePoolGetInfo, + .poolGetXMLDesc = remoteStoragePoolDumpXML, + .poolGetAutostart = remoteStoragePoolGetAutostart, + .poolSetAutostart = remoteStoragePoolSetAutostart, + .poolNumOfVolumes = remoteStoragePoolNumOfVolumes, + .poolListVolumes = remoteStoragePoolListVolumes, + + .volLookupByName = remoteStorageVolLookupByName, + .volLookupByKey = remoteStorageVolLookupByKey, + .volLookupByPath = remoteStorageVolLookupByPath, + .volCreateXML = remoteStorageVolCreateXML, + .volDelete = remoteStorageVolDelete, + .volGetInfo = remoteStorageVolGetInfo, + .volGetXMLDesc = remoteStorageVolDumpXML, + .volGetPath = remoteStorageVolGetPath, +}; + static virStateDriver state_driver = { remoteStartup, NULL, @@ -3931,6 +4728,7 @@ remoteRegister (void) { if (virRegisterDriver (&driver) == -1) return -1; if (virRegisterNetworkDriver (&network_driver) == -1) return -1; + if (virRegisterStorageDriver (&storage_driver) == -1) return -1; if (virRegisterStateDriver (&state_driver) == -1) return -1; return 0; -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This extends the generator / manual bindings to implement all the storage APIs. Most are handle automatically, and those which are not, pretty much follow the existing pattern for virDomain and virNetwork APIs. generator.py | 93 +++++++++++++++++- libvir.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ libvirt_wrap.h | 20 +++ types.c | 36 +++++++ 4 files changed, 439 insertions(+), 3 deletions(-) diff -r 7e5658cc1a93 python/generator.py --- a/python/generator.py Thu Feb 07 12:33:22 2008 -0500 +++ b/python/generator.py Thu Feb 07 12:33:27 2008 -0500 @@ -233,18 +233,32 @@ py_types = { 'unsigned char *': ('z', None, "charPtr", "char *"), 'char *': ('z', None, "charPtr", "char *"), 'const char *': ('z', None, "charPtrConst", "const char *"), + 'virJobPtr': ('O', "virJob", "virJobPtr", "virJobPtr"), 'const virJobPtr': ('O', "virJob", "virJobPtr", "virJobPtr"), 'virJob *': ('O', "virJob", "virJobPtr", "virJobPtr"), 'const virJob *': ('O', "virJob", "virJobPtr", "virJobPtr"), + 'virDomainPtr': ('O', "virDomain", "virDomainPtr", "virDomainPtr"), 'const virDomainPtr': ('O', "virDomain", "virDomainPtr", "virDomainPtr"), 'virDomain *': ('O', "virDomain", "virDomainPtr", "virDomainPtr"), 'const virDomain *': ('O', "virDomain", "virDomainPtr", "virDomainPtr"), + 'virNetworkPtr': ('O', "virNetwork", "virNetworkPtr", "virNetworkPtr"), 'const virNetworkPtr': ('O', "virNetwork", "virNetworkPtr", "virNetworkPtr"), 'virNetwork *': ('O', "virNetwork", "virNetworkPtr", "virNetworkPtr"), 'const virNetwork *': ('O', "virNetwork", "virNetworkPtr", "virNetworkPtr"), + + 'virStoragePoolPtr': ('O', "virStoragePool", "virStoragePoolPtr", "virStoragePoolPtr"), + 'const virStoragePoolPtr': ('O', "virStoragePool", "virStoragePoolPtr", "virStoragePoolPtr"), + 'virStoragePool *': ('O', "virStoragePool", "virStoragePoolPtr", "virStoragePoolPtr"), + 'const virStoragePool *': ('O', "virStoragePool", "virStoragePoolPtr", "virStoragePoolPtr"), + + 'virStorageVolPtr': ('O', "virStorageVol", "virStorageVolPtr", "virStorageVolPtr"), + 'const virStorageVolPtr': ('O', "virStorageVol", "virStorageVolPtr", "virStorageVolPtr"), + 'virStorageVol *': ('O', "virStorageVol", "virStorageVolPtr", "virStorageVolPtr"), + 'const virStorageVol *': ('O', "virStorageVol", "virStorageVolPtr", "virStorageVolPtr"), + 'virConnectPtr': ('O', "virConnect", "virConnectPtr", "virConnectPtr"), 'const virConnectPtr': ('O', "virConnect", "virConnectPtr", "virConnectPtr"), 'virConnect *': ('O', "virConnect", "virConnectPtr", "virConnectPtr"), @@ -273,6 +287,10 @@ skip_impl = ( 'virConnectListDefinedDomains', 'virConnectListNetworks', 'virConnectListDefinedNetworks', + 'virConnectListStoragePools', + 'virConnectListDefinedStoragePools', + 'virConnectListStorageVols', + 'virConnectListDefinedStorageVols', 'virConnGetLastError', 'virGetLastError', 'virJobGetInfo', @@ -294,6 +312,14 @@ skip_impl = ( 'virDomainSetSchedulerParameters', 'virDomainGetVcpus', 'virDomainPinVcpu', + 'virStoragePoolGetUUID', + 'virStoragePoolLookupByUUID', + 'virStorageVolGetUUID', + 'virStorageVolLookupByUUID', + 'virStoragePoolGetInfo', + 'virStorageVolGetInfo', + 'virStoragePoolGetAutostart', + 'virStoragePoolListVolumes', ) @@ -313,6 +339,7 @@ skip_function = ( 'virDefaultErrorFunc', # Python virErrorFuncHandler impl calls this from C 'virJobCopyLastError', # XXX fixme 'virJobGetError', # XXX fixme + 'virConnectDiscoverStoragePools', # XXX fixme ) @@ -575,6 +602,10 @@ classes_type = { "virDomain *": ("._o", "virDomain(self, _obj=%s)", "virDomain"), "virNetworkPtr": ("._o", "virNetwork(self, _obj=%s)", "virNetwork"), "virNetwork *": ("._o", "virNetwork(self, _obj=%s)", "virNetwork"), + "virStoragePoolPtr": ("._o", "virStoragePool(self, _obj=%s)", "virStoragePool"), + "virStoragePool *": ("._o", "virStoragePool(self, _obj=%s)", "virStoragePool"), + "virStorageVolPtr": ("._o", "virStorageVol(self, _obj=%s)", "virStorageVol"), + "virStorageVol *": ("._o", "virStorageVol(self, _obj=%s)", "virStorageVol"), "virConnectPtr": ("._o", "virConnect(_obj=%s)", "virConnect"), "virConnect *": ("._o", "virConnect(_obj=%s)", "virConnect"), } @@ -582,7 +613,7 @@ converter_type = { converter_type = { } -primary_classes = ["virDomain", "virNetwork", "virConnect"] +primary_classes = ["virDomain", "virNetwork", "virStoragePool", "virStorageVol", "virConnect"] classes_ancestor = { } @@ -590,12 +621,18 @@ classes_destructors = { "virJob": "virJobFree", "virDomain": "virDomainFree", "virNetwork": "virNetworkFree", + "virStoragePool": "virStoragePoolFree", + "virStorageVol": "virStorageVolFree", "virConnect": "virConnectClose", } functions_noexcept = { 'virDomainGetID': True, 'virDomainGetName': True, + 'virNetworkGetName': True, + 'virStoragePoolGetName': True, + 'virStorageVolGetName': True, + 'virStorageVolGetkey': True, } reference_keepers = { @@ -609,6 +646,8 @@ function_post = { 'virJobDestroy': "self._o = None", 'virDomainDestroy': "self._o = None", 'virNetworkDestroy': "self._o = None", + 'virStoragePoolDestroy': "self._o = None", + 'virStorageVolDestroy': "self._o = None", } # Functions returning an integral type which need special rules to @@ -644,6 +683,18 @@ def nameFixup(name, classe, type, file): elif name[0:16] == "virNetworkLookup": func = name[3:] func = string.lower(func[0:1]) + func[1:] + elif name[0:20] == "virStoragePoolDefine": + func = name[3:] + func = string.lower(func[0:1]) + func[1:] + elif name[0:20] == "virStoragePoolLookup": + func = name[3:] + func = string.lower(func[0:1]) + func[1:] + elif name[0:19] == "virStorageVolDefine": + func = name[3:] + func = string.lower(func[0:1]) + func[1:] + elif name[0:19] == "virStorageVolLookup": + func = name[3:] + func = string.lower(func[0:1]) + func[1:] elif name[0:12] == "virJobGet": func = name[9:] func = string.lower(func[0:1]) + func[1:] @@ -658,6 +709,18 @@ def nameFixup(name, classe, type, file): func = string.lower(func[0:1]) + func[1:] elif name[0:10] == "virNetwork": func = name[10:] + func = string.lower(func[0:1]) + func[1:] + elif name[0:17] == "virStoragePoolGet": + func = name[17:] + func = string.lower(func[0:1]) + func[1:] + elif name[0:14] == "virStoragePool": + func = name[14:] + func = string.lower(func[0:1]) + func[1:] + elif name[0:16] == "virStorageVolGet": + func = name[16:] + func = string.lower(func[0:1]) + func[1:] + elif name[0:13] == "virStorageVol": + func = name[13:] func = string.lower(func[0:1]) + func[1:] elif name[0:7] == "virNode": func = name[7:] @@ -908,7 +971,7 @@ def buildWrappers(): else: txt.write("Class %s()\n" % (classname)) classes.write("class %s:\n" % (classname)) - if classname in ["virDomain", "virNetwork", "virJob"]: + if classname in ["virDomain", "virNetwork", "virJob", "virStoragePool", "virStorageVol"]: classes.write(" def __init__(self, conn, _obj=None):\n") else: classes.write(" def __init__(self, _obj=None):\n") @@ -916,7 +979,7 @@ def buildWrappers(): list = reference_keepers[classname] for ref in list: classes.write(" self.%s = None\n" % ref[1]) - if classname in ["virDomain", "virNetwork", "virJob"]: + if classname in ["virDomain", "virNetwork", "virJob", "virStoragePool", "virStorageVol"]: classes.write(" self._conn = conn\n") classes.write(" if _obj != None:self._o = _obj;return\n") classes.write(" self._o = None\n\n"); @@ -1014,6 +1077,14 @@ def buildWrappers(): classes.write( " if ret is None:raise libvirtError('%s() failed', net=self)\n" % (name)) + elif classname == "virStoragePool": + classes.write( + " if ret is None:raise libvirtError('%s() failed', pool=self)\n" % + (name)) + elif classname == "virStorageVol": + classes.write( + " if ret is None:raise libvirtError('%s() failed', vol=self)\n" % + (name)) else: classes.write( " if ret is None:raise libvirtError('%s() failed')\n" % @@ -1090,6 +1161,14 @@ def buildWrappers(): classes.write ((" if " + test + ": raise libvirtError ('%s() failed', net=self)\n") % ("ret", name)) + elif classname == "virStoragePool": + classes.write ((" if " + test + + ": raise libvirtError ('%s() failed', pool=self)\n") % + ("ret", name)) + elif classname == "virStorageVol": + classes.write ((" if " + test + + ": raise libvirtError ('%s() failed', vol=self)\n") % + ("ret", name)) else: classes.write ((" if " + test + ": raise libvirtError ('%s() failed')\n") % @@ -1123,6 +1202,14 @@ def buildWrappers(): elif classname == "virNetwork": classes.write ((" if " + test + ": raise libvirtError ('%s() failed', net=self)\n") % + ("ret", name)) + elif classname == "virStoragePool": + classes.write ((" if " + test + + ": raise libvirtError ('%s() failed', pool=self)\n") % + ("ret", name)) + elif classname == "virStorageVol": + classes.write ((" if " + test + + ": raise libvirtError ('%s() failed', vol=self)\n") % ("ret", name)) else: classes.write ((" if " + test + diff -r 7e5658cc1a93 python/libvir.c --- a/python/libvir.c Thu Feb 07 12:33:22 2008 -0500 +++ b/python/libvir.c Thu Feb 07 12:33:27 2008 -0500 @@ -1098,6 +1098,291 @@ error: free(freeMems); return(py_retval); } + + +static PyObject * +libvirt_virConnectListStoragePools(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + PyObject *py_retval; + char **names = NULL; + int c_retval, i; + virConnectPtr conn; + PyObject *pyobj_conn; + + + if (!PyArg_ParseTuple(args, (char *)"O:virConnectListStoragePools", &pyobj_conn)) + return(NULL); + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + c_retval = virConnectNumOfStoragePools(conn); + if (c_retval < 0) + return VIR_PY_NONE; + + if (c_retval) { + names = malloc(sizeof(*names) * c_retval); + if (!names) + return VIR_PY_NONE; + c_retval = virConnectListStoragePools(conn, names, c_retval); + if (c_retval < 0) { + free(names); + return VIR_PY_NONE; + } + } + py_retval = PyList_New(c_retval); + if (py_retval == NULL) { + if (names) { + for (i = 0;i < c_retval;i++) + free(names[i]); + free(names); + } + return VIR_PY_NONE; + } + + 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_virConnectListDefinedStoragePools(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + PyObject *py_retval; + char **names = NULL; + int c_retval, i; + virConnectPtr conn; + PyObject *pyobj_conn; + + + if (!PyArg_ParseTuple(args, (char *)"O:virConnectListDefinedStoragePools", &pyobj_conn)) + return(NULL); + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + c_retval = virConnectNumOfDefinedStoragePools(conn); + if (c_retval < 0) + return VIR_PY_NONE; + + if (c_retval) { + names = malloc(sizeof(*names) * c_retval); + if (!names) + return VIR_PY_NONE; + c_retval = virConnectListDefinedStoragePools(conn, names, c_retval); + if (c_retval < 0) { + free(names); + return VIR_PY_NONE; + } + } + py_retval = PyList_New(c_retval); + if (py_retval == NULL) { + if (names) { + for (i = 0;i < c_retval;i++) + free(names[i]); + free(names); + } + return VIR_PY_NONE; + } + + 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_virStoragePoolListVolumes(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + PyObject *py_retval; + char **names = NULL; + int c_retval, i; + virStoragePoolPtr pool; + PyObject *pyobj_pool; + + + if (!PyArg_ParseTuple(args, (char *)"O:virStoragePoolListVolumes", &pyobj_pool)) + return(NULL); + pool = (virStoragePoolPtr) PyvirStoragePool_Get(pyobj_pool); + + c_retval = virStoragePoolNumOfVolumes(pool); + 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 = virStoragePoolListVolumes(pool, names, c_retval); + if (c_retval < 0) { + free(names); + return VIR_PY_NONE; + } + } + py_retval = PyList_New(c_retval); + if (py_retval == NULL) { + if (names) { + for (i = 0;i < c_retval;i++) + free(names[i]); + free(names); + } + return VIR_PY_NONE; + } + + 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_virStoragePoolGetAutostart(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + int c_retval, autostart; + virStoragePoolPtr pool; + PyObject *pyobj_pool; + + if (!PyArg_ParseTuple(args, (char *)"O:virStoragePoolGetAutostart", &pyobj_pool)) + return(NULL); + + pool = (virStoragePoolPtr) PyvirStoragePool_Get(pyobj_pool); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virStoragePoolGetAutostart(pool, &autostart); + LIBVIRT_END_ALLOW_THREADS; + + if (c_retval < 0) + return VIR_PY_NONE; + + py_retval = libvirt_intWrap(autostart); + return(py_retval); +} + +static PyObject * +libvirt_virStoragePoolGetInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + int c_retval; + virStoragePoolPtr pool; + PyObject *pyobj_pool; + virStoragePoolInfo info; + + if (!PyArg_ParseTuple(args, (char *)"O:virStoragePoolGetInfo", &pyobj_pool)) + return(NULL); + pool = (virStoragePoolPtr) PyvirStoragePool_Get(pyobj_pool); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virStoragePoolGetInfo(pool, &info); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if ((py_retval = PyList_New(4)) == NULL) + return VIR_PY_NONE; + + PyList_SetItem(py_retval, 0, libvirt_intWrap((int) info.state)); + PyList_SetItem(py_retval, 1, + libvirt_longlongWrap((unsigned long long) info.capacity)); + PyList_SetItem(py_retval, 2, + libvirt_longlongWrap((unsigned long long) info.allocation)); + PyList_SetItem(py_retval, 3, + libvirt_longlongWrap((unsigned long long) info.available)); + return(py_retval); +} + + +static PyObject * +libvirt_virStorageVolGetInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + int c_retval; + virStorageVolPtr pool; + PyObject *pyobj_pool; + virStorageVolInfo info; + + if (!PyArg_ParseTuple(args, (char *)"O:virStorageVolGetInfo", &pyobj_pool)) + return(NULL); + pool = (virStorageVolPtr) PyvirStorageVol_Get(pyobj_pool); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virStorageVolGetInfo(pool, &info); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if ((py_retval = PyList_New(3)) == NULL) + return VIR_PY_NONE; + PyList_SetItem(py_retval, 0, libvirt_intWrap((int) info.type)); + PyList_SetItem(py_retval, 1, + libvirt_longlongWrap((unsigned long long) info.capacity)); + PyList_SetItem(py_retval, 2, + libvirt_longlongWrap((unsigned long long) info.allocation)); + return(py_retval); +} + +static PyObject * +libvirt_virStoragePoolGetUUID(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + unsigned char uuid[VIR_UUID_BUFLEN]; + virStoragePoolPtr pool; + PyObject *pyobj_pool; + int c_retval; + + if (!PyArg_ParseTuple(args, (char *)"O:virStoragePoolGetUUID", &pyobj_pool)) + return(NULL); + pool = (virStoragePoolPtr) PyvirStoragePool_Get(pyobj_pool); + + if (pool == NULL) + return VIR_PY_NONE; + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virStoragePoolGetUUID(pool, &uuid[0]); + LIBVIRT_END_ALLOW_THREADS; + + if (c_retval < 0) + return VIR_PY_NONE; + + py_retval = PyString_FromStringAndSize((char *) &uuid[0], VIR_UUID_BUFLEN); + + return(py_retval); +} + + +static PyObject * +libvirt_virStoragePoolLookupByUUID(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + virStoragePoolPtr c_retval; + virConnectPtr conn; + PyObject *pyobj_conn; + unsigned char * uuid; + int len; + + if (!PyArg_ParseTuple(args, (char *)"Oz#:virStoragePoolLookupByUUID", &pyobj_conn, &uuid, &len)) + return(NULL); + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + if ((uuid == NULL) || (len != VIR_UUID_BUFLEN)) + return VIR_PY_NONE; + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virStoragePoolLookupByUUID(conn, uuid); + LIBVIRT_END_ALLOW_THREADS; + py_retval = libvirt_virStoragePoolPtrWrap((virStoragePoolPtr) c_retval); + return(py_retval); +} + + /************************************************************************ * * @@ -1131,6 +1416,14 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainSetSchedulerParameters", libvirt_virDomainSetSchedulerParameters, METH_VARARGS, NULL}, {(char *) "virDomainGetVcpus", libvirt_virDomainGetVcpus, METH_VARARGS, NULL}, {(char *) "virDomainPinVcpu", libvirt_virDomainPinVcpu, METH_VARARGS, NULL}, + {(char *) "virConnectListStoragePools", libvirt_virConnectListStoragePools, METH_VARARGS, NULL}, + {(char *) "virConnectListDefinedStoragePools", libvirt_virConnectListDefinedStoragePools, METH_VARARGS, NULL}, + {(char *) "virStoragePoolGetAutostart", libvirt_virStoragePoolGetAutostart, METH_VARARGS, NULL}, + {(char *) "virStoragePoolListVolumes", libvirt_virStoragePoolListVolumes, METH_VARARGS, NULL}, + {(char *) "virStoragePoolGetInfo", libvirt_virStoragePoolGetInfo, METH_VARARGS, NULL}, + {(char *) "virStorageVolGetInfo", libvirt_virStorageVolGetInfo, METH_VARARGS, NULL}, + {(char *) "virStoragePoolGetUUID", libvirt_virStoragePoolGetUUID, METH_VARARGS, NULL}, + {(char *) "virStoragePoolLookupByUUID", libvirt_virStoragePoolLookupByUUID, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; diff -r 7e5658cc1a93 python/libvirt_wrap.h --- a/python/libvirt_wrap.h Thu Feb 07 12:33:22 2008 -0500 +++ b/python/libvirt_wrap.h Thu Feb 07 12:33:27 2008 -0500 @@ -57,6 +57,24 @@ typedef struct { } PyvirNetwork_Object; +#define PyvirStoragePool_Get(v) (((v) == Py_None) ? NULL : \ + (((PyvirStoragePool_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + virStoragePoolPtr obj; +} PyvirStoragePool_Object; + + +#define PyvirStorageVol_Get(v) (((v) == Py_None) ? NULL : \ + (((PyvirStorageVol_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + virStorageVolPtr obj; +} PyvirStorageVol_Object; + + PyObject * libvirt_intWrap(int val); PyObject * libvirt_longWrap(long val); PyObject * libvirt_ulongWrap(unsigned long val); @@ -68,6 +86,8 @@ PyObject * libvirt_virJobPtrWrap(virJobP PyObject * libvirt_virJobPtrWrap(virJobPtr node); PyObject * libvirt_virDomainPtrWrap(virDomainPtr node); PyObject * libvirt_virNetworkPtrWrap(virNetworkPtr node); +PyObject * libvirt_virStoragePoolPtrWrap(virStoragePoolPtr node); +PyObject * libvirt_virStorageVolPtrWrap(virStorageVolPtr node); /* Provide simple macro statement wrappers (adapted from GLib, in turn from Perl): diff -r 7e5658cc1a93 python/types.c --- a/python/types.c Thu Feb 07 12:33:22 2008 -0500 +++ b/python/types.c Thu Feb 07 12:33:27 2008 -0500 @@ -163,6 +163,42 @@ libvirt_virNetworkPtrWrap(virNetworkPtr } PyObject * +libvirt_virStoragePoolPtrWrap(virStoragePoolPtr node) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libvirt_virStoragePoolPtrWrap: node = %p\n", node); +#endif + if (node == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCObject_FromVoidPtrAndDesc((void *) node, (char *) "virStoragePoolPtr", + NULL); + return (ret); +} + +PyObject * +libvirt_virStorageVolPtrWrap(virStorageVolPtr node) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libvirt_virStorageVolPtrWrap: node = %p\n", node); +#endif + if (node == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCObject_FromVoidPtrAndDesc((void *) node, (char *) "virStorageVolPtr", + NULL); + return (ret); +} + +PyObject * libvirt_virConnectPtrWrap(virConnectPtr node) { PyObject *ret; -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:33:42AM +0000, Daniel P. Berrange wrote:
This extends the generator / manual bindings to implement all the storage APIs. Most are handle automatically, and those which are not, pretty much follow the existing pattern for virDomain and virNetwork APIs.
generator.py | 93 +++++++++++++++++-
okay generator change are just related to more classes being defined looks fine, +1
diff -r 7e5658cc1a93 python/libvir.c +static PyObject * +libvirt_virConnectListStoragePools(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { [...] +} + + +static PyObject * +libvirt_virConnectListDefinedStoragePools(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { [...] +} + + +static PyObject * +libvirt_virStoragePoolListVolumes(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { [...] +}
okay functions needed to return lists objects, looks fine
+static PyObject * +libvirt_virStoragePoolGetInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { +} [...] + + +static PyObject * +libvirt_virStorageVolGetInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
that too
+} + +static PyObject * +libvirt_virStoragePoolGetUUID(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
UUID argument OUT
+} + + +static PyObject * +libvirt_virStoragePoolLookupByUUID(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
UUID argument IN
+} + +
/************************************************************************ * * @@ -1131,6 +1416,14 @@ static PyMethodDef libvirtMethods[] = {
registration
diff -r 7e5658cc1a93 python/libvirt_wrap.h --- a/python/libvirt_wrap.h Thu Feb 07 12:33:22 2008 -0500 +++ b/python/libvirt_wrap.h Thu Feb 07 12:33:27 2008 -0500
and more wrappers, okay look fine, all make sense. +1 Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

This patch adds new virsh commands for all the libvirt storage APIs allowing their use from the shell. The style follows that of the existing domain & network commands, so I won't bother listing them in great detail. The only complexity is in dealing with the vol-XXX commands. These can accept either a full volume path, a volume key (equiv of UUID) or a pool name + volume name. The latter presents a difficulty because our GetOpt() style parser does not know how to have commands which take either 1 or 2 args - it can do, one or the other but not both. So to specify a pool name + volume name requires the slightly ugly use of --pool flag. Most people will probably just use paths though. virsh.c | 1312 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1303 insertions(+), 9 deletions(-) diff -r 413d1461dd4b src/virsh.c --- a/src/virsh.c Thu Feb 07 12:33:27 2008 -0500 +++ b/src/virsh.c Thu Feb 07 12:33:30 2008 -0500 @@ -266,6 +266,24 @@ static virNetworkPtr vshCommandOptNetwor vshCommandOptNetworkBy(_ctl, _cmd, _optname, _name, \ VSH_BYUUID|VSH_BYNAME) +static virStoragePoolPtr vshCommandOptPoolBy(vshControl * ctl, vshCmd * cmd, + const char *optname, char **name, int flag); + +/* default is lookup by Name and UUID */ +#define vshCommandOptPool(_ctl, _cmd, _optname, _name) \ + vshCommandOptPoolBy(_ctl, _cmd, _optname, _name, \ + VSH_BYUUID|VSH_BYNAME) + +static virStorageVolPtr vshCommandOptVolBy(vshControl * ctl, vshCmd * cmd, + const char *optname, + const char *pooloptname, + char **name, int flag); + +/* default is lookup by Name and UUID */ +#define vshCommandOptVol(_ctl, _cmd,_optname, _pooloptname, _name) \ + vshCommandOptVolBy(_ctl, _cmd, _optname, _pooloptname, _name, \ + VSH_BYUUID|VSH_BYNAME) + static void vshPrintExtra(vshControl * ctl, const char *format, ...) ATTRIBUTE_FORMAT(printf, 2, 3); static void vshDebug(vshControl * ctl, int level, const char *format, ...) @@ -331,7 +349,7 @@ cmdMonitorProgress(vshControl *ctl, vshC do { if (virJobGetInfo(job, info) < 0) { - vshError(ctl, FALSE, _("Failed to get job status")); + vshError(ctl, FALSE, "%s", _("Failed to get job status")); return -1; } @@ -978,7 +996,7 @@ cmdCreate(vshControl * ctl, vshCmd * cmd virDomainGetName(dom), from); virDomainFree(dom); } else if (info.state == VIR_JOB_CANCELLED) { - vshError(ctl, FALSE, _("Cancelled domain create operation")); + vshError(ctl, FALSE, "%s", _("Cancelled domain create operation")); ret = FALSE; } else { vshError(ctl, FALSE, _("Failed to create domain from %s"), from); @@ -1146,7 +1164,7 @@ cmdStart(vshControl * ctl, vshCmd * cmd) vshPrint(ctl, _("Domain %s started\n"), virDomainGetName(dom)); } else if (info.state == VIR_JOB_CANCELLED) { - vshError(ctl, FALSE, _("Cancelled domain start operation")); + vshError(ctl, FALSE, "%s", _("Cancelled domain start operation")); ret = FALSE; } else { vshError(ctl, FALSE, _("Failed to start domain %s"), @@ -1222,7 +1240,7 @@ cmdSave(vshControl * ctl, vshCmd * cmd) if (info.state == VIR_JOB_COMPLETE) { vshPrint(ctl, _("Domain %s saved to %s\n"), name, to); } else if (info.state == VIR_JOB_CANCELLED) { - vshError(ctl, FALSE, _("Cancelled domain save operation")); + vshError(ctl, FALSE, "%s", _("Cancelled domain save operation")); ret = FALSE; } else { vshError(ctl, FALSE, _("Failed to save domain %s to %s"), name, to); @@ -1441,7 +1459,7 @@ cmdRestore(vshControl * ctl, vshCmd * cm if (info.state == VIR_JOB_COMPLETE) { vshPrint(ctl, _("Domain restored from %s\n"),from); } else if (info.state == VIR_JOB_CANCELLED) { - vshError(ctl, FALSE, _("Cancelled domain restore operation")); + vshError(ctl, FALSE, "%s", _("Cancelled domain restore operation")); ret = FALSE; } else { vshError(ctl, FALSE, _("Failed to restore domain from %s"), from); @@ -1514,7 +1532,7 @@ cmdDump(vshControl * ctl, vshCmd * cmd) if (info.state == VIR_JOB_COMPLETE) { vshPrint(ctl, _("Domain %s dumped to %s\n"), name, to); } else if (info.state == VIR_JOB_CANCELLED) { - vshError(ctl, FALSE, _("Cancelled domain dump operation")); + vshError(ctl, FALSE, "%s", _("Cancelled domain dump operation")); ret = FALSE; } else { vshError(ctl, FALSE, _("Failed to core dump domain %s to %s"), @@ -2574,7 +2592,7 @@ cmdNetworkCreate(vshControl * ctl, vshCm virNetworkGetName(dom), from); virNetworkFree(dom); } else if (info.state == VIR_JOB_CANCELLED) { - vshError(ctl, FALSE, _("Cancelled network create operation")); + vshError(ctl, FALSE, "%s", _("Cancelled network create operation")); ret = FALSE; } else { vshError(ctl, FALSE, _("Failed to create network from %s"), from); @@ -2932,7 +2950,7 @@ cmdNetworkStart(vshControl * ctl, vshCmd vshPrint(ctl, _("Network %s started\n"), virNetworkGetName(network)); } else if (info.state == VIR_JOB_CANCELLED) { - vshError(ctl, FALSE, _("Cancelled network start operation")); + vshError(ctl, FALSE, "%s", _("Cancelled network start operation")); ret = FALSE; } else { vshError(ctl, FALSE, _("Failed to start network %s"), @@ -3030,6 +3048,1161 @@ cmdNetworkUuid(vshControl * ctl, vshCmd } + + + + + + + + + + + +/* + * "pool-autostart" command + */ +static vshCmdInfo info_pool_autostart[] = { + {"syntax", "pool-autostart [--disable] <pool>"}, + {"help", gettext_noop("autostart a pool")}, + {"desc", + gettext_noop("Configure a pool to be automatically started at boot.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_autostart[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name or uuid")}, + {"disable", VSH_OT_BOOL, 0, gettext_noop("disable autostarting")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolAutostart(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + char *name; + int autostart; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name))) + return FALSE; + + autostart = !vshCommandOptBool(cmd, "disable"); + + if (virStoragePoolSetAutostart(pool, autostart) < 0) { + if (autostart) + vshError(ctl, FALSE, _("failed to mark pool %s as autostarted"), + name); + else + vshError(ctl, FALSE,_("failed to unmark pool %s as autostarted"), + name); + virStoragePoolFree(pool); + return FALSE; + } + + if (autostart) + vshPrint(ctl, _("Pool %s marked as autostarted\n"), name); + else + vshPrint(ctl, _("Pool %s unmarked as autostarted\n"), name); + + return TRUE; +} + +/* + * "pool-create" command + */ +static vshCmdInfo info_pool_create[] = { + {"syntax", "create a pool from an XML <file>"}, + {"help", gettext_noop("create a pool from an XML file")}, + {"desc", gettext_noop("Create a pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_create[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file containing an XML pool description")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolCreate(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + char *from; + int found; + int ret = TRUE; + char *buffer; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + from = vshCommandOptString(cmd, "file", &found); + if (!found) + return FALSE; + + if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) + return FALSE; + + pool = virStoragePoolCreateXML(ctl->conn, buffer); + free (buffer); + + if (pool != NULL) { + vshPrint(ctl, _("Pool %s created from %s\n"), + virStoragePoolGetName(pool), from); + } else { + vshError(ctl, FALSE, _("Failed to create pool from %s"), from); + ret = FALSE; + } + return ret; +} + + +/* + * "pool-define" command + */ +static vshCmdInfo info_pool_define[] = { + {"syntax", "define a pool from an XML <file>"}, + {"help", gettext_noop("define (but don't start) a pool from an XML file")}, + {"desc", gettext_noop("Define a pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_define[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file containing an XML pool description")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolDefine(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + char *from; + int found; + int ret = TRUE; + char *buffer; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + from = vshCommandOptString(cmd, "file", &found); + if (!found) + return FALSE; + + if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) + return FALSE; + + pool = virStoragePoolDefineXML(ctl->conn, buffer); + free (buffer); + + if (pool != NULL) { + vshPrint(ctl, _("Pool %s defined from %s\n"), + virStoragePoolGetName(pool), from); + } else { + vshError(ctl, FALSE, _("Failed to define pool from %s"), from); + ret = FALSE; + } + return ret; +} + + +/* + * "pool-build" command + */ +static vshCmdInfo info_pool_build[] = { + {"syntax", "pool-build <pool>"}, + {"help", gettext_noop("build a pool")}, + {"desc", gettext_noop("Build a given pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_build[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolBuild(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name))) + return FALSE; + + if (virStoragePoolBuild(pool, 0) == 0) { + vshPrint(ctl, _("Pool %s builded\n"), name); + } else { + vshError(ctl, FALSE, _("Failed to build pool %s"), name); + ret = FALSE; + virStoragePoolFree(pool); + } + + return ret; +} + + +/* + * "pool-destroy" command + */ +static vshCmdInfo info_pool_destroy[] = { + {"syntax", "pool-destroy <pool>"}, + {"help", gettext_noop("destroy a pool")}, + {"desc", gettext_noop("Destroy a given pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_destroy[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolDestroy(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name))) + return FALSE; + + if (virStoragePoolDestroy(pool) == 0) { + vshPrint(ctl, _("Pool %s destroyed\n"), name); + } else { + vshError(ctl, FALSE, _("Failed to destroy pool %s"), name); + ret = FALSE; + virStoragePoolFree(pool); + } + + return ret; +} + + +/* + * "pool-delete" command + */ +static vshCmdInfo info_pool_delete[] = { + {"syntax", "pool-delete <pool>"}, + {"help", gettext_noop("delete a pool")}, + {"desc", gettext_noop("Delete a given pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_delete[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolDelete(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name))) + return FALSE; + + if (virStoragePoolDelete(pool, 0) == 0) { + vshPrint(ctl, _("Pool %s deleteed\n"), name); + } else { + vshError(ctl, FALSE, _("Failed to delete pool %s"), name); + ret = FALSE; + virStoragePoolFree(pool); + } + + return ret; +} + + +/* + * "pool-refresh" command + */ +static vshCmdInfo info_pool_refresh[] = { + {"syntax", "pool-refresh <pool>"}, + {"help", gettext_noop("refresh a pool")}, + {"desc", gettext_noop("Refresh a given pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_refresh[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolRefresh(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name))) + return FALSE; + + if (virStoragePoolRefresh(pool, 0) == 0) { + vshPrint(ctl, _("Pool %s refreshed\n"), name); + } else { + vshError(ctl, FALSE, _("Failed to refresh pool %s"), name); + ret = FALSE; + } + virStoragePoolFree(pool); + + return ret; +} + + +/* + * "pool-dumpxml" command + */ +static vshCmdInfo info_pool_dumpxml[] = { + {"syntax", "pool-dumpxml <pool>"}, + {"help", gettext_noop("pool information in XML")}, + {"desc", gettext_noop("Output the pool information as an XML dump to stdout.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_dumpxml[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolDumpXML(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int ret = TRUE; + char *dump; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL))) + return FALSE; + + dump = virStoragePoolGetXMLDesc(pool, 0); + if (dump != NULL) { + printf("%s", dump); + free(dump); + } else { + ret = FALSE; + } + + virStoragePoolFree(pool); + return ret; +} + + +/* + * "pool-list" command + */ +static vshCmdInfo info_pool_list[] = { + {"syntax", "pool-list [ --inactive | --all ]"}, + {"help", gettext_noop("list pools")}, + {"desc", gettext_noop("Returns list of pools.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_list[] = { + {"inactive", VSH_OT_BOOL, 0, gettext_noop("list inactive pools")}, + {"all", VSH_OT_BOOL, 0, gettext_noop("list inactive & active pools")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolList(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED) +{ + int inactive = vshCommandOptBool(cmd, "inactive"); + int all = vshCommandOptBool(cmd, "all"); + int active = !inactive || all ? 1 : 0; + int maxactive = 0, maxinactive = 0, i; + char **activeNames = NULL, **inactiveNames = NULL; + inactive |= all; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (active) { + maxactive = virConnectNumOfStoragePools(ctl->conn); + if (maxactive < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list active pools")); + return FALSE; + } + if (maxactive) { + activeNames = vshMalloc(ctl, sizeof(char *) * maxactive); + + if ((maxactive = virConnectListStoragePools(ctl->conn, activeNames, + maxactive)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list active pools")); + free(activeNames); + return FALSE; + } + + qsort(&activeNames[0], maxactive, sizeof(char *), namesorter); + } + } + if (inactive) { + maxinactive = virConnectNumOfDefinedStoragePools(ctl->conn); + if (maxinactive < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list inactive pools")); + free(activeNames); + return FALSE; + } + if (maxinactive) { + inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive); + + if ((maxinactive = virConnectListDefinedStoragePools(ctl->conn, inactiveNames, maxinactive)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list inactive pools")); + free(activeNames); + free(inactiveNames); + return FALSE; + } + + qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter); + } + } + vshPrintExtra(ctl, "%-20s %-10s %-10s\n", _("Name"), _("State"), _("Autostart")); + vshPrintExtra(ctl, "-----------------------------------------\n"); + + for (i = 0; i < maxactive; i++) { + virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn, activeNames[i]); + const char *autostartStr; + int autostart = 0; + + /* this kind of work with pools is not atomic operation */ + if (!pool) { + free(activeNames[i]); + continue; + } + + if (virStoragePoolGetAutostart(pool, &autostart) < 0) + autostartStr = _("no autostart"); + else + autostartStr = autostart ? "yes" : "no"; + + vshPrint(ctl, "%-20s %-10s %-10s\n", + virStoragePoolGetName(pool), + _("active"), + autostartStr); + virStoragePoolFree(pool); + free(activeNames[i]); + } + for (i = 0; i < maxinactive; i++) { + virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn, inactiveNames[i]); + const char *autostartStr; + int autostart = 0; + + /* this kind of work with pools is not atomic operation */ + if (!pool) { + free(inactiveNames[i]); + continue; + } + + if (virStoragePoolGetAutostart(pool, &autostart) < 0) + autostartStr = _("no autostart"); + else + autostartStr = autostart ? "yes" : "no"; + + vshPrint(ctl, "%-20s %-10s %-10s\n", + inactiveNames[i], + _("inactive"), + autostartStr); + + virStoragePoolFree(pool); + free(inactiveNames[i]); + } + free(activeNames); + free(inactiveNames); + return TRUE; +} + +/* + * "pool-discover" command + */ +static vshCmdInfo info_pool_discover[] = { + {"syntax", "pool-discover <hostname> <type>"}, + {"help", gettext_noop("discover pools")}, + {"desc", gettext_noop("Returns discover of pools.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_discover[] = { + {"hostname", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("hostname to discover pools on")}, + {"type", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("type of storage pool to discover")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolDiscover(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED) +{ + char *hostname, *type; + char **xmlDesc = NULL; + int npool, i; + int found; + + hostname = vshCommandOptString(cmd, "hostname", &found); + if (!found) + return FALSE; + type = vshCommandOptString(cmd, "type", &found); + if (!found) + return FALSE; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if ((npool = virConnectDiscoverStoragePools(ctl->conn, + hostname, + type, + 0, + &xmlDesc)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to discover pools")); + return FALSE; + } + + for (i = 0 ; i < npool ; i++) { + vshPrint(ctl, "%s", xmlDesc[i]); + free(xmlDesc[i]); + } + free(xmlDesc); + + return TRUE; +} + +static double +prettyCapacity(unsigned long long val, + const char **unit) { + if (val < 1024) { + *unit = ""; + return (double)val; + } else if (val < (1024.0l * 1024.0l)) { + *unit = "KB"; + return (((double)val / 1024.0l)); + } else if (val < (1024.0l * 1024.0l * 1024.0l)) { + *unit = "MB"; + return ((double)val / (1024.0l * 1024.0l)); + } else if (val < (1024.0l * 1024.0l * 1024.0l * 1024.0l)) { + *unit = "GB"; + return ((double)val / (1024.0l * 1024.0l * 1024.0l)); + } else { + *unit = "TB"; + return ((double)val / (1024.0l * 1024.0l * 1024.0l * 1024.0l)); + } +} + +/* + * "pool-info" command + */ +static vshCmdInfo info_pool_info[] = { + {"syntax", "pool-info <pool>"}, + {"help", gettext_noop("storage pool information")}, + {"desc", gettext_noop("Returns basic information about the storage pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_info[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolInfo(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolInfo info; + virStoragePoolPtr pool; + int ret = TRUE; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL))) + return FALSE; + + vshPrint(ctl, "%-15s %s\n", _("Name:"), virStoragePoolGetName(pool)); + + if (virStoragePoolGetUUIDString(pool, &uuid[0])==0) + vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid); + + if (virStoragePoolGetInfo(pool, &info) == 0) { + double val; + const char *unit; + switch (info.state) { + case VIR_STORAGE_POOL_INACTIVE: + vshPrint(ctl, "%-15s %s\n", _("State:"), + _("inactive")); + break; + case VIR_STORAGE_POOL_BUILDING: + vshPrint(ctl, "%-15s %s\n", _("State:"), + _("building")); + break; + case VIR_STORAGE_POOL_RUNNING: + vshPrint(ctl, "%-15s %s\n", _("State:"), + _("running")); + break; + case VIR_STORAGE_POOL_DEGRADED: + vshPrint(ctl, "%-15s %s\n", _("State:"), + _("degraded")); + break; + } + + if (info.state == VIR_STORAGE_POOL_RUNNING || + info.state == VIR_STORAGE_POOL_DEGRADED) { + val = prettyCapacity(info.capacity, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit); + + val = prettyCapacity(info.allocation, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit); + + val = prettyCapacity(info.available, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Available:"), val, unit); + } + } else { + ret = FALSE; + } + + virStoragePoolFree(pool); + return ret; +} + + +/* + * "pool-name" command + */ +static vshCmdInfo info_pool_name[] = { + {"syntax", "pool-name <pool>"}, + {"help", gettext_noop("convert a pool UUID to pool name")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_name[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolName(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, + VSH_BYUUID))) + return FALSE; + + vshPrint(ctl, "%s\n", virStoragePoolGetName(pool)); + virStoragePoolFree(pool); + return TRUE; +} + + +/* + * "pool-start" command + */ +static vshCmdInfo info_pool_start[] = { + {"syntax", "start <pool>"}, + {"help", gettext_noop("start a (previously defined) inactive pool")}, + {"desc", gettext_noop("Start a pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_start[] = { + {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive pool")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolStart(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int ret = TRUE; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPoolBy(ctl, cmd, "name", NULL, VSH_BYNAME))) + return FALSE; + + if (virStoragePoolCreate(pool) == 0) { + vshPrint(ctl, _("Pool %s started\n"), + virStoragePoolGetName(pool)); + } else { + vshError(ctl, FALSE, _("Failed to start pool %s"), + virStoragePoolGetName(pool)); + ret = FALSE; + } + return ret; +} + + +/* + * "pool-undefine" command + */ +static vshCmdInfo info_pool_undefine[] = { + {"syntax", "pool-undefine <pool>"}, + {"help", gettext_noop("undefine an inactive pool")}, + {"desc", gettext_noop("Undefine the configuration for an inactive pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_undefine[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolUndefine(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name))) + return FALSE; + + if (virStoragePoolUndefine(pool) == 0) { + vshPrint(ctl, _("Pool %s has been undefined\n"), name); + } else { + vshError(ctl, FALSE, _("Failed to undefine pool %s"), name); + ret = FALSE; + } + + return ret; +} + + +/* + * "pool-uuid" command + */ +static vshCmdInfo info_pool_uuid[] = { + {"syntax", "pool-uuid <pool>"}, + {"help", gettext_noop("convert a pool name to pool UUID")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_uuid[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolUuid(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, + VSH_BYNAME))) + return FALSE; + + if (virStoragePoolGetUUIDString(pool, uuid) != -1) + vshPrint(ctl, "%s\n", uuid); + else + vshError(ctl, FALSE, "%s", _("failed to get pool UUID")); + + return TRUE; +} + + + + +/* + * "vol-create" command + */ +static vshCmdInfo info_vol_create[] = { + {"syntax", "create <file>"}, + {"help", gettext_noop("create a vol from an XML file")}, + {"desc", gettext_noop("Create a vol.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vol_create[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name")}, + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file containing an XML vol description")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolCreate(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + virStorageVolPtr vol; + char *from; + int found; + int ret = TRUE; + char *buffer; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, + VSH_BYNAME))) + return FALSE; + + from = vshCommandOptString(cmd, "file", &found); + if (!found) { + virStoragePoolFree(pool); + return FALSE; + } + + if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) { + virStoragePoolFree(pool); + return FALSE; + } + + vol = virStorageVolCreateXML(pool, buffer, 0); + free (buffer); + virStoragePoolFree(pool); + + if (vol != NULL) { + vshPrint(ctl, _("Vol %s created from %s\n"), + virStorageVolGetName(vol), from); + virStorageVolFree(vol); + } else { + vshError(ctl, FALSE, _("Failed to create vol from %s"), from); + ret = FALSE; + } + return ret; +} + +/* + * "vol-delete" command + */ +static vshCmdInfo info_vol_delete[] = { + {"syntax", "vol-delete <vol>"}, + {"help", gettext_noop("delete a vol")}, + {"desc", gettext_noop("Delete a given vol.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vol_delete[] = { + {"pool", VSH_OT_STRING, 0, gettext_noop("pool name or uuid")}, + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("vol name, key or path")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolDelete(vshControl * ctl, vshCmd * cmd) +{ + virStorageVolPtr vol; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) { + return FALSE; + } + + if (virStorageVolDelete(vol, 0) == 0) { + vshPrint(ctl, _("Vol %s deleteed\n"), name); + } else { + vshError(ctl, FALSE, _("Failed to delete vol %s"), name); + ret = FALSE; + virStorageVolFree(vol); + } + + return ret; +} + + +/* + * "vol-info" command + */ +static vshCmdInfo info_vol_info[] = { + {"syntax", "vol-info <vol>"}, + {"help", gettext_noop("storage vol information")}, + {"desc", gettext_noop("Returns basic information about the storage vol.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vol_info[] = { + {"pool", VSH_OT_STRING, 0, gettext_noop("pool name or uuid")}, + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("vol name, key or path")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolInfo(vshControl * ctl, vshCmd * cmd) +{ + virStorageVolInfo info; + virStorageVolPtr vol; + int ret = TRUE; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) + return FALSE; + + vshPrint(ctl, "%-15s %s\n", _("Name:"), virStorageVolGetName(vol)); + + if (virStorageVolGetInfo(vol, &info) == 0) { + double val; + const char *unit; + vshPrint(ctl, "%-15s %s\n", _("Type:"), + info.type == VIR_STORAGE_VOL_FILE ? + _("file") : _("block")); + + val = prettyCapacity(info.capacity, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit); + + val = prettyCapacity(info.allocation, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit); + } else { + ret = FALSE; + } + + virStorageVolFree(vol); + return ret; +} + + +/* + * "vol-dumpxml" command + */ +static vshCmdInfo info_vol_dumpxml[] = { + {"syntax", "vol-dumpxml <vol>"}, + {"help", gettext_noop("vol information in XML")}, + {"desc", gettext_noop("Output the vol information as an XML dump to stdout.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vol_dumpxml[] = { + {"pool", VSH_OT_STRING, 0, gettext_noop("pool name or uuid")}, + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("vol name, key or path")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolDumpXML(vshControl * ctl, vshCmd * cmd) +{ + virStorageVolPtr vol; + int ret = TRUE; + char *dump; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) + return FALSE; + + dump = virStorageVolGetXMLDesc(vol, 0); + if (dump != NULL) { + printf("%s", dump); + free(dump); + } else { + ret = FALSE; + } + + virStorageVolFree(vol); + return ret; +} + + +/* + * "vol-list" command + */ +static vshCmdInfo info_vol_list[] = { + {"syntax", "vol-list <pool>"}, + {"help", gettext_noop("list vols")}, + {"desc", gettext_noop("Returns list of vols by pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vol_list[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolList(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED) +{ + virStoragePoolPtr pool; + int maxactive = 0, i; + char **activeNames = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL))) + return FALSE; + + maxactive = virStoragePoolNumOfVolumes(pool); + if (maxactive < 0) { + virStoragePoolFree(pool); + vshError(ctl, FALSE, "%s", _("Failed to list active vols")); + return FALSE; + } + if (maxactive) { + activeNames = vshMalloc(ctl, sizeof(char *) * maxactive); + + if ((maxactive = virStoragePoolListVolumes(pool, activeNames, + maxactive)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list active vols")); + free(activeNames); + virStoragePoolFree(pool); + return FALSE; + } + + qsort(&activeNames[0], maxactive, sizeof(char *), namesorter); + } + vshPrintExtra(ctl, "%-20s %-40s\n", _("Name"), _("Path")); + vshPrintExtra(ctl, "-----------------------------------------\n"); + + for (i = 0; i < maxactive; i++) { + virStorageVolPtr vol = virStorageVolLookupByName(pool, activeNames[i]); + char *path; + + /* this kind of work with vols is not atomic operation */ + if (!vol) { + free(activeNames[i]); + continue; + } + + if ((path = virStorageVolGetPath(vol)) == NULL) { + virStorageVolFree(vol); + continue; + } + + + vshPrint(ctl, "%-20s %-40s\n", + virStorageVolGetName(vol), + path); + free(path); + virStorageVolFree(vol); + free(activeNames[i]); + } + free(activeNames); + virStoragePoolFree(pool); + return TRUE; +} + + +/* + * "vol-name" command + */ +static vshCmdInfo info_vol_name[] = { + {"syntax", "vol-name <vol>"}, + {"help", gettext_noop("convert a vol UUID to vol name")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vol_name[] = { + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("vol key or path")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolName(vshControl * ctl, vshCmd * cmd) +{ + virStorageVolPtr vol; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", "pool", NULL, + VSH_BYUUID))) + return FALSE; + + vshPrint(ctl, "%s\n", virStorageVolGetName(vol)); + virStorageVolFree(vol); + return TRUE; +} + + + +/* + * "vol-key" command + */ +static vshCmdInfo info_vol_key[] = { + {"syntax", "vol-key <vol>"}, + {"help", gettext_noop("convert a vol UUID to vol key")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vol_key[] = { + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("vol uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolKey(vshControl * ctl, vshCmd * cmd) +{ + virStorageVolPtr vol; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL, + VSH_BYUUID))) + return FALSE; + + vshPrint(ctl, "%s\n", virStorageVolGetKey(vol)); + virStorageVolFree(vol); + return TRUE; +} + + + +/* + * "vol-path" command + */ +static vshCmdInfo info_vol_path[] = { + {"syntax", "vol-path <pool> <vol>"}, + {"help", gettext_noop("convert a vol UUID to vol path")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vol_path[] = { + {"pool", VSH_OT_STRING, 0, gettext_noop("pool name or uuid")}, + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("vol name or key")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolPath(vshControl * ctl, vshCmd * cmd) +{ + virStorageVolPtr vol; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", "pool", NULL, + VSH_BYUUID))) + return FALSE; + + vshPrint(ctl, "%s\n", virStorageVolGetPath(vol)); + virStorageVolFree(vol); + return TRUE; +} + + + + + + + /* * "version" command */ @@ -3111,7 +4284,7 @@ cmdVersion(vshControl * ctl, vshCmd * cm } /* - * "hostname" command + * "hostkey" command */ static vshCmdInfo info_hostname[] = { {"syntax", "hostname"}, @@ -3971,6 +5144,7 @@ static vshCmdDef commands[] = { {"hostname", cmdHostname, NULL, info_hostname}, {"list", cmdList, opts_list, info_list}, {"migrate", cmdMigrate, opts_migrate, info_migrate}, + {"net-autostart", cmdNetworkAutostart, opts_network_autostart, info_network_autostart}, {"net-create", cmdNetworkCreate, opts_network_create, info_network_create}, {"net-define", cmdNetworkDefine, opts_network_define, info_network_define}, @@ -3982,6 +5156,23 @@ static vshCmdDef commands[] = { {"net-undefine", cmdNetworkUndefine, opts_network_undefine, info_network_undefine}, {"net-uuid", cmdNetworkUuid, opts_network_uuid, info_network_uuid}, {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo}, + + {"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}, + {"pool-define", cmdPoolDefine, opts_pool_define, info_pool_define}, + {"pool-destroy", cmdPoolDestroy, opts_pool_destroy, info_pool_destroy}, + {"pool-delete", cmdPoolDelete, opts_pool_delete, info_pool_delete}, + {"pool-discover", cmdPoolDiscover, opts_pool_discover, info_pool_discover}, + {"pool-dumpxml", cmdPoolDumpXML, opts_pool_dumpxml, info_pool_dumpxml}, + {"pool-info", cmdPoolInfo, opts_pool_info, info_pool_info}, + {"pool-list", cmdPoolList, opts_pool_list, info_pool_list}, + {"pool-name", cmdPoolName, opts_pool_name, info_pool_name}, + {"pool-refresh", cmdPoolRefresh, opts_pool_refresh, info_pool_refresh}, + {"pool-start", cmdPoolStart, opts_pool_start, info_pool_start}, + {"pool-undefine", cmdPoolUndefine, opts_pool_undefine, info_pool_undefine}, + {"pool-uuid", cmdPoolUuid, opts_pool_uuid, info_pool_uuid}, + {"quit", cmdQuit, NULL, info_quit}, {"reboot", cmdReboot, opts_reboot, info_reboot}, {"restore", cmdRestore, opts_restore, info_restore}, @@ -3997,6 +5188,16 @@ static vshCmdDef commands[] = { {"ttyconsole", cmdTTYConsole, opts_ttyconsole, info_ttyconsole}, {"undefine", cmdUndefine, opts_undefine, info_undefine}, {"uri", cmdURI, NULL, info_uri}, + + {"vol-create", cmdVolCreate, opts_vol_create, info_vol_create}, + {"vol-delete", cmdVolDelete, opts_vol_delete, info_vol_delete}, + {"vol-dumpxml", cmdVolDumpXML, opts_vol_dumpxml, info_vol_dumpxml}, + {"vol-info", cmdVolInfo, opts_vol_info, info_vol_info}, + {"vol-list", cmdVolList, opts_vol_list, info_vol_list}, + {"vol-path", cmdVolPath, opts_vol_path, info_vol_path}, + {"vol-name", cmdVolName, opts_vol_name, info_vol_name}, + {"vol-key", cmdVolKey, opts_vol_key, info_vol_key}, + {"vcpuinfo", cmdVcpuinfo, opts_vcpuinfo, info_vcpuinfo}, {"vcpupin", cmdVcpupin, opts_vcpupin, info_vcpupin}, {"version", cmdVersion, NULL, info_version}, @@ -4320,6 +5521,99 @@ vshCommandOptNetworkBy(vshControl * ctl, vshError(ctl, FALSE, _("failed to get network '%s'"), n); return network; +} + +static virStoragePoolPtr +vshCommandOptPoolBy(vshControl * ctl, vshCmd * cmd, const char *optname, + char **name, int flag) +{ + virStoragePoolPtr pool = NULL; + char *n; + + if (!(n = vshCommandOptString(cmd, optname, NULL))) { + vshError(ctl, FALSE, "%s", _("undefined pool name")); + return NULL; + } + + vshDebug(ctl, 5, "%s: found option <%s>: %s\n", + cmd->def->name, optname, n); + + if (name) + *name = n; + + /* try it by UUID */ + if (pool==NULL && (flag & VSH_BYUUID) && strlen(n)==VIR_UUID_STRING_BUFLEN-1) { + vshDebug(ctl, 5, "%s: <%s> trying as pool UUID\n", + cmd->def->name, optname); + pool = virStoragePoolLookupByUUIDString(ctl->conn, n); + } + /* try it by NAME */ + if (pool==NULL && (flag & VSH_BYNAME)) { + vshDebug(ctl, 5, "%s: <%s> trying as pool NAME\n", + cmd->def->name, optname); + pool = virStoragePoolLookupByName(ctl->conn, n); + } + + if (!pool) + vshError(ctl, FALSE, _("failed to get pool '%s'"), n); + + return pool; +} + +static virStorageVolPtr +vshCommandOptVolBy(vshControl * ctl, vshCmd * cmd, + const char *optname, + const char *pooloptname, + char **name, int flag) +{ + virStorageVolPtr vol = NULL; + virStoragePoolPtr pool = NULL; + char *n, *p; + int found; + + if (!(n = vshCommandOptString(cmd, optname, NULL))) { + vshError(ctl, FALSE, "%s", _("undefined vol name")); + return NULL; + } + + if (!(p = vshCommandOptString(cmd, pooloptname, &found)) && found) { + vshError(ctl, FALSE, "%s", _("undefined pool name")); + return NULL; + } + + if (p) + pool = vshCommandOptPoolBy(ctl, cmd, pooloptname, name, flag); + + vshDebug(ctl, 5, "%s: found option <%s>: %s\n", + cmd->def->name, optname, n); + + if (name) + *name = n; + + /* try it by PATH */ + if (pool && (flag & VSH_BYNAME)) { + vshDebug(ctl, 5, "%s: <%s> trying as vol UUID\n", + cmd->def->name, optname); + vol = virStorageVolLookupByName(pool, n); + } + if (vol == NULL && (flag & VSH_BYUUID)) { + vshDebug(ctl, 5, "%s: <%s> trying as vol key\n", + cmd->def->name, optname); + vol = virStorageVolLookupByKey(ctl->conn, n); + } + if (vol == NULL && (flag & VSH_BYUUID)) { + vshDebug(ctl, 5, "%s: <%s> trying as vol path\n", + cmd->def->name, optname); + vol = virStorageVolLookupByPath(ctl->conn, n); + } + + if (!vol) + vshError(ctl, FALSE, _("failed to get vol '%s'"), n); + + if (pool) + virStoragePoolFree(pool); + + return vol; } /* -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:34:34AM +0000, Daniel P. Berrange wrote:
This patch adds new virsh commands for all the libvirt storage APIs allowing their use from the shell. The style follows that of the existing domain & network commands, so I won't bother listing them in great detail.
The only complexity is in dealing with the vol-XXX commands. These can accept either a full volume path, a volume key (equiv of UUID) or a pool name + volume name. The latter presents a difficulty because our GetOpt() style parser does not know how to have commands which take either 1 or 2 args - it can do, one or the other but not both. So to specify a pool name + volume name requires the slightly ugly use of --pool flag. Most people will probably just use paths though.
Sounds fine.
@@ -331,7 +349,7 @@ cmdMonitorProgress(vshControl *ctl, vshC
do { if (virJobGetInfo(job, info) < 0) { - vshError(ctl, FALSE, _("Failed to get job status")); + vshError(ctl, FALSE, "%s", _("Failed to get job status")); return -1; }
Those vshError localization changes are fine and independant from storage, let's push them in CVS in any case. [...]
+static int +cmdPoolDelete(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name))) + return FALSE; + + if (virStoragePoolDelete(pool, 0) == 0) { + vshPrint(ctl, _("Pool %s deleteed\n"), name); + } else { + vshError(ctl, FALSE, _("Failed to delete pool %s"), name); + ret = FALSE; + virStoragePoolFree(pool); + } + + return ret; +}
just wondering, assuming the Delete operation really destroys on-disk storage and potentially a large set, shouldn't we add some kind of interactive confirmation ? Contrary to destroying a domain where state is preserved on the disk and rather easy to recover and destroying a network which has very little state, maybe here we need to do something special, optionally adding a -f flag to bypass confirmation like in rm.
+ +/* + * "vol-delete" command + */ +static vshCmdInfo info_vol_delete[] = { + {"syntax", "vol-delete <vol>"}, + {"help", gettext_noop("delete a vol")}, + {"desc", gettext_noop("Delete a given vol.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vol_delete[] = { + {"pool", VSH_OT_STRING, 0, gettext_noop("pool name or uuid")}, + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("vol name, key or path")}, + {NULL, 0, 0, NULL} +};
same for a volume. No surprise in that code, +1 Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Mon, Feb 18, 2008 at 06:12:47AM -0500, Daniel Veillard wrote:
+static int +cmdPoolDelete(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name))) + return FALSE; + + if (virStoragePoolDelete(pool, 0) == 0) { + vshPrint(ctl, _("Pool %s deleteed\n"), name); + } else { + vshError(ctl, FALSE, _("Failed to delete pool %s"), name); + ret = FALSE; + virStoragePoolFree(pool); + } + + return ret; +}
just wondering, assuming the Delete operation really destroys on-disk storage and potentially a large set, shouldn't we add some kind of interactive confirmation ? Contrary to destroying a domain where state is preserved on the disk and rather easy to recover and destroying a network which has very little state, maybe here we need to do something special, optionally adding a -f flag to bypass confirmation like in rm.
Well the 'rm' command doesn't do confirmation by default. It only asks for confirmation if you add the '-i' flag. The '-f' flag lets you them override the '-i' flag. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This patch adds 3 extra commands to virsh to allow storage pools and volumes to be defined & created without using XML. These have the names 'pool-define-as', 'pool-create-as', 'vol-create-as'. virsh will take the args to these commands and build suitable XML to pass into the libvirt APIs. This is a useful capability for people quickly shell scripting the virsh command, since they don't need to build up XML in the shell directly. In doing this we need to expose a couple of virBuffer* APIs to virsh. Turning these calls into macros exposed a couple of places where the existing code calls the virBufferVSprintf() function without actually having any va_args. These were turned int virBufferAdd calls instead. buf.c | 11 + buf.h | 12 + libvirt_sym.version | 4 qemu_conf.c | 6 virsh.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++++++++ xend_internal.c | 22 +-- xm_internal.c | 2 xml.c | 8 - 8 files changed, 386 insertions(+), 26 deletions(-) diff -r 09ce185344bd src/buf.c --- a/src/buf.c Thu Feb 07 12:33:30 2008 -0500 +++ b/src/buf.c Thu Feb 07 12:33:33 2008 -0500 @@ -61,7 +61,7 @@ virBufferGrow(virBufferPtr buf, unsigned * Returns 0 successful, -1 in case of internal or API error. */ int -virBufferAdd(virBufferPtr buf, const char *str, int len) +__virBufferAdd(virBufferPtr buf, const char *str, int len) { unsigned int needSize; @@ -97,7 +97,7 @@ virBufferAdd(virBufferPtr buf, const cha * Returns 0 if successful, -1 in the case of error. */ int -virBufferAddChar (virBufferPtr buf, char c) +__virBufferAddChar (virBufferPtr buf, char c) { unsigned int needSize; @@ -190,7 +190,7 @@ virBufferContentAndFree (virBufferPtr bu * Returns 0 successful, -1 in case of internal or API error. */ int -virBufferVSprintf(virBufferPtr buf, const char *format, ...) +__virBufferVSprintf(virBufferPtr buf, const char *format, ...) { int size, count, grow_size; va_list locarg, argptr; @@ -198,6 +198,11 @@ virBufferVSprintf(virBufferPtr buf, cons if ((format == NULL) || (buf == NULL)) { return (-1); } + + if (buf->size == 0 && + virBufferGrow(buf, 100) < 0) + return -1; + size = buf->size - buf->use - 1; va_start(argptr, format); va_copy(locarg, argptr); diff -r 09ce185344bd src/buf.h --- a/src/buf.h Thu Feb 07 12:33:30 2008 -0500 +++ b/src/buf.h Thu Feb 07 12:33:33 2008 -0500 @@ -29,15 +29,19 @@ virBufferPtr virBufferNew(unsigned int s virBufferPtr virBufferNew(unsigned int size); void virBufferFree(virBufferPtr buf); char *virBufferContentAndFree(virBufferPtr buf); -int virBufferAdd(virBufferPtr buf, const char *str, int len); -int virBufferAddChar(virBufferPtr buf, char c); -int virBufferVSprintf(virBufferPtr buf, const char *format, ...) +int __virBufferAdd(virBufferPtr buf, const char *str, int len); +int __virBufferAddChar(virBufferPtr buf, char c); +int __virBufferVSprintf(virBufferPtr buf, const char *format, ...) ATTRIBUTE_FORMAT(printf, 2, 3); int virBufferStrcat(virBufferPtr buf, ...); int virBufferEscapeString(virBufferPtr buf, const char *format, const char *str); int virBufferURIEncodeString (virBufferPtr buf, const char *str); #define virBufferAddLit(buf_, literal_string_) \ - virBufferAdd (buf_, "" literal_string_ "", sizeof literal_string_ - 1) + __virBufferAdd (buf_, "" literal_string_ "", sizeof literal_string_ - 1) + +#define virBufferAdd(b,s,l) __virBufferAdd((b),(s),(l)) +#define virBufferAddChar(b,c) __virBufferAddChar((b),(c)) +#define virBufferVSprintf(b,f,...) __virBufferVSprintf((b),(f), __VA_ARGS__) #endif /* __VIR_BUFFER_H__ */ diff -r 09ce185344bd src/libvirt_sym.version --- a/src/libvirt_sym.version Thu Feb 07 12:33:30 2008 -0500 +++ b/src/libvirt_sym.version Thu Feb 07 12:33:33 2008 -0500 @@ -194,5 +194,9 @@ __virFileReadAll; + __virBufferVSprintf; + __virBufferAdd; + __virBufferAddChar; + local: *; }; diff -r 09ce185344bd src/qemu_conf.c --- a/src/qemu_conf.c Thu Feb 07 12:33:30 2008 -0500 +++ b/src/qemu_conf.c Thu Feb 07 12:33:33 2008 -0500 @@ -2818,7 +2818,7 @@ char *qemudGenerateXML(virConnectPtr con if (virBufferAddLit(buf, " <readonly/>\n") < 0) goto no_memory; - if (virBufferVSprintf(buf, " </disk>\n") < 0) + if (virBufferAddLit(buf, " </disk>\n") < 0) goto no_memory; disk = disk->next; @@ -2889,7 +2889,7 @@ char *qemudGenerateXML(virConnectPtr con } } - if (virBufferVSprintf(buf, " </interface>\n") < 0) + if (virBufferAddLit(buf, " </interface>\n") < 0) goto no_memory; net = net->next; @@ -2974,7 +2974,7 @@ char *qemudGenerateNetworkXML(virConnect if (!buf) goto no_memory; - if (virBufferVSprintf(buf, "<network>\n") < 0) + if (virBufferAddLit(buf, "<network>\n") < 0) goto no_memory; if (virBufferVSprintf(buf, " <name>%s</name>\n", def->name) < 0) diff -r 09ce185344bd src/virsh.c --- a/src/virsh.c Thu Feb 07 12:33:30 2008 -0500 +++ b/src/virsh.c Thu Feb 07 12:33:33 2008 -0500 @@ -50,6 +50,7 @@ #include "internal.h" #include "console.h" #include "util.h" +#include "buf.h" static char *progname; @@ -244,6 +245,9 @@ static int vshCommandOptInt(vshCmd * cmd static int vshCommandOptInt(vshCmd * cmd, const char *name, int *found); static char *vshCommandOptString(vshCmd * cmd, const char *name, int *found); +#if 0 +static int vshCommandOptStringList(vshCmd * cmd, const char *name, char ***data); +#endif static int vshCommandOptBool(vshCmd * cmd, const char *name); #define VSH_BYID (1 << 1) @@ -3157,6 +3161,100 @@ cmdPoolCreate(vshControl * ctl, vshCmd * return ret; } +/* + * "pool-create-as" command + */ +static vshCmdInfo info_pool_create_as[] = { + {"syntax", "pool-create-as <name> <type>"}, + {"help", gettext_noop("create a pool from a set of args")}, + {"desc", gettext_noop("Create a pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_create_as[] = { + {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the pool")}, + {"type", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("type of the pool")}, + {"source-host", VSH_OT_DATA, 0, gettext_noop("source-host for underlying storage")}, + {"source-path", VSH_OT_DATA, 0, gettext_noop("source path for underlying storage")}, + {"source-dev", VSH_OT_DATA, 0, gettext_noop("source device for underlying storage")}, + {"target", VSH_OT_DATA, 0, gettext_noop("target for underlying storage")}, + {NULL, 0, 0, NULL} +}; + + +static int +cmdPoolCreateAs(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int found; + char *name, *type, *srcHost, *srcPath, *srcDev, *target; + virBuffer buf; + + memset(&buf, 0, sizeof(buf)); + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + name = vshCommandOptString(cmd, "name", &found); + if (!found) + goto cleanup; + type = vshCommandOptString(cmd, "type", &found); + if (!found) + goto cleanup; + + srcHost = vshCommandOptString(cmd, "source-host", &found); + srcPath = vshCommandOptString(cmd, "source-path", &found); + srcDev = vshCommandOptString(cmd, "source-dev", &found); + target = vshCommandOptString(cmd, "target", &found); + + if (virBufferVSprintf(&buf, "<pool type='%s'>\n", type) < 0) + goto cleanup; + if (virBufferVSprintf(&buf, " <name>%s</name>\n", name) < 0) + goto cleanup; + if (srcHost || srcPath || srcDev) { + if (virBufferAddLit(&buf, " <source>\n") < 0) + goto cleanup; + if (srcHost && + virBufferVSprintf(&buf, " <host name='%s'>\n", srcHost) < 0) + goto cleanup; + if (srcPath && + virBufferVSprintf(&buf, " <dir path='%s'/>\n", srcPath) < 0) + goto cleanup; + if (srcDev && + virBufferVSprintf(&buf, " <device path='%s'/>\n", srcDev) < 0) + goto cleanup; + + if (virBufferAddLit(&buf, " </source>\n") < 0) + goto cleanup; + } + if (target) { + if (virBufferAddLit(&buf, " <target>\n") < 0) + goto cleanup; + if (virBufferVSprintf(&buf, " <path>%s</path>\n", target) < 0) + goto cleanup; + if (virBufferAddLit(&buf, " </target>\n") < 0) + goto cleanup; + } + if (virBufferAddLit(&buf, "</pool>\n") < 0) + goto cleanup; + + pool = virStoragePoolCreateXML(ctl->conn, buf.content); + free (buf.content); + + if (pool != NULL) { + vshPrint(ctl, _("Pool %s created\n"), name); + virStoragePoolFree(pool); + return TRUE; + } else { + vshError(ctl, FALSE, _("Failed to create pool %s"), name); + return FALSE; + } + + cleanup: + free(buf.content); + return FALSE; +} + /* * "pool-define" command @@ -3203,6 +3301,101 @@ cmdPoolDefine(vshControl * ctl, vshCmd * ret = FALSE; } return ret; +} + + +/* + * "pool-define-as" command + */ +static vshCmdInfo info_pool_define_as[] = { + {"syntax", "pool-define-as <name> <type>"}, + {"help", gettext_noop("define a pool from a set of args")}, + {"desc", gettext_noop("Define a pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_define_as[] = { + {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the pool")}, + {"type", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("type of the pool")}, + {"source-host", VSH_OT_DATA, 0, gettext_noop("source-host for underlying storage")}, + {"source-path", VSH_OT_DATA, 0, gettext_noop("source path for underlying storage")}, + {"source-dev", VSH_OT_DATA, 0, gettext_noop("source device for underlying storage")}, + {"target", VSH_OT_DATA, 0, gettext_noop("target for underlying storage")}, + {NULL, 0, 0, NULL} +}; + + +static int +cmdPoolDefineAs(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + int found; + char *name, *type, *srcHost, *srcPath, *srcDev, *target; + virBuffer buf; + + memset(&buf, 0, sizeof(buf)); + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + name = vshCommandOptString(cmd, "name", &found); + if (!found) + goto cleanup; + type = vshCommandOptString(cmd, "type", &found); + if (!found) + goto cleanup; + + srcHost = vshCommandOptString(cmd, "source-host", &found); + srcPath = vshCommandOptString(cmd, "source-path", &found); + srcDev = vshCommandOptString(cmd, "source-dev", &found); + target = vshCommandOptString(cmd, "target", &found); + + if (virBufferVSprintf(&buf, "<pool type='%s'>\n", type) < 0) + goto cleanup; + if (virBufferVSprintf(&buf, " <name>%s</name>\n", name) < 0) + goto cleanup; + if (srcHost || srcPath || srcDev) { + if (virBufferAddLit(&buf, " <source>\n") < 0) + goto cleanup; + if (srcHost && + virBufferVSprintf(&buf, " <host>%s</host>\n", srcHost) < 0) + goto cleanup; + if (srcPath && + virBufferVSprintf(&buf, " <path>%s</path>\n", srcPath) < 0) + goto cleanup; + if (srcDev && + virBufferVSprintf(&buf, " <device>%s</device>\n", srcDev) < 0) + goto cleanup; + + if (virBufferAddLit(&buf, " </source>\n") < 0) + goto cleanup; + } + if (target) { + if (virBufferAddLit(&buf, " <target>\n") < 0) + goto cleanup; + if (virBufferVSprintf(&buf, " <path>%s</path>\n", target) < 0) + goto cleanup; + if (virBufferAddLit(&buf, " </target>\n") < 0) + goto cleanup; + } + if (virBufferAddLit(&buf, "</pool>\n") < 0) + goto cleanup; + + pool = virStoragePoolDefineXML(ctl->conn, buf.content); + free (buf.content); + + if (pool != NULL) { + vshPrint(ctl, _("Pool %s defined\n"), name); + virStoragePoolFree(pool); + return TRUE; + } else { + vshError(ctl, FALSE, _("Failed to define pool %s"), name); + return FALSE; + } + + cleanup: + free(buf.content); + return FALSE; } @@ -3746,6 +3939,131 @@ cmdPoolStart(vshControl * ctl, vshCmd * ret = FALSE; } return ret; +} + + +/* + * "vol-create-as" command + */ +static vshCmdInfo info_vol_create_as[] = { + {"syntax", "create-as <pool> <name> <capacity>"}, + {"help", gettext_noop("create a vol from a set of as")}, + {"desc", gettext_noop("Create a vol.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vol_create_as[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name")}, + {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the vol")}, + {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("size of the vol with optional k,M,G,T suffix")}, + {"allocation", VSH_OT_DATA, 0, gettext_noop("initial allocation size with optional k,M,G,T suffix")}, + {"format", VSH_OT_DATA, 0, gettext_noop("file format type raw,bochs,qcow,qcow2,vmdk")}, + {NULL, 0, 0, NULL} +}; + +static int cmdVolSize(const char *data, unsigned long long *val) +{ + char *end; + if (xstrtol_ull(data, &end, 10, val) < 0) + return -1; + + if (end && *end) { + /* Delibrate fallthrough cases here :-) */ + switch (*end) { + case 'T': + *val *= 1024; + case 'G': + *val *= 1024; + case 'M': + *val *= 1024; + case 'k': + *val *= 1024; + break; + default: + return -1; + } + end++; + if (*end) + return -1; + } + return 0; +} + +static int +cmdVolCreateAs(vshControl * ctl, vshCmd * cmd) +{ + virStoragePoolPtr pool; + virStorageVolPtr vol; + int found; + char *name, *capacityStr, *allocationStr, *format; + unsigned long long capacity, allocation = 0; + virBuffer buf; + + memset(&buf, 0, sizeof(buf)); + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, + VSH_BYNAME))) + return FALSE; + + name = vshCommandOptString(cmd, "name", &found); + if (!found) + goto cleanup; + + capacityStr = vshCommandOptString(cmd, "capacity", &found); + if (!found) + goto cleanup; + if (cmdVolSize(capacityStr, &capacity) < 0) + vshError(ctl, FALSE, _("Malformed size %s"), capacityStr); + + allocationStr = vshCommandOptString(cmd, "allocation", &found); + if (allocationStr && + cmdVolSize(allocationStr, &allocation) < 0) + vshError(ctl, FALSE, _("Malformed size %s"), allocationStr); + + format = vshCommandOptString(cmd, "format", &found); + + if (virBufferAddLit(&buf, "<volume>\n") < 0) + goto cleanup; + if (virBufferVSprintf(&buf, " <name>%s</name>\n", name) < 0) + goto cleanup; + if (virBufferVSprintf(&buf, " <capacity>%llu</capacity>\n", capacity) < 0) + goto cleanup; + if (allocationStr && + virBufferVSprintf(&buf, " <allocation>%llu</allocation>\n", allocation) < 0) + goto cleanup; + + if (format) { + if (virBufferAddLit(&buf, " <target>\n") < 0) + goto cleanup; + if (format) + if (virBufferVSprintf(&buf, " <format type='%s'/>\n",format) < 0) + goto cleanup; + if (virBufferAddLit(&buf, " </target>\n") < 0) + goto cleanup; + } + if (virBufferAddLit(&buf, "</volume>\n") < 0) + goto cleanup; + + vol = virStorageVolCreateXML(pool, buf.content, 0); + free (buf.content); + virStoragePoolFree(pool); + + if (vol != NULL) { + vshPrint(ctl, _("Vol %s created\n"), name); + virStorageVolFree(vol); + return TRUE; + } else { + vshError(ctl, FALSE, _("Failed to create vol %s"), name); + return FALSE; + } + + cleanup: + free(buf.content); + virStoragePoolFree(pool); + return FALSE; } @@ -5160,7 +5478,9 @@ static vshCmdDef commands[] = { {"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}, + {"pool-create-as", cmdPoolCreateAs, opts_pool_create_as, info_pool_create_as}, {"pool-define", cmdPoolDefine, opts_pool_define, info_pool_define}, + {"pool-define-as", cmdPoolDefineAs, opts_pool_define_as, info_pool_define_as}, {"pool-destroy", cmdPoolDestroy, opts_pool_destroy, info_pool_destroy}, {"pool-delete", cmdPoolDelete, opts_pool_delete, info_pool_delete}, {"pool-discover", cmdPoolDiscover, opts_pool_discover, info_pool_discover}, @@ -5190,6 +5510,7 @@ static vshCmdDef commands[] = { {"uri", cmdURI, NULL, info_uri}, {"vol-create", cmdVolCreate, opts_vol_create, info_vol_create}, + {"vol-create-as", cmdVolCreateAs, opts_vol_create_as, info_vol_create_as}, {"vol-delete", cmdVolDelete, opts_vol_delete, info_vol_delete}, {"vol-dumpxml", cmdVolDumpXML, opts_vol_dumpxml, info_vol_dumpxml}, {"vol-info", cmdVolInfo, opts_vol_info, info_vol_info}, @@ -5429,6 +5750,32 @@ vshCommandOptString(vshCmd * cmd, const return arg && arg->data && *arg->data ? arg->data : NULL; } + +#if 0 +static int +vshCommandOptStringList(vshCmd * cmd, const char *name, char ***data) +{ + vshCmdOpt *arg = cmd->opts; + char **val = NULL; + int nval = 0; + + while (arg) { + if (arg->def && STREQ(arg->def->name, name)) { + char **tmp = realloc(val, sizeof(*tmp) * (nval+1)); + if (!tmp) { + free(val); + return -1; + } + val = tmp; + val[nval++] = arg->data; + } + arg = arg->next; + } + + *data = val; + return nval; +} +#endif /* * Returns TRUE/FALSE if the option exists diff -r 09ce185344bd src/xend_internal.c --- a/src/xend_internal.c Thu Feb 07 12:33:30 2008 -0500 +++ b/src/xend_internal.c Thu Feb 07 12:33:33 2008 -0500 @@ -1440,7 +1440,7 @@ xend_parse_sexp_desc(virConnectPtr conn, virBufferVSprintf(&buf, " <bootloader>%s</bootloader>\n", tmp); } else if (sexpr_has(root, "domain/bootloader")) { bootloader = 1; - virBufferVSprintf(&buf, " <bootloader/>\n"); + virBufferAddLit(&buf, " <bootloader/>\n"); } tmp = sexpr_node(root, "domain/bootloader_args"); if (tmp != NULL && bootloader) { @@ -1464,12 +1464,12 @@ xend_parse_sexp_desc(virConnectPtr conn, max_mem = cur_mem; virBufferVSprintf(&buf, " <memory>%d</memory>\n", max_mem); if ((cur_mem >= MIN_XEN_GUEST_SIZE) && (cur_mem != max_mem)) - virBufferVSprintf(&buf, " <currentMemory>%d</currentMemory>\n", - cur_mem); - - virBufferVSprintf(&buf, " <vcpu"); + virBufferVSprintf(&buf, " <currentMemory>%d</currentMemory>\n", + cur_mem); + + virBufferAddLit(&buf, " <vcpu"); if (cpus != NULL) { - virBufferVSprintf(&buf, " cpuset='%s'", cpus); + virBufferVSprintf(&buf, " cpuset='%s'", cpus); } virBufferVSprintf(&buf, ">%d</vcpu>\n", sexpr_int(root, "domain/vcpus")); @@ -1646,7 +1646,7 @@ xend_parse_sexp_desc(virConnectPtr conn, } } else { /* This case is the cdrom device only */ - virBufferVSprintf(&buf, " <disk device='cdrom'>\n"); + virBufferAddLit(&buf, " <disk device='cdrom'>\n"); } virBufferVSprintf(&buf, " <target dev='%s'/>\n", dst); @@ -1654,9 +1654,9 @@ xend_parse_sexp_desc(virConnectPtr conn, /* XXX should we force mode == r, if cdrom==1, or assume xend has already done this ? */ if ((mode != NULL) && (!strcmp(mode, "r"))) - virBufferVSprintf(&buf, " <readonly/>\n"); + virBufferAddLit(&buf, " <readonly/>\n"); else if ((mode != NULL) && (!strcmp(mode, "w!"))) - virBufferVSprintf(&buf, " <shareable/>\n"); + virBufferAddLit(&buf, " <shareable/>\n"); virBufferAddLit(&buf, " </disk>\n"); bad_parse: @@ -1667,12 +1667,12 @@ xend_parse_sexp_desc(virConnectPtr conn, tmp2 = sexpr_node(node, "device/vif/script"); tmp = sexpr_node(node, "device/vif/bridge"); if ((tmp2 && strstr(tmp2, "bridge")) || tmp) { - virBufferVSprintf(&buf, " <interface type='bridge'>\n"); + virBufferAddLit(&buf, " <interface type='bridge'>\n"); if (tmp != NULL) virBufferVSprintf(&buf, " <source bridge='%s'/>\n", tmp); } else { - virBufferVSprintf(&buf, " <interface type='ethernet'>\n"); + virBufferAddLit(&buf, " <interface type='ethernet'>\n"); } tmp = sexpr_node(node, "device/vif/vifname"); diff -r 09ce185344bd src/xm_internal.c --- a/src/xm_internal.c Thu Feb 07 12:33:30 2008 -0500 +++ b/src/xm_internal.c Thu Feb 07 12:33:33 2008 -0500 @@ -667,7 +667,7 @@ char *xenXMDomainFormatXML(virConnectPtr val = MIN_XEN_GUEST_SIZE * 2; virBufferVSprintf(buf, " <memory>%ld</memory>\n", val * 1024); - virBufferVSprintf(buf, " <vcpu"); + virBufferAddLit(buf, " <vcpu"); if (xenXMConfigGetString(conf, "cpus", &str) == 0) { char *ranges; diff -r 09ce185344bd src/xml.c --- a/src/xml.c Thu Feb 07 12:33:30 2008 -0500 +++ b/src/xml.c Thu Feb 07 12:33:33 2008 -0500 @@ -1298,11 +1298,11 @@ virDomainParseXMLDiskDesc(virConnectPtr } } if (ro == 1) - virBufferVSprintf(buf, "(mode 'r')"); + virBufferAddLit(buf, "(mode 'r')"); else if (shareable == 1) - virBufferVSprintf(buf, "(mode 'w!')"); + virBufferAddLit(buf, "(mode 'w!')"); else - virBufferVSprintf(buf, "(mode 'w')"); + virBufferAddLit(buf, "(mode 'w')"); virBufferAddLit(buf, ")"); virBufferAddLit(buf, ")"); @@ -1617,7 +1617,7 @@ virDomainParseXMLDesc(virConnectPtr conn free(str); } else if (virXPathNumber("count(/domain/bootloader)", ctxt, &f) == 0 && (f > 0)) { - virBufferVSprintf(&buf, "(bootloader)"); + virBufferAddLit(&buf, "(bootloader)"); /* * if using a bootloader, the kernel and initrd strings are not * significant and should be discarded -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:35:19AM +0000, Daniel P. Berrange wrote:
This patch adds 3 extra commands to virsh to allow storage pools and volumes to be defined & created without using XML. These have the names 'pool-define-as', 'pool-create-as', 'vol-create-as'. virsh will take the args to these commands and build suitable XML to pass into the libvirt APIs. This is a useful capability for people quickly shell scripting the virsh command, since they don't need to build up XML in the shell directly. In doing this we need to expose a couple of virBuffer* APIs to virsh. Turning these calls into macros exposed a couple of places where the existing code calls the virBufferVSprintf() function without actually having any va_args. These were turned int virBufferAdd calls instead.
buf.c | 11 + buf.h | 12 +
Hum, for virsh own use, i was tempted to suggest using an xmlBuffer instead of virBuffer, since xmlBuffer functions are exported from libxml2. But there is no xmlBufferVSprintf() or equivalent, just the basic Add() , i'm a bit annoyed by exporting more of the internal functions but I guess code coherency primes, so that's fine. okay, +1 Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

This patch provides the general purpose code for the storage driver. The storage_driver.{c,h} file implements the libvirt internal storage driver API. The storage_conf.{c,h} file takes care of parsing and formatting the XML for pools and volumes. This should be reusable by the test driver's impl of the storage APIs in the future. The final storage_backend.{c,h} file provides a 2nd level internal driver API. This allows the main storage driver to call into storage pool specific impls for iSCSI, disk, filesystem, LVM and more. The storage_backend.h definitions allow the specific drivers to declare particular parts of the generic pool XML which are mandatory. For example, a disk pool always requires a source device element. This patch adds AC_SYS_LARGEFILE to turn on large file support inside libvirt globally, allowing us to manage files > 2GB. b/src/storage_backend.c | 80 +++ b/src/storage_backend.h | 121 ++++ b/src/storage_conf.c | 1138 ++++++++++++++++++++++++++++++++++++++++++ b/src/storage_conf.h | 313 +++++++++++ b/src/storage_driver.c | 1169 ++++++++++++++++++++++++++++++++++++++++++++ b/src/storage_driver.h | 45 + configure.in | 2 include/libvirt/virterror.h | 2 src/Makefile.am | 3 src/libvirt.c | 2 src/virterror.c | 12 11 files changed, 2887 insertions(+) diff -r 77cf7f42edd4 configure.in --- a/configure.in Thu Feb 07 12:33:33 2008 -0500 +++ b/configure.in Thu Feb 07 12:59:40 2008 -0500 @@ -689,6 +689,8 @@ AC_SUBST(CYGWIN_EXTRA_PYTHON_LIBADD) AC_SUBST(CYGWIN_EXTRA_PYTHON_LIBADD) AC_SUBST(MINGW_EXTRA_LDFLAGS) +AC_SYS_LARGEFILE + # very annoying rm -f COPYING cp COPYING.LIB COPYING diff -r 77cf7f42edd4 include/libvirt/virterror.h --- a/include/libvirt/virterror.h Thu Feb 07 12:33:33 2008 -0500 +++ b/include/libvirt/virterror.h Thu Feb 07 12:59:40 2008 -0500 @@ -136,6 +136,8 @@ typedef enum { VIR_ERR_INVALID_STORAGE_POOL, /* invalid storage pool object */ VIR_ERR_INVALID_STORAGE_VOL, /* invalid storage vol object */ 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 */ } virErrorNumber; /** diff -r 77cf7f42edd4 src/Makefile.am --- a/src/Makefile.am Thu Feb 07 12:33:33 2008 -0500 +++ b/src/Makefile.am Thu Feb 07 12:59:40 2008 -0500 @@ -59,6 +59,9 @@ CLIENT_SOURCES = \ openvz_conf.c openvz_conf.h \ openvz_driver.c openvz_driver.h \ nodeinfo.h nodeinfo.c \ + storage_conf.h storage_conf.c \ + storage_driver.h storage_driver.c \ + storage_backend.h storage_backend.c \ util.c util.h SERVER_SOURCES = \ diff -r 77cf7f42edd4 src/libvirt.c --- a/src/libvirt.c Thu Feb 07 12:33:33 2008 -0500 +++ b/src/libvirt.c Thu Feb 07 12:59:40 2008 -0500 @@ -38,6 +38,7 @@ #include "xen_unified.h" #include "remote_internal.h" #include "qemu_driver.h" +#include "storage_driver.h" #ifdef WITH_OPENVZ #include "openvz_driver.h" #endif @@ -216,6 +217,7 @@ virInitialize(void) #ifdef WITH_OPENVZ if (openvzRegister() == -1) return -1; #endif + if (storageRegister() == -1) return -1; #ifdef WITH_REMOTE if (remoteRegister () == -1) return -1; #endif diff -r 77cf7f42edd4 src/storage_backend.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend.c Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,80 @@ +/* + * storage_backend.h: internal storage driver backend contract + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <string.h> +#include <regex.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "util.h" + +#include "storage_backend.h" + + +virStorageBackendPtr virStorageBackendForType(int type) { + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "missing backend for pool type %d", type); + return NULL; +} + +virStorageBackendPoolOptionsPtr virStorageBackendPoolOptionsForType(int type) { + virStorageBackendPtr backend = virStorageBackendForType(type); + if (backend == NULL) + return NULL; + return &backend->poolOptions; +} + +virStorageBackendVolOptionsPtr virStorageBackendVolOptionsForType(int type) { + virStorageBackendPtr backend = virStorageBackendForType(type); + if (backend == NULL) + return NULL; + return &backend->volOptions; +} + + +int virStorageBackendFromString(const char *type) { + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %s", type); + return -1; +} + +const char *virStorageBackendToString(int type) { + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %d", type); + return NULL; +} + + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/storage_backend.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend.h Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,121 @@ +/* + * storage_backend.h: internal storage driver backend contract + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_BACKEND_H__ +#define __VIR_STORAGE_BACKEND_H__ + +#include <libvirt/libvirt.h> +#include "storage_conf.h" + + +typedef const char *(*virStorageVolFormatToString)(virConnectPtr conn, + int format); +typedef int (*virStorageVolFormatFromString)(virConnectPtr conn, + const char *format); + +typedef const char *(*virStoragePoolFormatToString)(virConnectPtr conn, + int format); +typedef int (*virStoragePoolFormatFromString)(virConnectPtr conn, + const char *format); + + +typedef struct _virStorageBackendVolOptions virStorageBackendVolOptions; +typedef virStorageBackendVolOptions *virStorageBackendVolOptionsPtr; +struct _virStorageBackendVolOptions { + virStorageVolFormatToString formatToString; + virStorageVolFormatFromString formatFromString; +}; + + +/* Flags to indicate mandatory components in the pool source */ +enum { + VIR_STORAGE_BACKEND_POOL_SOURCE_HOST = (1<<0), + VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE = (1<<1), + VIR_STORAGE_BACKEND_POOL_SOURCE_DIR = (1<<3), + VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER = (1<<4), +}; + +typedef struct _virStorageBackendPoolOptions virStorageBackendPoolOptions; +typedef virStorageBackendPoolOptions *virStorageBackendPoolOptionsPtr; +struct _virStorageBackendPoolOptions { + int flags; + virStoragePoolFormatToString formatToString; + virStoragePoolFormatFromString formatFromString; +}; + +typedef int (*virStorageBackendStartPool)(virConnectPtr conn, virStoragePoolObjPtr pool); +typedef int (*virStorageBackendBuildPool)(virConnectPtr conn, virStoragePoolObjPtr pool, unsigned int flags); +typedef int (*virStorageBackendRefreshPool)(virConnectPtr conn, virStoragePoolObjPtr pool); +typedef int (*virStorageBackendStopPool)(virConnectPtr conn, virStoragePoolObjPtr pool); +typedef int (*virStorageBackendDeletePool)(virConnectPtr conn, virStoragePoolObjPtr pool, unsigned int flags); + +typedef int (*virStorageBackendCreateVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol); +typedef int (*virStorageBackendRefreshVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol); +typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags); + + +typedef struct _virStorageBackend virStorageBackend; +typedef virStorageBackend *virStorageBackendPtr; + +struct _virStorageBackend { + int type; + + virStorageBackendStartPool startPool; + virStorageBackendBuildPool buildPool; + virStorageBackendRefreshPool refreshPool; + virStorageBackendStopPool stopPool; + virStorageBackendDeletePool deletePool; + + virStorageBackendCreateVol createVol; + virStorageBackendRefreshVol refreshVol; + virStorageBackendDeleteVol deleteVol; + + virStorageBackendPoolOptions poolOptions; + virStorageBackendVolOptions volOptions; + + int volType; +}; + + +virStorageBackendPtr virStorageBackendForType(int type); +virStorageBackendPoolOptionsPtr virStorageBackendPoolOptionsForType(int type); +virStorageBackendVolOptionsPtr virStorageBackendVolOptionsForType(int type); +int virStorageBackendFromString(const char *type); +const char *virStorageBackendToString(int type); + + +#endif /* __VIR_STORAGE_BACKEND_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/storage_conf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_conf.c Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,1138 @@ +/* + * storage_conf.c: config handling for storage driver + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <libvirt/libvirt.h> +#include <libvirt/virterror.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/uri.h> + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> + +#include "storage_conf.h" +#include "storage_backend.h" +#include "xml.h" +#include "uuid.h" +#include "buf.h" +#include "util.h" + +#define virStorageLog(msg...) fprintf(stderr, msg) + +void +virStorageReportError(virConnectPtr conn, int code, const char *fmt, ...) { + va_list args; + char errorMessage[1024]; + + if (fmt) { + va_start(args, fmt); + vsnprintf(errorMessage, sizeof(errorMessage)-1, fmt, args); + va_end(args); + } else { + errorMessage[0] = '\0'; + } + virStorageLog("%s", errorMessage); + __virRaiseError(conn, NULL, NULL, VIR_FROM_STORAGE, code, VIR_ERR_ERROR, + NULL, NULL, NULL, -1, -1, "%s", errorMessage); +} + + + +void virStorageVolDefFree(virStorageVolDefPtr def) { + int i; + free(def->name); + free(def->key); + + for (i = 0 ; i < def->source.nextent ; i++) { + free(def->source.extents[i].path); + } + free(def->source.extents); + + free(def->target.path); + free(def->target.perms.label); + free(def); +} + +void virStoragePoolDefFree(virStoragePoolDefPtr def) { + int i; + + free(def->name); + free(def->source.host.name); + for (i = 0 ; i < def->source.ndevice ; i++) { + free(def->source.devices[i].freeExtents); + free(def->source.devices[i].path); + } + free(def->source.devices); + free(def->source.dir); + + if (def->source.authType == VIR_STORAGE_POOL_AUTH_CHAP) { + free(def->source.auth.chap.login); + free(def->source.auth.chap.passwd); + } + + free(def->target.path); + free(def->target.perms.label); + free(def); +} + + +void virStoragePoolObjFree(virStoragePoolObjPtr obj) { + if (obj->def) + virStoragePoolDefFree(obj->def); + if (obj->newDef) + virStoragePoolDefFree(obj->newDef); + + free(obj->configFile); + free(obj->autostartLink); + free(obj); +} + +void virStoragePoolObjRemove(virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool) +{ + virStoragePoolObjPtr prev = NULL, curr; + + curr = driver->pools; + while (curr != pool) { + prev = curr; + curr = curr->next; + } + + if (curr) { + if (prev) + prev->next = curr->next; + else + driver->pools = curr->next; + + driver->ninactivePools--; + } + + virStoragePoolObjFree(pool); +} + + +static int virStoragePoolDefParseAuthChap(virConnectPtr conn, xmlXPathContextPtr ctxt, virStoragePoolAuthChapPtr auth) { + auth->login = virXPathString("string(/pool/source/auth/@login)", ctxt); + if (auth->login == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing auth host attribute"); + return -1; + } + + auth->passwd = virXPathString("string(/pool/source/auth/@passwd)", ctxt); + if (auth->passwd == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing auth passwd attribute"); + return -1; + } + + return 0; +} + + +static int virStoragePoolDefParsePerms(virConnectPtr conn, xmlXPathContextPtr ctxt, virStoragePermsPtr perms) { + char *mode; + long v; + + mode = virXPathString("string(/pool/permissions/mode)", ctxt); + if (!mode) { + perms->mode = 0700; + } else { + char *end; + perms->mode = strtol(mode, &end, 8); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed octal mode"); + return -1; + } + } + + if (virXPathNode("/pool/permissions/owner", ctxt) == NULL) { + perms->uid = getuid(); + } else { + if (virXPathLong("number(/pool/permissions/owner)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed owner element"); + return -1; + } + perms->uid = (int)v; + } + + if (virXPathNode("/pool/permissions/group", ctxt) == NULL) { + perms->uid = getgid(); + } else { + if (virXPathLong("number(/pool/permissions/group)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed group element"); + return -1; + } + perms->gid = (int)v; + } + + /* NB, we're ignoring missing labels here - they'll simply inherit */ + perms->label = virXPathString("string(/pool/permissions/label)", ctxt); + + return 0; +} + + +static virStoragePoolDefPtr virStoragePoolDefParseDoc(virConnectPtr conn, + xmlXPathContextPtr ctxt, xmlNodePtr root) { + virStorageBackendPoolOptionsPtr options; + virStoragePoolDefPtr ret; + xmlChar *type = NULL; + char *uuid = NULL; + char *authType = NULL; + + if ((ret = calloc(1, sizeof(virStoragePoolDef))) == NULL) + return NULL; + + if (STRNEQ((const char *)root->name, "pool")) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "unknown root element"); + goto cleanup; + } + + type = xmlGetProp(root, BAD_CAST "type"); + if ((ret->type = virStorageBackendFromString((const char *)type)) < 0) + goto cleanup; + xmlFree(type); + type = NULL; + + if ((options = virStorageBackendPoolOptionsForType(ret->type)) == NULL) { + goto cleanup; + } + + if ((ret->name = virXPathString("string(/pool/name)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing name element"); + goto cleanup; + } + + uuid = virXPathString("string(/pool/uuid)", ctxt); + if (uuid == NULL) { + if (virUUIDGenerate(ret->uuid) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unable to generate uuid"); + goto cleanup; + } + } else { + if (virUUIDParse(uuid, ret->uuid) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed uuid element"); + goto cleanup; + } + free(uuid); + uuid = NULL; + } + + if (options->formatFromString) { + char *format = virXPathString("string(/pool/source/format/@type)", ctxt); + if ((ret->source.format = (options->formatFromString)(conn, format)) < 0) { + free(format); + goto cleanup; + } + free(format); + } + + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_HOST) { + if ((ret->source.host.name = virXPathString("string(/pool/source/host/@name)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source host name"); + goto cleanup; + } + } + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) { + xmlNodePtr *nodeset = NULL; + int nsource, i; + + if ((nsource = virXPathNodeSet("/pool/source/device", ctxt, &nodeset)) <= 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "cannot extract source devices"); + goto cleanup; + } + if ((ret->source.devices = calloc(nsource, sizeof(*ret->source.devices))) == NULL) { + free(nodeset); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "device"); + goto cleanup; + } + for (i = 0 ; i < nsource ; i++) { + xmlChar *path = xmlGetProp(nodeset[i], BAD_CAST "path"); + if (path == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source device path"); + goto cleanup; + } + ret->source.devices[i].path = (char *)path; + } + free(nodeset); + ret->source.ndevice = nsource; + } + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DIR) { + if ((ret->source.dir = virXPathString("string(/pool/source/dir/@path)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source path"); + goto cleanup; + } + } + + + authType = virXPathString("string(/pool/source/auth/@type)", ctxt); + if (authType == NULL) { + ret->source.authType = VIR_STORAGE_POOL_AUTH_NONE; + } else { + if (STREQ(authType, "chap")) { + ret->source.authType = VIR_STORAGE_POOL_AUTH_CHAP; + } else { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "unknown auth type '%s'", (const char *)authType); + goto cleanup; + } + free(authType); + authType = NULL; + } + + if (ret->source.authType == VIR_STORAGE_POOL_AUTH_CHAP) { + if (virStoragePoolDefParseAuthChap(conn, ctxt, &ret->source.auth.chap) < 0) + goto cleanup; + } + + if ((ret->target.path = virXPathString("string(/pool/target/path)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing target path"); + goto cleanup; + } + + if (virStoragePoolDefParsePerms(conn, ctxt, &ret->target.perms) < 0) + goto cleanup; + + return ret; + + cleanup: + free(uuid); + if (type) + xmlFree(type); + if (authType) + xmlFree(authType); + virStoragePoolDefFree(ret); + return NULL; +} + +virStoragePoolDefPtr virStoragePoolDefParse(virConnectPtr conn, + const char *xmlStr, const char *filename) { + virStoragePoolDefPtr ret = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr ctxt = NULL; + + if (!(xml = xmlReadDoc(BAD_CAST xmlStr, filename ? filename : "storage.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed xml document"); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "xmlXPathContext"); + goto cleanup; + } + + node = xmlDocGetRootElement(xml); + if (node == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing root element"); + goto cleanup; + } + + ret = virStoragePoolDefParseDoc(conn, ctxt, node); + + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + + return ret; + + cleanup: + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + return NULL; +} + + +char *virStoragePoolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr def) { + virStorageBackendPoolOptionsPtr options; + virBufferPtr buf; + const char *type; + char uuid[VIR_UUID_STRING_BUFLEN]; + int i; + + options = virStorageBackendPoolOptionsForType(def->type); + if (options == NULL) + return NULL; + + if ((buf = virBufferNew(8192)) == NULL) + goto no_memory; + + type = virStorageBackendToString(def->type); + if (!type) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unexpected pool type"); + goto cleanup; + } + if (virBufferVSprintf(buf, "<pool type='%s'>\n", type) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <name>%s</name>\n", def->name) < 0) + goto no_memory; + + virUUIDFormat(def->uuid, uuid); + if (virBufferVSprintf(buf," <uuid>%s</uuid>\n", uuid) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <capacity>%llu</capacity>\n", def->capacity) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <allocation>%llu</allocation>\n", def->allocation) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <available>%llu</available>\n", def->available) < 0) + goto no_memory; + + + if (virBufferAddLit(buf," <source>\n") < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_HOST) && + def->source.host.name && + virBufferVSprintf(buf," <host name='%s'/>\n", def->source.host.name) < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) && + def->source.ndevice) { + for (i = 0 ; i < def->source.ndevice ; i++) { + if (virBufferVSprintf(buf," <device path='%s'>\n", def->source.devices[i].path) < 0) + goto no_memory; + if (def->source.devices[i].nfreeExtent) { + int j; + for (j = 0 ; j < def->source.devices[i].nfreeExtent ; j++) { + if (virBufferVSprintf(buf, " <freeExtent start='%llu' end='%llu'/>\n", + def->source.devices[i].freeExtents[j].start, + def->source.devices[i].freeExtents[j].end) < 0) + goto no_memory; + } + } + if (virBufferAddLit(buf," </device>\n") < 0) + goto no_memory; + } + } + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DIR) && + def->source.dir && + virBufferVSprintf(buf," <directory path='%s'/>\n", def->source.dir) < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER) && + def->source.adapter && + virBufferVSprintf(buf," <adapter name='%s'/>\n", def->source.adapter) < 0) + goto no_memory; + + if (options->formatToString) { + const char *format = (options->formatToString)(conn, def->source.format); + if (!format) + goto cleanup; + if (virBufferVSprintf(buf," <format type='%s'/>\n", format) < 0) + goto no_memory; + } + + + if (def->source.authType == VIR_STORAGE_POOL_AUTH_CHAP && + virBufferVSprintf(buf," <auth type='chap' login='%s' passwd='%s'>\n", + def->source.auth.chap.login, + def->source.auth.chap.passwd) < 0) + goto no_memory; + if (virBufferAddLit(buf," </source>\n") < 0) + goto no_memory; + + + + if (virBufferAddLit(buf," <target>\n") < 0) + goto no_memory; + + if (def->target.path && + virBufferVSprintf(buf," <path>%s</path>\n", def->target.path) < 0) + goto no_memory; + + if (virBufferAddLit(buf," <permissions>\n") < 0) + goto no_memory; + if (virBufferVSprintf(buf," <mode>0%o</mode>\n", def->target.perms.mode) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <owner>%d</owner>\n", def->target.perms.uid) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <group>%d</group>\n", def->target.perms.gid) < 0) + goto no_memory; + + if (def->target.perms.label) { + if (virBufferVSprintf(buf," <label>%s</label>\n", def->target.perms.label) < 0) + goto no_memory; + } + if (virBufferAddLit(buf," </permissions>\n") < 0) + goto no_memory; + if (virBufferAddLit(buf," </target>\n") < 0) + goto no_memory; + + if (virBufferAddLit(buf,"</pool>\n") < 0) + goto no_memory; + + return virBufferContentAndFree(buf); + + no_memory: + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "xml"); + cleanup: + if (buf) virBufferFree(buf); + return NULL; +} + + +static int virStorageVolDefParsePerms(virConnectPtr conn, xmlXPathContextPtr ctxt, virStoragePermsPtr perms) { + char *mode; + long v; + + mode = virXPathString("string(/volume/permissions/mode)", ctxt); + if (!mode) { + perms->mode = 0600; + } else { + char *end; + perms->mode = strtol(mode, &end, 8); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed octal mode"); + return -1; + } + } + + if (virXPathNode("/volume/permissions/owner", ctxt) == NULL) { + perms->uid = getuid(); + } else { + if (virXPathLong("number(/volume/permissions/owner)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing owner element"); + return -1; + } + perms->uid = (int)v; + } + if (virXPathNode("/volume/permissions/group", ctxt) == NULL) { + perms->gid = getgid(); + } else { + if (virXPathLong("number(/volume/permissions/group)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing owner element"); + return -1; + } + perms->gid = (int)v; + } + + /* NB, we're ignoring missing labels here - they'll simply inherit */ + perms->label = virXPathString("string(/volume/permissions/label)", ctxt); + + return 0; +} + + +static int virStorageSize(virConnectPtr conn, + const char *unit, + const char *val, + unsigned long long *ret) { + unsigned long long mult; + char *end; + + if (!unit) { + mult = 1; + } else { + switch (unit[0]) { + case 'k': + case 'K': + mult = 1024ull; + break; + + case 'm': + case 'M': + mult = 1024ull * 1024ull; + break; + + case 'g': + case 'G': + mult = 1024ull * 1024ull * 1024ull; + break; + + case 't': + case 'T': + mult = 1024ull * 1024ull * 1024ull * 1024ull; + break; + + default: + virStorageReportError(conn, VIR_ERR_XML_ERROR, "Unknown size units %s", unit); + return -1; + } + } + + *ret = strtoull(val, &end, 10); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed capacity element"); + return -1; + } + *ret *= mult; + + return 0; +} + +static virStorageVolDefPtr virStorageVolDefParseDoc(virConnectPtr conn, + virStoragePoolDefPtr pool, + xmlXPathContextPtr ctxt, xmlNodePtr root) { + virStorageVolDefPtr ret; + virStorageBackendVolOptionsPtr options; + char *allocation = NULL; + char *capacity = NULL; + char *unit = NULL; + + options = virStorageBackendVolOptionsForType(pool->type); + if (options == NULL) + return NULL; + + if ((ret = calloc(1, sizeof(virStorageVolDef))) == NULL) + return NULL; + + if (STRNEQ((const char *)root->name, "volume")) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "unknown root element"); + goto cleanup; + } + + ret->name = virXPathString("string(/volume/name)", ctxt); + if (ret->name == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing name element"); + goto cleanup; + } + + /* Auto-generated so delibrately ignore */ + /*ret->key = virXPathString("string(/volume/key)", ctxt);*/ + + capacity = virXPathString("string(/volume/capacity)", ctxt); + unit = virXPathString("string(/volume/capacity/@unit)", ctxt); + if (capacity == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing capacity element"); + goto cleanup; + } + if (virStorageSize(conn, unit, capacity, &ret->capacity) < 0) + goto cleanup; + free(capacity); + capacity = NULL; + free(unit); + unit = NULL; + + allocation = virXPathString("string(/volume/allocation)", ctxt); + if (allocation) { + unit = virXPathString("string(/volume/allocation/@unit)", ctxt); + if (virStorageSize(conn, unit, allocation, &ret->allocation) < 0) + goto cleanup; + free(allocation); + allocation = NULL; + free(unit); + unit = NULL; + } else { + ret->allocation = ret->capacity; + } + + ret->target.path = virXPathString("string(/volume/target/path)", ctxt); + if (options->formatFromString) { + char *format = virXPathString("string(/volume/target/format/@type)", ctxt); + if ((ret->target.format = (options->formatFromString)(conn, format)) < 0) { + free(format); + goto cleanup; + } + free(format); + } + + if (virStorageVolDefParsePerms(conn, ctxt, &ret->target.perms) < 0) + goto cleanup; + + return ret; + + cleanup: + free(allocation); + free(capacity); + free(unit); + virStorageVolDefFree(ret); + return NULL; +} + + +virStorageVolDefPtr virStorageVolDefParse(virConnectPtr conn, + virStoragePoolDefPtr pool, + const char *xmlStr, const char *filename) { + virStorageVolDefPtr ret = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr ctxt = NULL; + + if (!(xml = xmlReadDoc(BAD_CAST xmlStr, filename ? filename : "storage.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed xml document"); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "xmlXPathContext"); + goto cleanup; + } + + node = xmlDocGetRootElement(xml); + if (node == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing root element"); + goto cleanup; + } + + ret = virStorageVolDefParseDoc(conn, pool, ctxt, node); + + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + + return ret; + + cleanup: + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + return NULL; +} + + + +char *virStorageVolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr pool, + virStorageVolDefPtr def) { + virStorageBackendVolOptionsPtr options; + virBufferPtr buf = virBufferNew(8192); + + options = virStorageBackendVolOptionsForType(pool->type); + if (options == NULL) + return NULL; + + if (!buf) + goto no_memory; + + if (virBufferAddLit(buf, "<volume>\n") < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <name>%s</name>\n", def->name) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <key>%s</key>\n", def->key) < 0) + goto no_memory; + + if (virBufferAddLit(buf, " <source>\n") < 0) + goto no_memory; + if (def->source.nextent) { + int i; + const char *thispath = NULL; + for (i = 0 ; i < def->source.nextent ; i++) { + if (thispath == NULL || + STRNEQ(thispath, def->source.extents[i].path)) { + if (thispath != NULL) + if (virBufferAddLit(buf, " </device>\n") < 0) + goto no_memory; + if (virBufferVSprintf(buf, " <device path='%s'>\n", def->source.extents[i].path) < 0) + goto no_memory; + } + + if (virBufferVSprintf(buf, " <extent start='%llu' end='%llu'/>\n", + def->source.extents[i].start, + def->source.extents[i].end) < 0) + goto no_memory; + thispath = def->source.extents[i].path; + } + if (thispath != NULL) + if (virBufferAddLit(buf, " </device>\n") < 0) + goto no_memory; + } + if (virBufferAddLit(buf, " </source>\n") < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <capacity>%llu</capacity>\n", def->capacity) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <allocation>%llu</allocation>\n", def->allocation) < 0) + goto no_memory; + + if (virBufferAddLit(buf, " <target>\n") < 0) + goto no_memory; + + if (def->target.path && + virBufferVSprintf(buf," <path>%s</path>\n", def->target.path) < 0) + goto no_memory; + + if (options->formatToString) { + const char *format = (options->formatToString)(conn, def->target.format); + if (!format) + goto cleanup; + if (virBufferVSprintf(buf," <format type='%s'/>\n", format) < 0) + goto no_memory; + } + + if (virBufferAddLit(buf," <permissions>\n") < 0) + goto no_memory; + if (virBufferVSprintf(buf," <mode>0%o</mode>\n", def->target.perms.mode) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <owner>%d</owner>\n", def->target.perms.uid) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <group>%d</group>\n", def->target.perms.gid) < 0) + goto no_memory; + + if (def->target.perms.label && + virBufferVSprintf(buf," <label>%s</label>\n", def->target.perms.label) < 0) + goto no_memory; + if (virBufferAddLit(buf," </permissions>\n") < 0) + goto no_memory; + + if (virBufferAddLit(buf, " </target>\n") < 0) + goto no_memory; + + if (virBufferAddLit(buf,"</volume>\n") < 0) + goto no_memory; + + return virBufferContentAndFree(buf); + + no_memory: + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "xml"); + cleanup: + if (buf) virBufferFree(buf); + return NULL; +} + + +virStoragePoolObjPtr virStoragePoolObjFindByUUID(virStorageDriverStatePtr driver, + const unsigned char *uuid) { + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (!memcmp(pool->def->uuid, uuid, VIR_UUID_BUFLEN)) + return pool; + pool = pool->next; + } + + return NULL; +} + +virStoragePoolObjPtr virStoragePoolObjFindByName(virStorageDriverStatePtr driver, + const char *name) { + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (STREQ(pool->def->name, name)) + return pool; + pool = pool->next; + } + + return NULL; +} + +void virStoragePoolObjClearVols(virStoragePoolObjPtr pool) +{ + virStorageVolDefPtr vol = pool->volumes; + while (vol) { + virStorageVolDefPtr next = vol->next; + virStorageVolDefFree(vol); + vol = next; + } + pool->volumes = NULL; + pool->nvolumes = 0; +} + + + +virStorageVolDefPtr virStorageVolDefFindByKey(virStoragePoolObjPtr pool, + const char *key) { + virStorageVolDefPtr vol = pool->volumes; + + while (vol) { + if (STREQ(vol->key, key)) + return vol; + vol = vol->next; + } + + return NULL; +} +virStorageVolDefPtr virStorageVolDefFindByPath(virStoragePoolObjPtr pool, + const char *path) { + virStorageVolDefPtr vol = pool->volumes; + + while (vol) { + if (STREQ(vol->target.path, path)) + return vol; + vol = vol->next; + } + + return NULL; +} + +virStorageVolDefPtr virStorageVolDefFindByName(virStoragePoolObjPtr pool, + const char *name) { + virStorageVolDefPtr vol = pool->volumes; + + while (vol) { + if (STREQ(vol->name, name)) + return vol; + vol = vol->next; + } + + return NULL; +} + +virStoragePoolObjPtr virStoragePoolObjAssignDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolDefPtr def) { + virStoragePoolObjPtr pool; + + if ((pool = virStoragePoolObjFindByName(driver, def->name))) { + if (!virStoragePoolObjIsActive(pool)) { + virStoragePoolDefFree(pool->def); + pool->def = def; + } else { + if (pool->newDef) + virStoragePoolDefFree(pool->newDef); + pool->newDef = def; + } + return pool; + } + + if (!(pool = calloc(1, sizeof(virStoragePoolObj)))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "pool"); + return NULL; + } + + pool->active = 0; + pool->def = def; + pool->next = driver->pools; + + driver->pools = pool; + driver->ninactivePools++; + + return pool; +} + +static virStoragePoolObjPtr +virStoragePoolObjLoad(virStorageDriverStatePtr driver, + const char *file, + const char *path, + const char *xml, + const char *autostartLink) { + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + + if (!(def = virStoragePoolDefParse(NULL, xml, file))) { + virErrorPtr err = virGetLastError(); + virStorageLog("Error parsing storage pool config '%s' : %s", + path, err->message); + return NULL; + } + + if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { + virStorageLog("Storage Pool config filename '%s' does not match pool name '%s'", + path, def->name); + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(NULL, driver, def))) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + + pool->configFile = strdup(path); + if (pool->configFile == NULL) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + pool->autostartLink = strdup(autostartLink); + if (pool->autostartLink == NULL) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + + pool->autostart = virFileLinkPointsTo(pool->autostartLink, pool->configFile); + + return pool; +} + + +int virStoragePoolObjScanConfigs(virStorageDriverStatePtr driver) { + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(driver->configDir))) { + if (errno == ENOENT) + return 0; + virStorageLog("Failed to open dir '%s': %s", + driver->configDir, strerror(errno)); + return -1; + } + + while ((entry = readdir(dir))) { + char *xml = NULL; + char path[PATH_MAX]; + char autostartLink[PATH_MAX]; + + if (entry->d_name[0] == '.') + continue; + + if (!virFileHasSuffix(entry->d_name, ".xml")) + continue; + + if (virFileBuildPath(driver->configDir, entry->d_name, NULL, path, PATH_MAX) < 0) { + virStorageLog("Config filename '%s/%s' is too long", + driver->configDir, entry->d_name); + continue; + } + + if (virFileBuildPath(driver->autostartDir, entry->d_name, NULL, autostartLink, PATH_MAX) < 0) { + virStorageLog("Autostart link path '%s/%s' is too long", + driver->autostartDir, entry->d_name); + continue; + } + + if (virFileReadAll(path, 8192, &xml) < 0) + continue; + + virStoragePoolObjLoad(driver, entry->d_name, path, xml, autostartLink); + + free(xml); + } + + closedir(dir); + + return 0; +} + +int virStoragePoolObjSaveDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool, + virStoragePoolDefPtr def) { + char *xml; + int fd = -1, ret = -1; + int towrite; + + if (!pool->configFile) { + int err; + char path[PATH_MAX]; + + if ((err = virFileMakePath(driver->configDir))) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot create config directory %s: %s", + driver->configDir, strerror(err)); + return -1; + } + + if (virFileBuildPath(driver->configDir, def->name, ".xml", + path, sizeof(path)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot construct config file path"); + return -1; + } + if (!(pool->configFile = strdup(path))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "configFile"); + return -1; + } + + if (virFileBuildPath(driver->autostartDir, def->name, ".xml", + path, sizeof(path)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot construct autostart link path"); + free(pool->configFile); + pool->configFile = NULL; + return -1; + } + if (!(pool->autostartLink = strdup(path))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "configFile"); + free(pool->configFile); + pool->configFile = NULL; + return -1; + } + } + + if (!(xml = virStoragePoolDefFormat(conn, def))) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "failed to generate XML"); + return -1; + } + + if ((fd = open(pool->configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot create config file %s: %s", + pool->configFile, strerror(errno)); + goto cleanup; + } + + towrite = strlen(xml); + if (write(fd, xml, towrite) != towrite) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot write config file %s: %s", + pool->configFile, strerror(errno)); + goto cleanup; + } + + if (close(fd) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot save config file %s: %s", + pool->configFile, strerror(errno)); + goto cleanup; + } + + ret = 0; + + cleanup: + if (fd != -1) + close(fd); + + free(xml); + + return ret; +} + +int virStoragePoolObjDeleteDef(virConnectPtr conn, virStoragePoolObjPtr pool) { + if (!pool->configFile) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "no config file for %s", pool->def->name); + return -1; + } + + if (unlink(pool->configFile) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot remove config for %s", pool->def->name); + return -1; + } + + return 0; +} + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/storage_conf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_conf.h Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,313 @@ +/* + * storage_conf.h: config handling for storage driver + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_CONF_H__ +#define __VIR_STORAGE_CONF_H__ + +#include <libvirt/libvirt.h> +#include "internal.h" + +/* Shared structs */ + + + +typedef struct _virStoragePerms virStoragePerms; +typedef virStoragePerms *virStoragePermsPtr; +struct _virStoragePerms { + int mode; + int uid; + int gid; + char *label; +}; + +/* Storage volumes */ + + +/* + * How the volume's data is stored on underlying + * physical devices - can potentially span many + * devices in LVM case. + */ +typedef struct _virStorageVolSourceExtent virStorageVolSourceExtent; +typedef virStorageVolSourceExtent *virStorageVolSourceExtentPtr; +struct _virStorageVolSourceExtent { + char *path; + unsigned long long start; + unsigned long long end; +}; + +typedef struct _virStorageVolSource virStorageVolSource; +typedef virStorageVolSource *virStorageVolSourcePtr; +struct _virStorageVolSource { + int nextent; + virStorageVolSourceExtentPtr extents; +}; + + +/* + * How the volume appears on the host + */ +typedef struct _virStorageVolTarget virStorageVolTarget; +typedef virStorageVolTarget *virStorageVolTargetPtr; +struct _virStorageVolTarget { + char *path; + int format; + virStoragePerms perms; +}; + + +typedef struct _virStorageVolDef virStorageVolDef; +typedef virStorageVolDef *virStorageVolDefPtr; +struct _virStorageVolDef { + char *name; + char *key; + + unsigned long long allocation; + unsigned long long capacity; + + virStorageVolSource source; + virStorageVolTarget target; + + virStorageVolDefPtr next; +}; + + + + +/* Storage pools */ + +enum virStoragePoolType { + VIR_STORAGE_POOL_DIR = 1, /* Local directory */ + VIR_STORAGE_POOL_FS, /* Local filesystem */ + VIR_STORAGE_POOL_NETFS, /* Networked filesystem - eg NFS, GFS, etc */ + VIR_STORAGE_POOL_LOGICAL, /* Logical volume groups / volumes */ + VIR_STORAGE_POOL_DISK, /* Disk partitions */ + VIR_STORAGE_POOL_ISCSI, /* iSCSI targets */ + VIR_STORAGE_POOL_SCSI, /* SCSI HBA */ +}; + + +enum virStoragePoolAuthType { + VIR_STORAGE_POOL_AUTH_NONE, + VIR_STORAGE_POOL_AUTH_CHAP, +}; + +typedef struct _virStoragePoolAuthChap virStoragePoolAuthChap; +typedef virStoragePoolAuthChap *virStoragePoolAuthChapPtr; +struct _virStoragePoolAuthChap { + char *login; + char *passwd; +}; + + +/* + * For remote pools, info on how to reach the host + */ +typedef struct _virStoragePoolSourceHost virStoragePoolSourceHost; +typedef virStoragePoolSourceHost *virStoragePoolSourceHostPtr; +struct _virStoragePoolSourceHost { + char *name; + int port; + int protocol; +}; + + +/* + * Available extents on the underlying storage + */ +typedef struct _virStoragePoolSourceDeviceExtent virStoragePoolSourceDeviceExtent; +typedef virStoragePoolSourceDeviceExtent *virStoragePoolSourceDeviceExtentPtr; +struct _virStoragePoolSourceDeviceExtent { + unsigned long long start; + unsigned long long end; +}; + + +/* + * Pools can be backed by one or more devices, and some + * allow us to track free space on underlying devices. + */ +typedef struct _virStoragePoolSourceDevice virStoragePoolSourceDevice; +typedef virStoragePoolSourceDevice *virStoragePoolSourceDevicePtr; +struct _virStoragePoolSourceDevice { + int nfreeExtent; + virStoragePoolSourceDeviceExtentPtr freeExtents; + char *path; + int format; /* Pool specific source format */ +}; + + + +typedef struct _virStoragePoolSource virStoragePoolSource; +typedef virStoragePoolSource *virStoragePoolSourcePtr; +struct _virStoragePoolSource { + /* An optional host */ + virStoragePoolSourceHost host; + + /* And either one or more devices ... */ + int ndevice; + virStoragePoolSourceDevicePtr devices; + + /* Or a directory */ + char *dir; + + /* Or an adapter */ + char *adapter; + + int authType; /* virStoragePoolAuthType */ + union { + virStoragePoolAuthChap chap; + } auth; + + int format; /* Pool type specific format such as filesystem type, or lvm version, etc */ +}; + + +typedef struct _virStoragePoolTarget virStoragePoolTarget; +typedef virStoragePoolTarget *virStoragePoolTargetPtr; +struct _virStoragePoolTarget { + char *path; /* Optional local filesystem mapping */ + virStoragePerms perms; /* Default permissions for volumes */ +}; + + +typedef struct _virStoragePoolDef virStoragePoolDef; +typedef virStoragePoolDef *virStoragePoolDefPtr; +struct _virStoragePoolDef { + /* General metadata */ + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; + int type; /* virStoragePoolType */ + + unsigned long long allocation; + unsigned long long capacity; + unsigned long long available; + + virStoragePoolSource source; + virStoragePoolTarget target; +}; + +typedef struct _virStoragePoolObj virStoragePoolObj; +typedef virStoragePoolObj *virStoragePoolObjPtr; + +struct _virStoragePoolObj { + char *configFile; + char *autostartLink; + int active; + int autostart; + + virStoragePoolDefPtr def; + virStoragePoolDefPtr newDef; + + int nvolumes; + virStorageVolDefPtr volumes; + + virStoragePoolObjPtr next; +}; + + + + +typedef struct _virStorageDriverState virStorageDriverState; +typedef virStorageDriverState *virStorageDriverStatePtr; + +struct _virStorageDriverState { + int nactivePools; + int ninactivePools; + virStoragePoolObjPtr pools; + char *configDir; + char *autostartDir; +}; + + +static inline int virStoragePoolObjIsActive(virStoragePoolObjPtr pool) { + return pool->active; +} + +void virStorageReportError(virConnectPtr conn, + int code, + const char *fmt, ...); + +int virStoragePoolObjScanConfigs(virStorageDriverStatePtr driver); + +virStoragePoolObjPtr virStoragePoolObjFindByUUID(virStorageDriverStatePtr driver, + const unsigned char *uuid); +virStoragePoolObjPtr virStoragePoolObjFindByName(virStorageDriverStatePtr driver, + const char *name); + +virStorageVolDefPtr virStorageVolDefFindByKey(virStoragePoolObjPtr pool, + const char *key); +virStorageVolDefPtr virStorageVolDefFindByPath(virStoragePoolObjPtr pool, + const char *path); +virStorageVolDefPtr virStorageVolDefFindByName(virStoragePoolObjPtr pool, + const char *name); + +void virStoragePoolObjClearVols(virStoragePoolObjPtr pool); + +virStoragePoolDefPtr virStoragePoolDefParse(virConnectPtr conn, + const char *xml, + const char *filename); +char *virStoragePoolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr def); + +virStorageVolDefPtr virStorageVolDefParse(virConnectPtr conn, + virStoragePoolDefPtr pool, + const char *xml, + const char *filename); +char *virStorageVolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr pool, + virStorageVolDefPtr def); + +virStoragePoolObjPtr virStoragePoolObjAssignDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolDefPtr def); + +int virStoragePoolObjSaveDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool, + virStoragePoolDefPtr def); +int virStoragePoolObjDeleteDef(virConnectPtr conn, + virStoragePoolObjPtr pool); + +void virStorageVolDefFree(virStorageVolDefPtr def); +void virStoragePoolDefFree(virStoragePoolDefPtr def); +void virStoragePoolObjFree(virStoragePoolObjPtr pool); +void virStoragePoolObjRemove(virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool); + +#endif /* __VIR_STORAGE_DRIVER_H__ */ + + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/storage_driver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_driver.c Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,1169 @@ +/* + * storage_driver.c: core driver for storage APIs + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <errno.h> +#include <string.h> + +#include "driver.h" +#include "util.h" +#include "storage_driver.h" +#include "storage_conf.h" + +#include "storage_backend.h" + +#define storageLog(msg...) fprintf(stderr, msg) + +static virStorageDriverStatePtr driverState; + +static int storageDriverShutdown(void); + + +static void storageDriverAutostart(virStorageDriverStatePtr driver) { + virStoragePoolObjPtr pool; + + pool = driver->pools; + while (pool != NULL) { + virStoragePoolObjPtr next = pool->next; + + if (pool->autostart && + !virStoragePoolObjIsActive(pool)) { + virStorageBackendPtr backend; + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + storageLog("Missing backend %d", + pool->def->type); + pool = next; + continue; + } + + if (backend->startPool && + backend->startPool(NULL, pool) < 0) { + virErrorPtr err = virGetLastError(); + storageLog("Failed to autostart storage pool '%s': %s", + pool->def->name, err ? err->message : NULL); + pool = next; + continue; + } + + if (backend->refreshPool(NULL, pool) < 0) { + virErrorPtr err = virGetLastError(); + if (backend->stopPool) + backend->stopPool(NULL, pool); + storageLog("Failed to autostart storage pool '%s': %s", + pool->def->name, err ? err->message : NULL); + pool = next; + continue; + } + pool->active = 1; + driver->nactivePools++; + driver->ninactivePools--; + } + + pool = next; + } +} + +/** + * virStorageStartup: + * + * Initialization function for the QEmu daemon + */ +static int +storageDriverStartup(void) { + uid_t uid = geteuid(); + struct passwd *pw; + char *base = NULL; + char driverConf[PATH_MAX]; + + if (!(driverState = calloc(1, sizeof(virStorageDriverState)))) { + return -1; + } + + if (!uid) { + if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) + goto out_of_memory; + } else { + if (!(pw = getpwuid(uid))) { + storageLog("Failed to find user record for uid '%d': %s", + uid, strerror(errno)); + goto out_of_memory; + } + + if (asprintf (&base, "%s/.libvirt", pw->pw_dir) == -1) { + storageLog("out of memory in asprintf"); + goto out_of_memory; + } + } + + /* Configuration paths are either ~/.libvirt/storage/... (session) or + * /etc/libvirt/storage/... (system). + */ + if (snprintf (driverConf, sizeof(driverConf), "%s/storage.conf", base) == -1) + goto out_of_memory; + driverConf[sizeof(driverConf)-1] = '\0'; + + if (asprintf (&driverState->configDir, "%s/storage", base) == -1) + goto out_of_memory; + + if (asprintf (&driverState->autostartDir, "%s/storage/autostart", base) == -1) + goto out_of_memory; + + free(base); + base = NULL; + + /* + if (virStorageLoadDriverConfig(driver, driverConf) < 0) { + virStorageDriverShutdown(); + return -1; + } + */ + + if (virStoragePoolObjScanConfigs(driverState) < 0) { + storageDriverShutdown(); + return -1; + } + storageDriverAutostart(driverState); + + return 0; + + out_of_memory: + storageLog("virStorageStartup: out of memory"); + free(base); + free(driverState); + driverState = NULL; + return -1; +} + +/** + * virStorageReload: + * + * Function to restart the storage driver, it will recheck the configuration + * files and update its state + */ +static int +storageDriverReload(void) { + virStoragePoolObjScanConfigs(driverState); + storageDriverAutostart(driverState); + + return 0; +} + +/** + * virStorageActive: + * + * Checks if the storage driver is active, i.e. has an active pool + * + * Returns 1 if active, 0 otherwise + */ +static int +storageDriverActive(void) { + /* If we've any active networks or guests, then we + * mark this driver as active + */ + if (driverState->nactivePools) + return 1; + + /* Otherwise we're happy to deal with a shutdown */ + return 0; +} + +/** + * virStorageShutdown: + * + * Shutdown the storage driver, it will stop all active storage pools + */ +static int +storageDriverShutdown(void) { + virStoragePoolObjPtr pool; + + if (!driverState) + return -1; + + /* shutdown active pools */ + pool = driverState->pools; + while (pool) { + virStoragePoolObjPtr next = pool->next; + if (virStoragePoolObjIsActive(pool)) { + virStorageBackendPtr backend; + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + storageLog("Missing backend"); + continue; + } + + if (backend->stopPool && + backend->stopPool(NULL, pool) < 0) { + virErrorPtr err = virGetLastError(); + storageLog("Failed to stop storage pool '%s': %s", + pool->def->name, err->message); + } + virStoragePoolObjClearVols(pool); + } + pool = next; + } + + /* free inactive pools */ + pool = driverState->pools; + while (pool) { + virStoragePoolObjPtr next = pool->next; + virStoragePoolObjFree(pool); + pool = next; + } + driverState->pools = NULL; + driverState->nactivePools = 0; + driverState->ninactivePools = 0; + + free(driverState->configDir); + free(driverState->autostartDir); + free(driverState); + driverState = NULL; + + return 0; +} + + + +static virStoragePoolPtr storagePoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, uuid); + virStoragePoolPtr ret; + + if (!pool) { + virStorageReportError(conn, VIR_ERR_NO_STORAGE_POOL, "no pool with matching uuid"); + return NULL; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + return ret; +} + +static virStoragePoolPtr storagePoolLookupByName(virConnectPtr conn, + const char *name) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, name); + virStoragePoolPtr ret; + + if (!pool) { + virStorageReportError(conn, VIR_ERR_NO_STORAGE_POOL, "no pool with matching name"); + return NULL; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + return ret; +} + +static virStoragePoolPtr storagePoolLookupByVolume(virStorageVolPtr vol) { + return storagePoolLookupByName(vol->conn, vol->pool); +} + +static virDrvOpenStatus storageOpen(virConnectPtr conn, + xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) { + if (!driverState) + return VIR_DRV_OPEN_DECLINED; + + conn->storagePrivateData = driverState; + return VIR_DRV_OPEN_SUCCESS; +} + +static int storageClose(virConnectPtr conn) { + conn->storagePrivateData = NULL; + return 0; +} + +static int storageNumPools(virConnectPtr conn) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + return driver->nactivePools; +} + +static int storageListPools(virConnectPtr conn, char **const names, int nnames) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + int got = 0, i; + while (pool && got < nnames) { + if (virStoragePoolObjIsActive(pool)) { + if (!(names[got] = strdup(pool->def->name))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "names"); + goto cleanup; + } + got++; + } + pool = pool->next; + } + return got; + + cleanup: + for (i = 0 ; i < got ; i++) { + free(names[i]); + names[i] = NULL; + } + return -1; +} + +static int storageNumDefinedPools(virConnectPtr conn) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + return driver->ninactivePools; +} + +static int storageListDefinedPools(virConnectPtr conn, char **const names, int nnames) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + int got = 0, i; + while (pool && got < nnames) { + if (!virStoragePoolObjIsActive(pool)) { + if (!(names[got] = strdup(pool->def->name))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "names"); + goto cleanup; + } + got++; + } + pool = pool->next; + } + return got; + + cleanup: + for (i = 0 ; i < got ; i++) { + free(names[i]); + names[i] = NULL; + } + return -1; +} + +static int storageDiscoverPools(virConnectPtr conn ATTRIBUTE_UNUSED, + const char *hostname ATTRIBUTE_UNUSED, + const char *type ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED, + char ***xmlDesc ATTRIBUTE_UNUSED) { + return -1; +} + +static virStoragePoolPtr storagePoolCreate(virConnectPtr conn, const char *xml) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr )conn->storagePrivateData; + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + virStoragePoolPtr ret; + virStorageBackendPtr backend; + + if (!(def = virStoragePoolDefParse(conn, xml, NULL))) + return NULL; + + if (virStoragePoolObjFindByUUID(driver, def->uuid) || + virStoragePoolObjFindByName(driver, def->name)) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "storage pool already exists"); + virStoragePoolDefFree(def); + return NULL; + } + + if ((backend = virStorageBackendForType(def->type)) == NULL) { + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(conn, driver, def))) { + virStoragePoolDefFree(def); + return NULL; + } + + if (backend->startPool(conn, pool) < 0) { + virStoragePoolObjRemove(driver, pool); + return NULL; + } + pool->active = 1; + driver->nactivePools++; + driver->ninactivePools--; + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + + return ret; +} + +static virStoragePoolPtr storagePoolDefine(virConnectPtr conn, const char *xml) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr )conn->storagePrivateData; + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + virStoragePoolPtr ret; + virStorageBackendPtr backend; + + if (!(def = virStoragePoolDefParse(conn, xml, NULL))) + return NULL; + + if ((backend = virStorageBackendForType(def->type)) == NULL) { + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(conn, driver, def))) { + virStoragePoolDefFree(def); + return NULL; + } + + if (virStoragePoolObjSaveDef(conn, driver, pool, def) < 0) { + virStoragePoolObjRemove(driver, pool); + return NULL; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + return ret; +} + +static int storagePoolUndefine(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, "pool is still active"); + return -1; + } + + if (virStoragePoolObjDeleteDef(obj->conn, pool) < 0) + return -1; + + if (unlink(pool->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) + storageLog("Failed to delete autostart link '%s': %s", + pool->autostartLink, strerror(errno)); + + free(pool->configFile); + pool->configFile = NULL; + free(pool->autostartLink); + pool->autostartLink = NULL; + + virStoragePoolObjRemove(driver, pool); + + return 0; +} + +static int storagePoolStart(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, "pool already active"); + return -1; + } + if (backend->startPool && + backend->startPool(obj->conn, pool) < 0) + return -1; + if (backend->refreshPool(obj->conn, pool) < 0) { + if (backend->stopPool) + backend->stopPool(obj->conn, pool); + return -1; + } + + pool->active = 1; + driver->nactivePools++; + driver->ninactivePools--; + + return 0; +} + +static int storagePoolBuild(virStoragePoolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is already active"); + return -1; + } + + if (backend->buildPool && + backend->buildPool(obj->conn, pool, flags) < 0) + return -1; + + return 0; +} + + +static int storagePoolDestroy(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + if (backend->stopPool && + backend->stopPool(obj->conn, pool) < 0) + return -1; + + virStoragePoolObjClearVols(pool); + + pool->active = 0; + driver->nactivePools--; + driver->ninactivePools++; + + if (pool->configFile == NULL) + virStoragePoolObjRemove(driver, pool); + + return 0; +} + + +static int storagePoolDelete(virStoragePoolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is still active"); + return -1; + } + + if (backend->deletePool && + backend->deletePool(obj->conn, pool, flags) < 0) + return -1; + + return 0; +} + + +static int storagePoolRefresh(virStoragePoolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + int ret = 0; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + virStoragePoolObjClearVols(pool); + if ((ret = backend->refreshPool(obj->conn, pool)) < 0) { + if (backend->stopPool) + backend->stopPool(obj->conn, pool); + + pool->active = 0; + driver->nactivePools--; + driver->ninactivePools++; + + if (pool->configFile == NULL) + virStoragePoolObjRemove(driver, pool); + } + + return ret; +} + + +static int storagePoolGetInfo(virStoragePoolPtr obj, virStoragePoolInfoPtr info) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + memset(info, 0, sizeof(virStoragePoolInfo)); + if (pool->active) + info->state = VIR_STORAGE_POOL_RUNNING; + else + info->state = VIR_STORAGE_POOL_INACTIVE; + info->capacity = pool->def->capacity; + info->allocation = pool->def->allocation; + info->available = pool->def->available; + + return 0; +} + +static char *storagePoolDumpXML(virStoragePoolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return NULL; + } + + return virStoragePoolDefFormat(obj->conn, pool->def); +} + +static int storagePoolGetAutostart(virStoragePoolPtr obj, + int *autostart) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no pool with matching uuid"); + return -1; + } + + if (!pool->configFile) { + *autostart = 0; + } else { + *autostart = pool->autostart; + } + + return 0; +} + +static int storagePoolSetAutostart(virStoragePoolPtr obj, + int autostart) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no pool with matching uuid"); + return -1; + } + + if (!pool->configFile) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_ARG, + "pool has no config file"); + return -1; + } + + autostart = (autostart != 0); + + if (pool->autostart == autostart) + return 0; + + if (autostart) { + int err; + + if ((err = virFileMakePath(driver->autostartDir))) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "cannot create autostart directory %s: %s", + driver->autostartDir, strerror(err)); + return -1; + } + + if (symlink(pool->configFile, pool->autostartLink) < 0) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "Failed to create symlink '%s' to '%s': %s", + pool->autostartLink, pool->configFile, strerror(errno)); + return -1; + } + } else { + if (unlink(pool->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "Failed to delete symlink '%s': %s", + pool->autostartLink, strerror(errno)); + return -1; + } + } + + pool->autostart = autostart; + + return 0; +} + + +static int storagePoolNumVolumes(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + return pool->nvolumes; +} + +static int storagePoolListVolumes(virStoragePoolPtr obj, char **const names, int maxnames) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + int i = 0; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + memset(names, 0, maxnames); + vol = pool->volumes; + while (vol && i < maxnames) { + names[i] = strdup(vol->name); + if (names[i] == NULL) { + virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, "name"); + goto cleanup; + } + vol = vol->next; + i++; + } + + return i; + + cleanup: + for (i = 0 ; i < maxnames ; i++) { + free(names[i]); + names[i] = NULL; + } + return -1; +} + + +static virStorageVolPtr storageVolumeLookupByName(virStoragePoolPtr obj, const char *name) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return NULL; + } + + vol = virStorageVolDefFindByName(pool, name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage vol with matching name"); + return NULL; + } + + return virGetStorageVol(obj->conn, pool->def->name, vol->name, vol->key); +} + + +static virStorageVolPtr storageVolumeLookupByKey(virConnectPtr conn, const char *key) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (virStoragePoolObjIsActive(pool)) { + virStorageVolDefPtr vol = virStorageVolDefFindByKey(pool, key); + + if (vol) + return virGetStorageVol(conn, pool->def->name, vol->name, vol->key); + } + pool = pool->next; + } + + virStorageReportError(conn, VIR_ERR_INVALID_STORAGE_VOL, + "no storage vol with matching key"); + return NULL; +} + +static virStorageVolPtr storageVolumeLookupByPath(virConnectPtr conn, const char *path) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (virStoragePoolObjIsActive(pool)) { + virStorageVolDefPtr vol = virStorageVolDefFindByPath(pool, path); + + if (vol) + return virGetStorageVol(conn, pool->def->name, vol->name, vol->key); + } + pool = pool->next; + } + + virStorageReportError(conn, VIR_ERR_INVALID_STORAGE_VOL, + "no storage vol with matching path"); + return NULL; +} + +static virStorageVolPtr storageVolumeCreateXML(virStoragePoolPtr obj, + const char *xmldesc, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return NULL; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return NULL; + + vol = virStorageVolDefParse(obj->conn, pool->def, xmldesc, NULL); + if (vol == NULL) + return NULL; + + if (virStorageVolDefFindByName(pool, vol->name)) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "storage vol already exists"); + virStorageVolDefFree(vol); + return NULL; + } + + if (!backend->createVol) { + virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, + "storage pool does not support volume creation"); + virStorageVolDefFree(vol); + return NULL; + } + + if (backend->createVol(obj->conn, pool, vol) < 0) { + virStorageVolDefFree(vol); + return NULL; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + + return virGetStorageVol(obj->conn, pool->def->name, vol->name, vol->key); +} + +static int storageVolumeDelete(virStorageVolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageBackendPtr backend; + virStorageVolDefPtr vol, tmp, prev; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return -1; + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage vol with matching name"); + return -1; + } + + if (!backend->deleteVol) { + virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, + "storage pool does not support volume deletion"); + virStorageVolDefFree(vol); + return -1; + } + + if (backend->deleteVol(obj->conn, pool, vol, flags) < 0) { + return -1; + } + + prev = NULL; + tmp = pool->volumes; + while (tmp) { + if (tmp == vol) { + break; + } + prev = tmp; + tmp = tmp->next; + } + if (prev) { + prev->next = vol->next; + } else { + pool->volumes = vol->next; + } + pool->nvolumes--; + virStorageVolDefFree(vol); + + return 0; +} + +static int storageVolumeGetInfo(virStorageVolPtr obj, virStorageVolInfoPtr info) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageBackendPtr backend; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage vol with matching name"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return -1; + + if (backend->refreshVol && + backend->refreshVol(obj->conn, pool, vol) < 0) + return -1; + + memset(info, 0, sizeof(*info)); + info->type = backend->volType; + info->capacity = vol->capacity; + info->allocation = vol->allocation; + + return 0; +} + +static char *storageVolumeGetXMLDesc(virStorageVolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageBackendPtr backend; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return NULL; + } + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage vol with matching name"); + return NULL; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return NULL; + + return virStorageVolDefFormat(obj->conn, pool->def, vol); +} + +static char *storageVolumeGetPath(virStorageVolPtr obj) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageVolDefPtr vol; + char *ret; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return NULL; + } + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage vol with matching name"); + return NULL; + } + + ret = strdup(vol->target.path); + if (ret == NULL) { + virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, "path"); + return NULL; + } + return ret; +} + + + + + +static virStorageDriver storageDriver = { + "storage", + storageOpen, + storageClose, + storageNumPools, + storageListPools, + storageNumDefinedPools, + storageListDefinedPools, + storageDiscoverPools, + storagePoolLookupByName, + storagePoolLookupByUUID, + storagePoolLookupByVolume, + storagePoolCreate, + storagePoolDefine, + storagePoolBuild, + storagePoolUndefine, + storagePoolStart, + storagePoolDestroy, + storagePoolDelete, + storagePoolRefresh, + storagePoolGetInfo, + storagePoolDumpXML, + storagePoolGetAutostart, + storagePoolSetAutostart, + storagePoolNumVolumes, + storagePoolListVolumes, + storageVolumeLookupByName, + storageVolumeLookupByKey, + storageVolumeLookupByPath, + storageVolumeCreateXML, + storageVolumeDelete, + storageVolumeGetInfo, + storageVolumeGetXMLDesc, + storageVolumeGetPath +}; + + +static virStateDriver stateDriver = { + storageDriverStartup, + storageDriverShutdown, + storageDriverReload, + storageDriverActive, +}; + +int storageRegister(void) { + virRegisterStorageDriver(&storageDriver); + virRegisterStateDriver(&stateDriver); + return 0; +} + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/storage_driver.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_driver.h Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,45 @@ +/* + * storage_driver.h: core driver for storage APIs + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_DRIVER_H__ +#define __VIR_STORAGE_DRIVER_H__ + +#include "storage_conf.h" + +int storageRegister(void); + +#endif /* __VIR_STORAGE_DRIVER_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/virterror.c --- a/src/virterror.c Thu Feb 07 12:33:33 2008 -0500 +++ b/src/virterror.c Thu Feb 07 12:59:40 2008 -0500 @@ -668,6 +668,18 @@ __virErrorMsg(virErrorNumber error, cons else errmsg = _("authentication failed: %s"); break; + case VIR_ERR_NO_STORAGE_POOL: + if (info == NULL) + errmsg = _("Storage pool not found"); + else + errmsg = _("Storage pool not found: %s"); + break; + case VIR_ERR_NO_STORAGE_VOL: + if (info == NULL) + errmsg = _("Storage volume not found"); + else + errmsg = _("Storage volume not found: %s"); + break; case VIR_ERR_INVALID_STORAGE_POOL: if (info == NULL) errmsg = _("invalid storage pool pointer in"); -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
diff -r 77cf7f42edd4 src/storage_conf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_conf.c Thu Feb 07 12:59:40 2008 -0500 ... + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) { + xmlNodePtr *nodeset = NULL; + int nsource, i; + + if ((nsource = virXPathNodeSet("/pool/source/device", ctxt, &nodeset)) <= 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "cannot extract source devices"); + goto cleanup; + } + if ((ret->source.devices = calloc(nsource, sizeof(*ret->source.devices))) == NULL) { + free(nodeset); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "device"); + goto cleanup; + } + for (i = 0 ; i < nsource ; i++) { + xmlChar *path = xmlGetProp(nodeset[i], BAD_CAST "path"); + if (path == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source device path");
Hi Dan, It looks like you need to free nodeset here, too.
+ goto cleanup; + } + ret->source.devices[i].path = (char *)path; + } + free(nodeset); + ret->source.ndevice = nsource; + } + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DIR) { + if ((ret->source.dir = virXPathString("string(/pool/source/dir/@path)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source path"); + goto cleanup; + } + }

On Mon, Feb 18, 2008 at 10:25:04AM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
diff -r 77cf7f42edd4 src/storage_conf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_conf.c Thu Feb 07 12:59:40 2008 -0500 ... + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) { + xmlNodePtr *nodeset = NULL; + int nsource, i; + + if ((nsource = virXPathNodeSet("/pool/source/device", ctxt, &nodeset)) <= 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "cannot extract source devices"); + goto cleanup; + } + if ((ret->source.devices = calloc(nsource, sizeof(*ret->source.devices))) == NULL) { + free(nodeset); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "device"); + goto cleanup; + } + for (i = 0 ; i < nsource ; i++) { + xmlChar *path = xmlGetProp(nodeset[i], BAD_CAST "path"); + if (path == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source device path");
It looks like you need to free nodeset here, too.
Yes, thanks Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
diff -r 77cf7f42edd4 src/storage_conf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_conf.c Thu Feb 07 12:59:40 2008 -0500 ... +static int virStoragePoolDefParsePerms(virConnectPtr conn, xmlXPathContextPtr ctxt, virStoragePermsPtr perms) { + char *mode; + long v; + + mode = virXPathString("string(/pool/permissions/mode)", ctxt); + if (!mode) { + perms->mode = 0700; + } else { + char *end; + perms->mode = strtol(mode, &end, 8); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed octal mode"); + return -1; + } + } ...
As long as you're checking for invalid inputs, you might as well check for a couple other cases: negative: -01, and overflow: (otherwise, 07777777777777777777777777777 is accepted) And you can drop the "end &&" part, since "end" will never be NULL. if (*end || perms->mode < 0 || perms->mode > 0777) {
+ if (!mode) { + perms->mode = 0600; + } else { + char *end; + perms->mode = strtol(mode, &end, 8); + if (end && *end) {
Same here. ...
+ *ret = strtoull(val, &end, 10); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed capacity element"); + return -1;
Maybe use virStrToLong_ui instead of strtoull here?

On Mon, Feb 18, 2008 at 02:41:09PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
diff -r 77cf7f42edd4 src/storage_conf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_conf.c Thu Feb 07 12:59:40 2008 -0500 ... +static int virStoragePoolDefParsePerms(virConnectPtr conn, xmlXPathContextPtr ctxt, virStoragePermsPtr perms) { + char *mode; + long v; + + mode = virXPathString("string(/pool/permissions/mode)", ctxt); + if (!mode) { + perms->mode = 0700; + } else { + char *end; + perms->mode = strtol(mode, &end, 8); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed octal mode"); + return -1; + } + } ...
As long as you're checking for invalid inputs, you might as well check for a couple other cases: negative: -01, and overflow: (otherwise, 07777777777777777777777777777 is accepted) And you can drop the "end &&" part, since "end" will never be NULL.
Ok. Will do.
+ *ret = strtoull(val, &end, 10); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed capacity element"); + return -1;
Maybe use virStrToLong_ui instead of strtoull here?
Yes, it should do. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
+static int virStorageSize(virConnectPtr conn, + const char *unit, + const char *val, + unsigned long long *ret) { + unsigned long long mult; + char *end; + + if (!unit) { + mult = 1; + } else { + switch (unit[0]) { + case 'k': + case 'K': + mult = 1024ull; + break; + + case 'm': + case 'M': + mult = 1024ull * 1024ull; + break; + + case 'g': + case 'G': + mult = 1024ull * 1024ull * 1024ull; + break; + + case 't': + case 'T': + mult = 1024ull * 1024ull * 1024ull * 1024ull; + break;
Hi Dan,
From our current perspective, 'T' looks big enough, but you might want to add 'P', 'Y', and 'Z', if only so the code doesn't seem clueless when reporting "unknown size units" :-)
+ default: + virStorageReportError(conn, VIR_ERR_XML_ERROR, "Unknown size units %s", unit);
Alternatively, s/Unknown/Invalid/
+ return -1; + } + } + + *ret = strtoull(val, &end, 10); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed capacity element"); + return -1; + } + *ret *= mult;
If you change the guard to e.g., if (*end || *ret > ULLONG_MAX / mult) { virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed capacity element"); you avoid risk of overflow.

On Mon, Feb 18, 2008 at 03:20:37PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
+static int virStorageSize(virConnectPtr conn, + const char *unit, + const char *val, + unsigned long long *ret) { + unsigned long long mult; + char *end; + + if (!unit) { + mult = 1; + } else { + switch (unit[0]) { + case 'k': + case 'K': + mult = 1024ull; + break; + + case 'm': + case 'M': + mult = 1024ull * 1024ull; + break; + + case 'g': + case 'G': + mult = 1024ull * 1024ull * 1024ull; + break; + + case 't': + case 'T': + mult = 1024ull * 1024ull * 1024ull * 1024ull; + break;
From our current perspective, 'T' looks big enough, but you might want to add 'P', 'Y', and 'Z', if only so the code doesn't seem clueless when reporting "unknown size units" :-)
Yeah, might as well.
+ *ret = strtoull(val, &end, 10); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed capacity element"); + return -1; + } + *ret *= mult;
If you change the guard to e.g.,
if (*end || *ret > ULLONG_MAX / mult) { virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed capacity element");
you avoid risk of overflow.
Ok, thanks Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:36:04AM +0000, Daniel P. Berrange wrote:
This patch provides the general purpose code for the storage driver. The storage_driver.{c,h} file implements the libvirt internal storage driver API. The storage_conf.{c,h} file takes care of parsing and formatting the XML for pools and volumes. This should be reusable by the test driver's impl of the storage APIs in the future. The final storage_backend.{c,h} file provides a 2nd level internal driver API. This allows the main storage driver to call into storage pool specific impls for iSCSI, disk, filesystem, LVM and more. The storage_backend.h definitions allow the specific drivers to declare particular parts of the generic pool XML which are mandatory. For example, a disk pool always requires a source device element. [...] +/* Flags to indicate mandatory components in the pool source */ +enum { + VIR_STORAGE_BACKEND_POOL_SOURCE_HOST = (1<<0), + VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE = (1<<1),
wondering about 1<<2 , is that the storage option your removed compared to previous patches ?
+ VIR_STORAGE_BACKEND_POOL_SOURCE_DIR = (1<<3), + VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER = (1<<4), +}; [...] +static virStoragePoolDefPtr virStoragePoolDefParseDoc(virConnectPtr conn, + xmlXPathContextPtr ctxt, xmlNodePtr root) { [...] + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) { + xmlNodePtr *nodeset = NULL; + int nsource, i; + + if ((nsource = virXPathNodeSet("/pool/source/device", ctxt, &nodeset)) <= 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "cannot extract source devices"); + goto cleanup; + } + if ((ret->source.devices = calloc(nsource, sizeof(*ret->source.devices))) == NULL) { + free(nodeset); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "device"); + goto cleanup; + } + for (i = 0 ; i < nsource ; i++) { + xmlChar *path = xmlGetProp(nodeset[i], BAD_CAST "path"); + if (path == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source device path");
hum nodeset is leaked here it seems.
+ goto cleanup; + } + ret->source.devices[i].path = (char *)path; + } + free(nodeset); + ret->source.ndevice = nsource; + } [...] + authType = virXPathString("string(/pool/source/auth/@type)", ctxt); + if (authType == NULL) { + ret->source.authType = VIR_STORAGE_POOL_AUTH_NONE; + } else { + if (STREQ(authType, "chap")) { + ret->source.authType = VIR_STORAGE_POOL_AUTH_CHAP; + } else { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "unknown auth type '%s'", (const char *)authType); + goto cleanup;
hum, the cleanup of authType is more complex than necessary, i would just free(authType); here before goto cleanup;
+ } + free(authType); + authType = NULL;
remove this affectation
+ } + [...] + return ret; + + cleanup: + free(uuid); + if (type) + xmlFree(type); + if (authType) + xmlFree(authType);
and remove this, basically keeping authType processing to just the block where it is used.
+ virStoragePoolDefFree(ret); + return NULL; +} + +virStoragePoolDefPtr virStoragePoolDefParse(virConnectPtr conn, + const char *xmlStr, const char *filename) { + virStoragePoolDefPtr ret = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr ctxt = NULL; + [...] + xmlFreeDoc(xml); + + return ret; + + cleanup: + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + return NULL; +}
since we try to remove if (x) free(x) style, just call xmlFreeDoc(xml); since xmlFreeDoc handles NULLs fine. [...]
+ cleanup: + if (buf) virBufferFree(buf);
same here
+ return NULL;
[...]
+ ret->target.path = virXPathString("string(/volume/target/path)", ctxt); + if (options->formatFromString) { + char *format = virXPathString("string(/volume/target/format/@type)", ctxt); + if ((ret->target.format = (options->formatFromString)(conn, format)) < 0) {
So you have in a structure an optional function to do the formatting, feels a bit strange, but okay [...]
+ if (xml) + xmlFreeDoc(xml);
one more, they can probably be all cleaned up after the commit anyway [...]
+static virStoragePoolObjPtr +virStoragePoolObjLoad(virStorageDriverStatePtr driver, + const char *file, + const char *path, + const char *xml, + const char *autostartLink) { + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + + if (!(def = virStoragePoolDefParse(NULL, xml, file))) { + virErrorPtr err = virGetLastError(); + virStorageLog("Error parsing storage pool config '%s' : %s", + path, err->message); + return NULL; + } + + if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { + virStorageLog("Storage Pool config filename '%s' does not match pool name '%s'", + path, def->name); + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(NULL, driver, def))) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + }
Shouldn't autostartLink and path be checked against NULL
+ pool->configFile = strdup(path); + if (pool->configFile == NULL) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + pool->autostartLink = strdup(autostartLink); + if (pool->autostartLink == NULL) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + }
Since that seems assumed in that function, okay it is not public, so if internally we know the value is never NULL, fine, but otherwise we would get a confusing error message. [...]
diff -r 77cf7f42edd4 src/storage_driver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_driver.c Thu Feb 07 12:59:40 2008 -0500 [...] +#define storageLog(msg...) fprintf(stderr, msg)
any way to integrate into normal error reporting ? But that should not block from commiting to CVs. [...]
+static int storageListPools(virConnectPtr conn, char **const names, int nnames) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + int got = 0, i; + while (pool && got < nnames) { + if (virStoragePoolObjIsActive(pool)) { + if (!(names[got] = strdup(pool->def->name))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "names"); + goto cleanup; + } + got++; + } + pool = pool->next; + } + return got; + + cleanup: + for (i = 0 ; i < got ; i++) { + free(names[i]); + names[i] = NULL; + }
Hum, that looks right, but when passed a array like that it may be a good idea to memset(0) it it can help triggering errors early or the task of debugging. [...]
+ +static int storageListDefinedPools(virConnectPtr conn, char **const names, int nnames) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + int got = 0, i; + while (pool && got < nnames) { + if (!virStoragePoolObjIsActive(pool)) { + if (!(names[got] = strdup(pool->def->name))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "names"); + goto cleanup; + } + got++; + } + pool = pool->next; + } + return got; + + cleanup: + for (i = 0 ; i < got ; i++) { + free(names[i]); + names[i] = NULL; + } + return -1; +}
Similar here. [...]
+static int storagePoolDelete(virStoragePoolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is still active"); + return -1; + } + + if (backend->deletePool && + backend->deletePool(obj->conn, pool, flags) < 0) + return -1; + + return 0; +}
So you return 0 is the backend has no deletePool defined, looks a bit strange
+ +static int storagePoolRefresh(virStoragePoolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) {
we assume backend->refreshPool is always there, maybe a test is in order.
+ if ((ret = backend->refreshPool(obj->conn, pool)) < 0) { + if (backend->stopPool) + backend->stopPool(obj->conn, pool);
okay, huge, but looks fine. One thing I realize is that it adds a lot of new strings for localization so it's better to push early if we want to get decent translations. Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Mon, Feb 18, 2008 at 09:51:28AM -0500, Daniel Veillard wrote:
On Tue, Feb 12, 2008 at 04:36:04AM +0000, Daniel P. Berrange wrote:
This patch provides the general purpose code for the storage driver. The storage_driver.{c,h} file implements the libvirt internal storage driver API. The storage_conf.{c,h} file takes care of parsing and formatting the XML for pools and volumes. This should be reusable by the test driver's impl of the storage APIs in the future. The final storage_backend.{c,h} file provides a 2nd level internal driver API. This allows the main storage driver to call into storage pool specific impls for iSCSI, disk, filesystem, LVM and more. The storage_backend.h definitions allow the specific drivers to declare particular parts of the generic pool XML which are mandatory. For example, a disk pool always requires a source device element. [...] +/* Flags to indicate mandatory components in the pool source */ +enum { + VIR_STORAGE_BACKEND_POOL_SOURCE_HOST = (1<<0), + VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE = (1<<1),
wondering about 1<<2 , is that the storage option your removed compared to previous patches ?
Yes a (harmless) bug -- I'll updated the numbers to remove the gap
+ VIR_STORAGE_BACKEND_POOL_SOURCE_DIR = (1<<3), + VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER = (1<<4), +};
+ for (i = 0 ; i < nsource ; i++) { + xmlChar *path = xmlGetProp(nodeset[i], BAD_CAST "path"); + if (path == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source device path");
hum nodeset is leaked here it seems.
Yes will fix that.
+ authType = virXPathString("string(/pool/source/auth/@type)", ctxt); + if (authType == NULL) { + ret->source.authType = VIR_STORAGE_POOL_AUTH_NONE; + } else { + if (STREQ(authType, "chap")) { + ret->source.authType = VIR_STORAGE_POOL_AUTH_CHAP; + } else { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "unknown auth type '%s'", (const char *)authType); + goto cleanup;
hum, the cleanup of authType is more complex than necessary, i would just free(authType); here before goto cleanup;
+ } + free(authType); + authType = NULL;
remove this affectation
+ } + [...] + return ret; + + cleanup: + free(uuid); + if (type) + xmlFree(type); + if (authType) + xmlFree(authType);
and remove this, basically keeping authType processing to just the block where it is used.
Ok, reasonable enough.
+ xmlFreeDoc(xml); + + return ret; + + cleanup: + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + return NULL; +}
since we try to remove if (x) free(x) style, just call xmlFreeDoc(xml); since xmlFreeDoc handles NULLs fine.
Yes, I did 'make syntax-check' but seems to have missed those
+ ret->target.path = virXPathString("string(/volume/target/path)", ctxt); + if (options->formatFromString) { + char *format = virXPathString("string(/volume/target/format/@type)", ctxt); + if ((ret->target.format = (options->formatFromString)(conn, format)) < 0) {
So you have in a structure an optional function to do the formatting, feels a bit strange, but okay
Well the format field is optional - not all pools support volumes with special formats. Fs/dir driver supports raw, qcow, vmdk, etc. The iSCSI driver though doesn't support any - you just get a block device, no choice, so the formatFromString and formatToString functions are not needed.
+static virStoragePoolObjPtr +virStoragePoolObjLoad(virStorageDriverStatePtr driver, + const char *file, + const char *path, + const char *xml, + const char *autostartLink) { + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + + if (!(def = virStoragePoolDefParse(NULL, xml, file))) { + virErrorPtr err = virGetLastError(); + virStorageLog("Error parsing storage pool config '%s' : %s", + path, err->message); + return NULL; + } + + if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { + virStorageLog("Storage Pool config filename '%s' does not match pool name '%s'", + path, def->name); + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(NULL, driver, def))) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + }
Shouldn't autostartLink and path be checked against NULL
The caller will never pass in a NULL
+ pool->configFile = strdup(path); + if (pool->configFile == NULL) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + pool->autostartLink = strdup(autostartLink); + if (pool->autostartLink == NULL) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + }
Since that seems assumed in that function, okay it is not public, so if internally we know the value is never NULL, fine, but otherwise we would get a confusing error message.
Yep, we know the caller won't pass in a NULL in this internal method.
[...]
diff -r 77cf7f42edd4 src/storage_driver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_driver.c Thu Feb 07 12:59:40 2008 -0500 [...] +#define storageLog(msg...) fprintf(stderr, msg)
any way to integrate into normal error reporting ? But that should not block from commiting to CVs.
This isn't really error reporting - its just for a few harmless debug messages. We don't have any API for propagating log messages back to the application, though I've proposed it once before...
+static int storageListPools(virConnectPtr conn, char **const names, int nnames) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + int got = 0, i; + while (pool && got < nnames) { + if (virStoragePoolObjIsActive(pool)) { + if (!(names[got] = strdup(pool->def->name))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "names"); + goto cleanup; + } + got++; + } + pool = pool->next; + } + return got; + + cleanup: + for (i = 0 ; i < got ; i++) { + free(names[i]); + names[i] = NULL; + }
Hum, that looks right, but when passed a array like that it may be a good idea to memset(0) it it can help triggering errors early or the task of debugging.
Yep, good idea.
+static int storagePoolDelete(virStoragePoolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is still active"); + return -1; + } + + if (backend->deletePool && + backend->deletePool(obj->conn, pool, flags) < 0) + return -1; + + return 0; +}
So you return 0 is the backend has no deletePool defined, looks a bit strange
Hmm, yes, should return -1 if deletePool is not supported by the driver
+ +static int storagePoolRefresh(virStoragePoolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) {
we assume backend->refreshPool is always there, maybe a test is in order.
refreshPool & refreshVol are the two compulsory drivers methods for the backend, all the rest are optional. There's not much point in checking because anyone adding a new driver will immediately find that they're required because nothing will work without them Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Mon, Feb 18, 2008 at 09:51:28AM -0500, Daniel Veillard wrote:
On Tue, Feb 12, 2008 at 04:36:04AM +0000, Daniel P. Berrange wrote: ...
+ if (xml) + xmlFreeDoc(xml); + return NULL; +}
since we try to remove if (x) free(x) style, just call xmlFreeDoc(xml); since xmlFreeDoc handles NULLs fine.
Yes, I did 'make syntax-check' but seems to have missed those
To make it check for that, add a line in Makefile.cfg: useless_free_options = \ --name=sexpr_free \ --name=xmlXPathFreeContext \ --name=xmlXPathFreeObject FYI, I did consider adding it a week or two ago, but saw the xmlGenericError call in libxml2/tree.c: void xmlFreeDoc(xmlDocPtr cur) { xmlDtdPtr extSubset, intSubset; xmlDictPtr dict = NULL; if (cur == NULL) { #ifdef DEBUG_TREE xmlGenericError(xmlGenericErrorContext, "xmlFreeDoc : document == NULL\n"); #endif return; } Of course, if DV says it's ok, I guess that #ifdef'd code is not an issue.

On Tue, Feb 19, 2008 at 10:09:20AM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Mon, Feb 18, 2008 at 09:51:28AM -0500, Daniel Veillard wrote:
On Tue, Feb 12, 2008 at 04:36:04AM +0000, Daniel P. Berrange wrote: ...
+ if (xml) + xmlFreeDoc(xml); + return NULL; +}
since we try to remove if (x) free(x) style, just call xmlFreeDoc(xml); since xmlFreeDoc handles NULLs fine.
Yes, I did 'make syntax-check' but seems to have missed those
To make it check for that, add a line in Makefile.cfg:
useless_free_options = \ --name=sexpr_free \ --name=xmlXPathFreeContext \ --name=xmlXPathFreeObject
FYI, I did consider adding it a week or two ago, but saw the xmlGenericError call in libxml2/tree.c:
void xmlFreeDoc(xmlDocPtr cur) { xmlDtdPtr extSubset, intSubset; xmlDictPtr dict = NULL;
if (cur == NULL) { #ifdef DEBUG_TREE xmlGenericError(xmlGenericErrorContext, "xmlFreeDoc : document == NULL\n"); #endif return; }
Of course, if DV says it's ok, I guess that #ifdef'd code is not an issue.
Well DEBUG_TREE is not defined by default in libxml2 builds, really all xml*Free* should be okay with NULL pointers, that's part of regression tests, well except for xmlFree which is an indirection to the OS free() established at link time. Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
+int virStoragePoolObjSaveDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool, + virStoragePoolDefPtr def) { + char *xml; + int fd = -1, ret = -1; + int towrite;
slightly better to use ssize_t. see below. ...
+ if ((fd = open(pool->configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot create config file %s: %s", + pool->configFile, strerror(errno)); + goto cleanup; + } + + towrite = strlen(xml);
Here, you'd expect towrite to be of type size_t. But in comparing with write's return value, you want ssize_t. No big deal in any case, because we won't be generating 2^31-byte-long XML strings.
+ if (write(fd, xml, towrite) != towrite) {
write should always be used in a loop, since it can succeed and write less than requested. Here, it's fine to use fopen/fwrite/fclose instead.
+ virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot write config file %s: %s", + pool->configFile, strerror(errno));

On Tue, Feb 12, 2008 at 04:36:04AM +0000, Daniel P. Berrange wrote:
This patch provides the general purpose code for the storage driver. The storage_driver.{c,h} file implements the libvirt internal storage driver API. The storage_conf.{c,h} file takes care of parsing and formatting the XML for pools and volumes. This should be reusable by the test driver's impl of the storage APIs in the future. The final storage_backend.{c,h} file provides a 2nd level internal driver API. This allows the main storage driver to call into storage pool specific impls for iSCSI, disk, filesystem, LVM and more. The storage_backend.h definitions allow the specific drivers to declare particular parts of the generic pool XML which are mandatory. For example, a disk pool always requires a source device element.
This patch adds AC_SYS_LARGEFILE to turn on large file support inside libvirt globally, allowing us to manage files > 2GB.
Makefile.maint | 2 b/src/storage_backend.c | 88 ++ b/src/storage_backend.h | 121 +++ b/src/storage_conf.c | 1251 +++++++++++++++++++++++++++++++++++++ b/src/storage_conf.h | 314 +++++++++ b/src/storage_driver.c | 1261 ++++++++++++++++++++++++++++++++++++++ b/src/storage_driver.h | 45 + configure.in | 2 docs/devhelp/libvirt-libvirt.html | 39 - include/libvirt/virterror.h | 2 po/POTFILES.in | 3 src/Makefile.am | 3 src/libvirt.c | 2 src/virterror.c | 12 14 files changed, 3122 insertions(+), 23 deletions(-) diff -r d043e3715bce Makefile.maint --- a/Makefile.maint Tue Feb 19 16:26:35 2008 -0500 +++ b/Makefile.maint Tue Feb 19 16:59:10 2008 -0500 @@ -289,7 +289,7 @@ sc_two_space_separator_in_usage: 1>&2; exit 1; } || : err_func_re = \ -(DISABLE_fprintf|qemudLog|(xmlRpc|vir(Xend|XML|Hash|Conf|Test|LibConn))Error) +(DISABLE_fprintf|qemudLog|(xmlRpc|vir(Xend|XML|Hash|Conf|Test|LibConn|StorageReport))Error) # Look for diagnostics that aren't marked for translation. # This won't find any for which error's format string is on a separate line. diff -r d043e3715bce configure.in --- a/configure.in Tue Feb 19 16:26:35 2008 -0500 +++ b/configure.in Tue Feb 19 16:59:10 2008 -0500 @@ -689,6 +689,8 @@ AC_SUBST(CYGWIN_EXTRA_PYTHON_LIBADD) AC_SUBST(CYGWIN_EXTRA_PYTHON_LIBADD) AC_SUBST(MINGW_EXTRA_LDFLAGS) +AC_SYS_LARGEFILE + # very annoying rm -f COPYING cp COPYING.LIB COPYING diff -r d043e3715bce docs/devhelp/libvirt-libvirt.html --- a/docs/devhelp/libvirt-libvirt.html Tue Feb 19 16:26:35 2008 -0500 +++ b/docs/devhelp/libvirt-libvirt.html Tue Feb 19 16:59:10 2008 -0500 @@ -112,7 +112,7 @@ int <a href="#virDomainFree">virDomainFr int <a href="#virDomainFree">virDomainFree</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain); const char * <a href="#virStoragePoolGetName">virStoragePoolGetName</a> (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool); int <a href="#virDomainSetAutostart">virDomainSetAutostart</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> int autostart); -<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> <a href="#virStoragePoolDefineXML">virStoragePoolDefineXML</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xml); +<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> <a href="#virStoragePoolDefineXML">virStoragePoolDefineXML</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xml, <br/> unsigned int flags); <a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> <a href="#virStorageVolLookupByPath">virStorageVolLookupByPath</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * path); <a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> <a href="#virStorageVolLookupByName">virStorageVolLookupByName</a> (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br/> const char * name); <a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> <a href="#virDomainCreateLinux">virDomainCreateLinux</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xmlDesc, <br/> unsigned int flags); @@ -126,7 +126,6 @@ int <a href="#virConnectNumOfNetworks">v <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> <a href="#virStoragePoolLookupByUUIDString">virStoragePoolLookupByUUIDString</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * uuidstr); char * <a href="#virDomainGetXMLDesc">virDomainGetXMLDesc</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> int flags); int <a href="#virStoragePoolGetUUID">virStoragePoolGetUUID</a> (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br/> unsigned char * uuid); -int <a href="#virConnectDiscoverStoragePools">virConnectDiscoverStoragePools</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * hostname, <br/> const char * type, <br/> unsigned int flags, <br/> char ** * xmlDesc); int <a href="#virStorageVolGetInfo">virStorageVolGetInfo</a> (<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> vol, <br/> <a href="libvirt-libvirt.html#virStorageVolInfoPtr">virStorageVolInfoPtr</a> info); const char * <a href="#virNetworkGetName">virNetworkGetName</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> network); int <a href="#virNetworkDestroy">virNetworkDestroy</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> network); @@ -173,7 +172,7 @@ int <a href="#virDomainGetUUID">virDomai int <a href="#virDomainGetUUID">virDomainGetUUID</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> unsigned char * uuid); <a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> <a href="#virNetworkCreateXML">virNetworkCreateXML</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xmlDesc); int <a href="#virDomainGetVcpus">virDomainGetVcpus</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> <a href="libvirt-libvirt.html#virVcpuInfoPtr">virVcpuInfoPtr</a> info, <br/> int maxinfo, <br/> unsigned char * cpumaps, <br/> int maplen); -<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> <a href="#virStoragePoolCreateXML">virStoragePoolCreateXML</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xmlDesc); +<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> <a href="#virStoragePoolCreateXML">virStoragePoolCreateXML</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xmlDesc, <br/> unsigned int flags); int <a href="#virStoragePoolGetInfo">virStoragePoolGetInfo</a> (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br/> <a href="libvirt-libvirt.html#virStoragePoolInfoPtr">virStoragePoolInfoPtr</a> info); int <a href="#virStoragePoolRefresh">virStoragePoolRefresh</a> (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br/> unsigned int flags); int <a href="#virConnectNumOfDefinedDomains">virConnectNumOfDefinedDomains</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn); @@ -210,7 +209,7 @@ int <a href="#virDomainGetUUIDString">vi <a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> <a href="#virDomainGetConnect">virDomainGetConnect</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> dom); int <a href="#virConnectNumOfDefinedStoragePools">virConnectNumOfDefinedStoragePools</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn); <a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> <a href="#virConnectOpen">virConnectOpen</a> (const char * name); -int <a href="#virStoragePoolCreate">virStoragePoolCreate</a> (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool); +int <a href="#virStoragePoolCreate">virStoragePoolCreate</a> (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br/> unsigned int flags); int <a href="#virDomainSetVcpus">virDomainSetVcpus</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> unsigned int nvcpus); unsigned int <a href="#virDomainGetID">virDomainGetID</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain); int <a href="#virDomainInterfaceStats">virDomainInterfaceStats</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> dom, <br/> const char * path, <br/> <a href="libvirt-libvirt.html#virDomainInterfaceStatsPtr">virDomainInterfaceStatsPtr</a> stats, <br/> size_t size); @@ -469,14 +468,14 @@ The content of this structure is not mad <div class="refsect2" lang="en"><h3><a name="virStoragePoolBuildFlags">Enum </a>virStoragePoolBuildFlags</h3><pre class="programlisting">enum <a href="#virStoragePoolBuildFlags">virStoragePoolBuildFlags</a> { <a name="VIR_STORAGE_POOL_BUILD_NEW">VIR_STORAGE_POOL_BUILD_NEW</a> = 0 /* Regular build from scratch */ <a name="VIR_STORAGE_POOL_BUILD_REPAIR">VIR_STORAGE_POOL_BUILD_REPAIR</a> = 1 /* Repair / reinitialize */ - <a name="VIR_STORAGE_POOL_BUILD_EXTEND">VIR_STORAGE_POOL_BUILD_EXTEND</a> = 2 /* Extend existing pool */ + <a name="VIR_STORAGE_POOL_BUILD_RESIZE">VIR_STORAGE_POOL_BUILD_RESIZE</a> = 2 /* Extend existing pool */ }; </pre><p/> </div> <hr/> <div class="refsect2" lang="en"><h3><a name="virStoragePoolDeleteFlags">Enum </a>virStoragePoolDeleteFlags</h3><pre class="programlisting">enum <a href="#virStoragePoolDeleteFlags">virStoragePoolDeleteFlags</a> { <a name="VIR_STORAGE_POOL_DELETE_NORMAL">VIR_STORAGE_POOL_DELETE_NORMAL</a> = 0 /* Delete metadata only (fast) */ - <a name="VIR_STORAGE_POOL_DELETE_CLEAR">VIR_STORAGE_POOL_DELETE_CLEAR</a> = 1 /* Clear all data to zeros (slow) */ + <a name="VIR_STORAGE_POOL_DELETE_ZEROED">VIR_STORAGE_POOL_DELETE_ZEROED</a> = 1 /* Clear all data to zeros (slow) */ }; </pre><p/> </div> @@ -515,7 +514,7 @@ The content of this structure is not mad <hr/> <div class="refsect2" lang="en"><h3><a name="virStorageVolDeleteFlags">Enum </a>virStorageVolDeleteFlags</h3><pre class="programlisting">enum <a href="#virStorageVolDeleteFlags">virStorageVolDeleteFlags</a> { <a name="VIR_STORAGE_VOL_DELETE_NORMAL">VIR_STORAGE_VOL_DELETE_NORMAL</a> = 0 /* Delete metadata only (fast) */ - <a name="VIR_STORAGE_VOL_DELETE_CLEAR">VIR_STORAGE_VOL_DELETE_CLEAR</a> = 1 /* Clear all data to zeros (slow) */ + <a name="VIR_STORAGE_VOL_DELETE_ZEROED">VIR_STORAGE_VOL_DELETE_ZEROED</a> = 1 /* Clear all data to zeros (slow) */ }; </pre><p/> </div> @@ -576,10 +575,6 @@ The content of this structure is not mad </pre><p>This function closes the connection to the Hypervisor. This should not be called if further interaction with the Hypervisor are needed especially if there is running domain which need further monitoring by the application.</p> <div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>conn</tt></i>:</span></td><td>pointer to the hypervisor connection</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 in case of success or -1 in case of error.</td></tr></tbody></table></div></div> <hr/> - <div class="refsect2" lang="en"><h3><a name="virConnectDiscoverStoragePools"/>virConnectDiscoverStoragePools ()</h3><pre class="programlisting">int virConnectDiscoverStoragePools (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * hostname, <br/> const char * type, <br/> unsigned int flags, <br/> char ** * xmlDesc)<br/> -</pre><p>Talks to a host and attempt to auto-discover a set of exported storage pools available. eg For iSCSI this would be a set of iSCSI targets. For NFS this would be a list of exported paths. Each discovered pool is returned as an XML document suitable for feeding into <a href="libvirt-libvirt.html#virStoragePoolCreateXML">virStoragePoolCreateXML</a></p> -<div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>conn</tt></i>:</span></td><td>pointer to hypervisor connection</td></tr><tr><td><span class="term"><i><tt>hostname</tt></i>:</span></td><td>host to discover pools on</td></tr><tr><td><span class="term"><i><tt>type</tt></i>:</span></td><td>type of storge pool to discover</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td>flags for discovery (unused, pass 0)</td></tr><tr><td><span class="term"><i><tt>xmlDesc</tt></i>:</span></td><td>return array of of XML documents, one per pool</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>number of discovered pools, or -1 on error</td></tr></tbody></table></div></div> - <hr/> <div class="refsect2" lang="en"><h3><a name="virConnectGetCapabilities"/>virConnectGetCapabilities ()</h3><pre class="programlisting">char * virConnectGetCapabilities (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn)<br/> </pre><p>Provides capabilities of the hypervisor / driver.</p> <div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>conn</tt></i>:</span></td><td>pointer to the hypervisor connection</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>NULL in case of error, or an XML string defining the capabilities. The client must free the returned string after use.</td></tr></tbody></table></div></div> @@ -924,28 +919,28 @@ The content of this structure is not mad </pre><p>Build the underlying storage pool</p> <div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td/></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 on success, or -1 upon failure</td></tr></tbody></table></div></div> <hr/> - <div class="refsect2" lang="en"><h3><a name="virStoragePoolCreate"/>virStoragePoolCreate ()</h3><pre class="programlisting">int virStoragePoolCreate (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool)<br/> + <div class="refsect2" lang="en"><h3><a name="virStoragePoolCreate"/>virStoragePoolCreate ()</h3><pre class="programlisting">int virStoragePoolCreate (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br/> unsigned int flags)<br/> </pre><p>Starts an inactive storage pool</p> -<div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 on success, or -1 if it could not be started</td></tr></tbody></table></div></div> - <hr/> - <div class="refsect2" lang="en"><h3><a name="virStoragePoolCreateXML"/>virStoragePoolCreateXML ()</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> virStoragePoolCreateXML (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xmlDesc)<br/> +<div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td/></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 on success, or -1 if it could not be started</td></tr></tbody></table></div></div> + <hr/> + <div class="refsect2" lang="en"><h3><a name="virStoragePoolCreateXML"/>virStoragePoolCreateXML ()</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> virStoragePoolCreateXML (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xmlDesc, <br/> unsigned int flags)<br/> </pre><p>Create a new storage based on its XML description. The pool is not persitent, so its definition will disappear when it is destroyed, or if the host is restarted</p> -<div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>conn</tt></i>:</span></td><td>pointer to hypervisor connection</td></tr><tr><td><span class="term"><i><tt>xmlDesc</tt></i>:</span></td><td>XML description for new pool</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>a <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> object, or NULL if creation failed</td></tr></tbody></table></div></div> - <hr/> - <div class="refsect2" lang="en"><h3><a name="virStoragePoolDefineXML"/>virStoragePoolDefineXML ()</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> virStoragePoolDefineXML (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xml)<br/> +<div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>conn</tt></i>:</span></td><td>pointer to hypervisor connection</td></tr><tr><td><span class="term"><i><tt>xmlDesc</tt></i>:</span></td><td>XML description for new pool</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td/></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>a <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> object, or NULL if creation failed</td></tr></tbody></table></div></div> + <hr/> + <div class="refsect2" lang="en"><h3><a name="virStoragePoolDefineXML"/>virStoragePoolDefineXML ()</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> virStoragePoolDefineXML (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xml, <br/> unsigned int flags)<br/> </pre><p>Define a new inactive storage pool based on its XML description. The pool is persitent, until explicitly undefined.</p> -<div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>conn</tt></i>:</span></td><td>pointer to hypervisor connection</td></tr><tr><td><span class="term"><i><tt>xml</tt></i>:</span></td><td>XML description for new pool</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>a <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> object, or NULL if creation failed</td></tr></tbody></table></div></div> +<div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>conn</tt></i>:</span></td><td>pointer to hypervisor connection</td></tr><tr><td><span class="term"><i><tt>xml</tt></i>:</span></td><td>XML description for new pool</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td/></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>a <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> object, or NULL if creation failed</td></tr></tbody></table></div></div> <hr/> <div class="refsect2" lang="en"><h3><a name="virStoragePoolDelete"/>virStoragePoolDelete ()</h3><pre class="programlisting">int virStoragePoolDelete (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br/> unsigned int flags)<br/> -</pre><p>Delete the underlying pool resources. This is a non-recoverable operation.</p> +</pre><p>Delete the underlying pool resources. This is a non-recoverable operation. The <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> object itself is not free'd.</p> <div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td>flags for obliteration process</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 on success, or -1 if it could not be obliterate</td></tr></tbody></table></div></div> <hr/> <div class="refsect2" lang="en"><h3><a name="virStoragePoolDestroy"/>virStoragePoolDestroy ()</h3><pre class="programlisting">int virStoragePoolDestroy (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool)<br/> -</pre><p>Destroy an active storage pool. The <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> object should not be used after this method returns successfully as it has been free'd</p> +</pre><p>Destroy an active storage pool. This will deactivate the pool on the host, but keep any persistent config associated with it. If it has a persistent config it can later be restarted with virStoragePoolCreate(). This does not free the associated <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> object.</p> <div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 on success, or -1 if it could not be destroyed</td></tr></tbody></table></div></div> <hr/> <div class="refsect2" lang="en"><h3><a name="virStoragePoolFree"/>virStoragePoolFree ()</h3><pre class="programlisting">int virStoragePoolFree (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool)<br/> -</pre><p>Free a storage pool object</p> +</pre><p>Free a storage pool object, releasing all memory associated with it. Does not change the state of the pool on the host.</p> <div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 on success, or -1 if it could not be free'd.</td></tr></tbody></table></div></div> <hr/> <div class="refsect2" lang="en"><h3><a name="virStoragePoolGetAutostart"/>virStoragePoolGetAutostart ()</h3><pre class="programlisting">int virStoragePoolGetAutostart (<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br/> int * autostart)<br/> diff -r d043e3715bce include/libvirt/virterror.h --- a/include/libvirt/virterror.h Tue Feb 19 16:26:35 2008 -0500 +++ b/include/libvirt/virterror.h Tue Feb 19 16:59:10 2008 -0500 @@ -136,6 +136,8 @@ typedef enum { VIR_ERR_INVALID_STORAGE_POOL, /* invalid storage pool object */ VIR_ERR_INVALID_STORAGE_VOL, /* invalid storage vol object */ 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 */ } virErrorNumber; /** diff -r d043e3715bce po/POTFILES.in --- a/po/POTFILES.in Tue Feb 19 16:26:35 2008 -0500 +++ b/po/POTFILES.in Tue Feb 19 16:59:10 2008 -0500 @@ -10,6 +10,9 @@ src/qemu_conf.c src/qemu_conf.c src/qemu_driver.c src/remote_internal.c +src/storage_backend.c +src/storage_conf.c +src/storage_driver.c src/sexpr.c src/test.c src/uuid.c diff -r d043e3715bce src/Makefile.am --- a/src/Makefile.am Tue Feb 19 16:26:35 2008 -0500 +++ b/src/Makefile.am Tue Feb 19 16:59:10 2008 -0500 @@ -58,6 +58,9 @@ CLIENT_SOURCES = \ openvz_conf.c openvz_conf.h \ openvz_driver.c openvz_driver.h \ nodeinfo.h nodeinfo.c \ + storage_conf.h storage_conf.c \ + storage_driver.h storage_driver.c \ + storage_backend.h storage_backend.c \ util.c util.h SERVER_SOURCES = \ diff -r d043e3715bce src/libvirt.c --- a/src/libvirt.c Tue Feb 19 16:26:35 2008 -0500 +++ b/src/libvirt.c Tue Feb 19 16:59:10 2008 -0500 @@ -37,6 +37,7 @@ #include "xen_unified.h" #include "remote_internal.h" #include "qemu_driver.h" +#include "storage_driver.h" #ifdef WITH_OPENVZ #include "openvz_driver.h" #endif @@ -215,6 +216,7 @@ virInitialize(void) #ifdef WITH_OPENVZ if (openvzRegister() == -1) return -1; #endif + if (storageRegister() == -1) return -1; #ifdef WITH_REMOTE if (remoteRegister () == -1) return -1; #endif diff -r d043e3715bce src/storage_backend.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend.c Tue Feb 19 16:59:10 2008 -0500 @@ -0,0 +1,88 @@ +/* + * storage_backend.h: internal storage driver backend contract + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <string.h> +#include <regex.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "util.h" + +#include "storage_backend.h" + + +virStorageBackendPtr +virStorageBackendForType(int type) { + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("missing backend for pool type %d"), type); + return NULL; +} + +virStorageBackendPoolOptionsPtr +virStorageBackendPoolOptionsForType(int type) { + virStorageBackendPtr backend = virStorageBackendForType(type); + if (backend == NULL) + return NULL; + return &backend->poolOptions; +} + +virStorageBackendVolOptionsPtr +virStorageBackendVolOptionsForType(int type) { + virStorageBackendPtr backend = virStorageBackendForType(type); + if (backend == NULL) + return NULL; + return &backend->volOptions; +} + + +int +virStorageBackendFromString(const char *type) { + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("unknown storage backend type %s"), type); + return -1; +} + +const char * +virStorageBackendToString(int type) { + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("unknown storage backend type %d"), type); + return NULL; +} + + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r d043e3715bce src/storage_backend.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend.h Tue Feb 19 16:59:10 2008 -0500 @@ -0,0 +1,121 @@ +/* + * storage_backend.h: internal storage driver backend contract + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_BACKEND_H__ +#define __VIR_STORAGE_BACKEND_H__ + +#include <libvirt/libvirt.h> +#include "storage_conf.h" + + +typedef const char *(*virStorageVolFormatToString)(virConnectPtr conn, + int format); +typedef int (*virStorageVolFormatFromString)(virConnectPtr conn, + const char *format); + +typedef const char *(*virStoragePoolFormatToString)(virConnectPtr conn, + int format); +typedef int (*virStoragePoolFormatFromString)(virConnectPtr conn, + const char *format); + + +typedef struct _virStorageBackendVolOptions virStorageBackendVolOptions; +typedef virStorageBackendVolOptions *virStorageBackendVolOptionsPtr; +struct _virStorageBackendVolOptions { + virStorageVolFormatToString formatToString; + virStorageVolFormatFromString formatFromString; +}; + + +/* Flags to indicate mandatory components in the pool source */ +enum { + VIR_STORAGE_BACKEND_POOL_SOURCE_HOST = (1<<0), + VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE = (1<<1), + VIR_STORAGE_BACKEND_POOL_SOURCE_DIR = (1<<2), + VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER = (1<<3), +}; + +typedef struct _virStorageBackendPoolOptions virStorageBackendPoolOptions; +typedef virStorageBackendPoolOptions *virStorageBackendPoolOptionsPtr; +struct _virStorageBackendPoolOptions { + int flags; + virStoragePoolFormatToString formatToString; + virStoragePoolFormatFromString formatFromString; +}; + +typedef int (*virStorageBackendStartPool)(virConnectPtr conn, virStoragePoolObjPtr pool); +typedef int (*virStorageBackendBuildPool)(virConnectPtr conn, virStoragePoolObjPtr pool, unsigned int flags); +typedef int (*virStorageBackendRefreshPool)(virConnectPtr conn, virStoragePoolObjPtr pool); +typedef int (*virStorageBackendStopPool)(virConnectPtr conn, virStoragePoolObjPtr pool); +typedef int (*virStorageBackendDeletePool)(virConnectPtr conn, virStoragePoolObjPtr pool, unsigned int flags); + +typedef int (*virStorageBackendCreateVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol); +typedef int (*virStorageBackendRefreshVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol); +typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags); + + +typedef struct _virStorageBackend virStorageBackend; +typedef virStorageBackend *virStorageBackendPtr; + +struct _virStorageBackend { + int type; + + virStorageBackendStartPool startPool; + virStorageBackendBuildPool buildPool; + virStorageBackendRefreshPool refreshPool; + virStorageBackendStopPool stopPool; + virStorageBackendDeletePool deletePool; + + virStorageBackendCreateVol createVol; + virStorageBackendRefreshVol refreshVol; + virStorageBackendDeleteVol deleteVol; + + virStorageBackendPoolOptions poolOptions; + virStorageBackendVolOptions volOptions; + + int volType; +}; + + +virStorageBackendPtr virStorageBackendForType(int type); +virStorageBackendPoolOptionsPtr virStorageBackendPoolOptionsForType(int type); +virStorageBackendVolOptionsPtr virStorageBackendVolOptionsForType(int type); +int virStorageBackendFromString(const char *type); +const char *virStorageBackendToString(int type); + + +#endif /* __VIR_STORAGE_BACKEND_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r d043e3715bce src/storage_conf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_conf.c Tue Feb 19 16:59:10 2008 -0500 @@ -0,0 +1,1251 @@ +/* + * storage_conf.c: config handling for storage driver + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <libvirt/libvirt.h> +#include <libvirt/virterror.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/uri.h> + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> + +#include "storage_conf.h" +#include "storage_backend.h" +#include "xml.h" +#include "uuid.h" +#include "buf.h" +#include "util.h" + +#define virStorageLog(msg...) fprintf(stderr, msg) + +void +virStorageReportError(virConnectPtr conn, int code, const char *fmt, ...) { + va_list args; + char errorMessage[1024]; + + if (fmt) { + va_start(args, fmt); + vsnprintf(errorMessage, sizeof(errorMessage)-1, fmt, args); + va_end(args); + } else { + errorMessage[0] = '\0'; + } + virStorageLog("%s", errorMessage); + __virRaiseError(conn, NULL, NULL, VIR_FROM_STORAGE, code, VIR_ERR_ERROR, + NULL, NULL, NULL, -1, -1, "%s", errorMessage); +} + + + +void +virStorageVolDefFree(virStorageVolDefPtr def) { + int i; + free(def->name); + free(def->key); + + for (i = 0 ; i < def->source.nextent ; i++) { + free(def->source.extents[i].path); + } + free(def->source.extents); + + free(def->target.path); + free(def->target.perms.label); + free(def); +} + +void +virStoragePoolDefFree(virStoragePoolDefPtr def) { + int i; + + free(def->name); + free(def->source.host.name); + for (i = 0 ; i < def->source.ndevice ; i++) { + free(def->source.devices[i].freeExtents); + free(def->source.devices[i].path); + } + free(def->source.devices); + free(def->source.dir); + + if (def->source.authType == VIR_STORAGE_POOL_AUTH_CHAP) { + free(def->source.auth.chap.login); + free(def->source.auth.chap.passwd); + } + + free(def->target.path); + free(def->target.perms.label); + free(def); +} + + +void +virStoragePoolObjFree(virStoragePoolObjPtr obj) { + if (obj->def) + virStoragePoolDefFree(obj->def); + if (obj->newDef) + virStoragePoolDefFree(obj->newDef); + + free(obj->configFile); + free(obj->autostartLink); + free(obj); +} + +void +virStoragePoolObjRemove(virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool) +{ + virStoragePoolObjPtr prev = NULL, curr; + + curr = driver->pools; + while (curr != pool) { + prev = curr; + curr = curr->next; + } + + if (curr) { + if (prev) + prev->next = curr->next; + else + driver->pools = curr->next; + + driver->ninactivePools--; + } + + virStoragePoolObjFree(pool); +} + + +static int +virStoragePoolDefParseAuthChap(virConnectPtr conn, + xmlXPathContextPtr ctxt, + virStoragePoolAuthChapPtr auth) { + auth->login = virXPathString("string(/pool/source/auth/@login)", ctxt); + if (auth->login == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing auth host attribute")); + return -1; + } + + auth->passwd = virXPathString("string(/pool/source/auth/@passwd)", ctxt); + if (auth->passwd == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing auth passwd attribute")); + return -1; + } + + return 0; +} + + +static int +virStoragePoolDefParsePerms(virConnectPtr conn, + xmlXPathContextPtr ctxt, + virStoragePermsPtr perms) { + char *mode; + long v; + + mode = virXPathString("string(/pool/permissions/mode)", ctxt); + if (!mode) { + perms->mode = 0700; + } else { + char *end; + perms->mode = strtol(mode, &end, 8); + if (*end || perms->mode < 0 || perms->mode > 0777) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("malformed octal mode")); + return -1; + } + } + + if (virXPathNode("/pool/permissions/owner", ctxt) == NULL) { + perms->uid = getuid(); + } else { + if (virXPathLong("number(/pool/permissions/owner)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("malformed owner element")); + return -1; + } + perms->uid = (int)v; + } + + if (virXPathNode("/pool/permissions/group", ctxt) == NULL) { + perms->uid = getgid(); + } else { + if (virXPathLong("number(/pool/permissions/group)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("malformed group element")); + return -1; + } + perms->gid = (int)v; + } + + /* NB, we're ignoring missing labels here - they'll simply inherit */ + perms->label = virXPathString("string(/pool/permissions/label)", ctxt); + + return 0; +} + + +static virStoragePoolDefPtr +virStoragePoolDefParseDoc(virConnectPtr conn, + xmlXPathContextPtr ctxt, + xmlNodePtr root) { + virStorageBackendPoolOptionsPtr options; + virStoragePoolDefPtr ret; + xmlChar *type = NULL; + char *uuid = NULL; + char *authType = NULL; + + if ((ret = calloc(1, sizeof(virStoragePoolDef))) == NULL) + return NULL; + + if (STRNEQ((const char *)root->name, "pool")) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("unknown root element")); + goto cleanup; + } + + type = xmlGetProp(root, BAD_CAST "type"); + if ((ret->type = virStorageBackendFromString((const char *)type)) < 0) + goto cleanup; + xmlFree(type); + type = NULL; + + if ((options = virStorageBackendPoolOptionsForType(ret->type)) == NULL) { + goto cleanup; + } + + if ((ret->name = virXPathString("string(/pool/name)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing name element")); + goto cleanup; + } + + uuid = virXPathString("string(/pool/uuid)", ctxt); + if (uuid == NULL) { + if (virUUIDGenerate(ret->uuid) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unable to generate uuid")); + goto cleanup; + } + } else { + if (virUUIDParse(uuid, ret->uuid) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("malformed uuid element")); + goto cleanup; + } + free(uuid); + uuid = NULL; + } + + if (options->formatFromString) { + char *format = virXPathString("string(/pool/source/format/@type)", ctxt); + if ((ret->source.format = (options->formatFromString)(conn, format)) < 0) { + free(format); + goto cleanup; + } + free(format); + } + + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_HOST) { + if ((ret->source.host.name = virXPathString("string(/pool/source/host/@name)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing source host name")); + goto cleanup; + } + } + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) { + xmlNodePtr *nodeset = NULL; + int nsource, i; + + if ((nsource = virXPathNodeSet("/pool/source/device", ctxt, &nodeset)) <= 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("cannot extract source devices")); + goto cleanup; + } + if ((ret->source.devices = calloc(nsource, sizeof(*ret->source.devices))) == NULL) { + free(nodeset); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("device")); + goto cleanup; + } + for (i = 0 ; i < nsource ; i++) { + xmlChar *path = xmlGetProp(nodeset[i], BAD_CAST "path"); + if (path == NULL) { + free(nodeset); + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing source device path")); + goto cleanup; + } + ret->source.devices[i].path = (char *)path; + } + free(nodeset); + ret->source.ndevice = nsource; + } + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DIR) { + if ((ret->source.dir = virXPathString("string(/pool/source/dir/@path)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing source path")); + goto cleanup; + } + } + + + authType = virXPathString("string(/pool/source/auth/@type)", ctxt); + if (authType == NULL) { + ret->source.authType = VIR_STORAGE_POOL_AUTH_NONE; + } else { + if (STREQ(authType, "chap")) { + ret->source.authType = VIR_STORAGE_POOL_AUTH_CHAP; + } else { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("unknown auth type '%s'"), + (const char *)authType); + free(authType); + authType = NULL; + goto cleanup; + } + free(authType); + authType = NULL; + } + + if (ret->source.authType == VIR_STORAGE_POOL_AUTH_CHAP) { + if (virStoragePoolDefParseAuthChap(conn, ctxt, &ret->source.auth.chap) < 0) + goto cleanup; + } + + if ((ret->target.path = virXPathString("string(/pool/target/path)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing target path")); + goto cleanup; + } + + if (virStoragePoolDefParsePerms(conn, ctxt, &ret->target.perms) < 0) + goto cleanup; + + return ret; + + cleanup: + free(uuid); + if (type) + xmlFree(type); + virStoragePoolDefFree(ret); + return NULL; +} + +virStoragePoolDefPtr +virStoragePoolDefParse(virConnectPtr conn, + const char *xmlStr, + const char *filename) { + virStoragePoolDefPtr ret = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr ctxt = NULL; + + if (!(xml = xmlReadDoc(BAD_CAST xmlStr, + filename ? filename : "storage.xml", + NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("malformed xml document")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("xmlXPathContext")); + goto cleanup; + } + + node = xmlDocGetRootElement(xml); + if (node == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing root element")); + goto cleanup; + } + + ret = virStoragePoolDefParseDoc(conn, ctxt, node); + + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + + return ret; + + cleanup: + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + return NULL; +} + + +char * +virStoragePoolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr def) { + virStorageBackendPoolOptionsPtr options; + virBufferPtr buf; + const char *type; + char uuid[VIR_UUID_STRING_BUFLEN]; + int i; + + options = virStorageBackendPoolOptionsForType(def->type); + if (options == NULL) + return NULL; + + if ((buf = virBufferNew(8192)) == NULL) + goto no_memory; + + type = virStorageBackendToString(def->type); + if (!type) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected pool type")); + goto cleanup; + } + if (virBufferVSprintf(buf, "<pool type='%s'>\n", type) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <name>%s</name>\n", def->name) < 0) + goto no_memory; + + virUUIDFormat(def->uuid, uuid); + if (virBufferVSprintf(buf," <uuid>%s</uuid>\n", uuid) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <capacity>%llu</capacity>\n", + def->capacity) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <allocation>%llu</allocation>\n", + def->allocation) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <available>%llu</available>\n", + def->available) < 0) + goto no_memory; + + + if (virBufferAddLit(buf," <source>\n") < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_HOST) && + def->source.host.name && + virBufferVSprintf(buf," <host name='%s'/>\n", def->source.host.name) < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) && + def->source.ndevice) { + for (i = 0 ; i < def->source.ndevice ; i++) { + if (virBufferVSprintf(buf," <device path='%s'>\n", def->source.devices[i].path) < 0) + goto no_memory; + if (def->source.devices[i].nfreeExtent) { + int j; + for (j = 0 ; j < def->source.devices[i].nfreeExtent ; j++) { + if (virBufferVSprintf(buf, " <freeExtent start='%llu' end='%llu'/>\n", + def->source.devices[i].freeExtents[j].start, + def->source.devices[i].freeExtents[j].end) < 0) + goto no_memory; + } + } + if (virBufferAddLit(buf," </device>\n") < 0) + goto no_memory; + } + } + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DIR) && + def->source.dir && + virBufferVSprintf(buf," <directory path='%s'/>\n", def->source.dir) < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER) && + def->source.adapter && + virBufferVSprintf(buf," <adapter name='%s'/>\n", def->source.adapter) < 0) + goto no_memory; + + if (options->formatToString) { + const char *format = (options->formatToString)(conn, def->source.format); + if (!format) + goto cleanup; + if (virBufferVSprintf(buf," <format type='%s'/>\n", format) < 0) + goto no_memory; + } + + + if (def->source.authType == VIR_STORAGE_POOL_AUTH_CHAP && + virBufferVSprintf(buf," <auth type='chap' login='%s' passwd='%s'>\n", + def->source.auth.chap.login, + def->source.auth.chap.passwd) < 0) + goto no_memory; + if (virBufferAddLit(buf," </source>\n") < 0) + goto no_memory; + + + + if (virBufferAddLit(buf," <target>\n") < 0) + goto no_memory; + + if (def->target.path && + virBufferVSprintf(buf," <path>%s</path>\n", def->target.path) < 0) + goto no_memory; + + if (virBufferAddLit(buf," <permissions>\n") < 0) + goto no_memory; + if (virBufferVSprintf(buf," <mode>0%o</mode>\n", + def->target.perms.mode) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <owner>%d</owner>\n", + def->target.perms.uid) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <group>%d</group>\n", + def->target.perms.gid) < 0) + goto no_memory; + + if (def->target.perms.label) { + if (virBufferVSprintf(buf," <label>%s</label>\n", + def->target.perms.label) < 0) + goto no_memory; + } + if (virBufferAddLit(buf," </permissions>\n") < 0) + goto no_memory; + if (virBufferAddLit(buf," </target>\n") < 0) + goto no_memory; + + if (virBufferAddLit(buf,"</pool>\n") < 0) + goto no_memory; + + return virBufferContentAndFree(buf); + + no_memory: + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("xml")); + cleanup: + virBufferFree(buf); + return NULL; +} + + +static int +virStorageVolDefParsePerms(virConnectPtr conn, + xmlXPathContextPtr ctxt, + virStoragePermsPtr perms) { + char *mode; + long v; + + mode = virXPathString("string(/volume/permissions/mode)", ctxt); + if (!mode) { + perms->mode = 0600; + } else { + char *end = NULL; + perms->mode = strtol(mode, &end, 8); + if (*end || perms->mode < 0 || perms->mode > 0777) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("malformed octal mode")); + return -1; + } + } + + if (virXPathNode("/volume/permissions/owner", ctxt) == NULL) { + perms->uid = getuid(); + } else { + if (virXPathLong("number(/volume/permissions/owner)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing owner element")); + return -1; + } + perms->uid = (int)v; + } + if (virXPathNode("/volume/permissions/group", ctxt) == NULL) { + perms->gid = getgid(); + } else { + if (virXPathLong("number(/volume/permissions/group)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing owner element")); + return -1; + } + perms->gid = (int)v; + } + + /* NB, we're ignoring missing labels here - they'll simply inherit */ + perms->label = virXPathString("string(/volume/permissions/label)", ctxt); + + return 0; +} + + +static int +virStorageSize(virConnectPtr conn, + const char *unit, + const char *val, + unsigned long long *ret) { + unsigned long long mult; + char *end; + + if (!unit) { + mult = 1; + } else { + switch (unit[0]) { + case 'k': + case 'K': + mult = 1024ull; + break; + + case 'm': + case 'M': + mult = 1024ull * 1024ull; + break; + + case 'g': + case 'G': + mult = 1024ull * 1024ull * 1024ull; + break; + + case 't': + case 'T': + mult = 1024ull * 1024ull * 1024ull * 1024ull; + break; + + case 'p': + case 'P': + mult = 1024ull * 1024ull * 1024ull * 1024ull * 1024ull; + break; + + case 'y': + case 'Y': + mult = 1024ull * 1024ull * 1024ull * 1024ull * 1024ull * + 1024ull; + break; + + case 'z': + case 'Z': + mult = 1024ull * 1024ull * 1024ull * 1024ull * 1024ull * + 1024ull * 1024ull; + break; + + default: + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("unknown size units '%s'"), unit); + return -1; + } + } + + if (virStrToLong_ull (val, &end, 10, ret) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("malformed capacity element")); + return -1; + } + if (*ret > (ULLONG_MAX / mult)) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("capacity element value too large")); + return -1; + } + + *ret *= mult; + + return 0; +} + +static virStorageVolDefPtr +virStorageVolDefParseDoc(virConnectPtr conn, + virStoragePoolDefPtr pool, + xmlXPathContextPtr ctxt, + xmlNodePtr root) { + virStorageVolDefPtr ret; + virStorageBackendVolOptionsPtr options; + char *allocation = NULL; + char *capacity = NULL; + char *unit = NULL; + + options = virStorageBackendVolOptionsForType(pool->type); + if (options == NULL) + return NULL; + + if ((ret = calloc(1, sizeof(virStorageVolDef))) == NULL) + return NULL; + + if (STRNEQ((const char *)root->name, "volume")) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("unknown root element")); + goto cleanup; + } + + ret->name = virXPathString("string(/volume/name)", ctxt); + if (ret->name == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing name element")); + goto cleanup; + } + + /* Auto-generated so delibrately ignore */ + /*ret->key = virXPathString("string(/volume/key)", ctxt);*/ + + capacity = virXPathString("string(/volume/capacity)", ctxt); + unit = virXPathString("string(/volume/capacity/@unit)", ctxt); + if (capacity == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing capacity element")); + goto cleanup; + } + if (virStorageSize(conn, unit, capacity, &ret->capacity) < 0) + goto cleanup; + free(capacity); + capacity = NULL; + free(unit); + unit = NULL; + + allocation = virXPathString("string(/volume/allocation)", ctxt); + if (allocation) { + unit = virXPathString("string(/volume/allocation/@unit)", ctxt); + if (virStorageSize(conn, unit, allocation, &ret->allocation) < 0) + goto cleanup; + free(allocation); + allocation = NULL; + free(unit); + unit = NULL; + } else { + ret->allocation = ret->capacity; + } + + ret->target.path = virXPathString("string(/volume/target/path)", ctxt); + if (options->formatFromString) { + char *format = virXPathString("string(/volume/target/format/@type)", ctxt); + if ((ret->target.format = (options->formatFromString)(conn, format)) < 0) { + free(format); + goto cleanup; + } + free(format); + } + + if (virStorageVolDefParsePerms(conn, ctxt, &ret->target.perms) < 0) + goto cleanup; + + return ret; + + cleanup: + free(allocation); + free(capacity); + free(unit); + virStorageVolDefFree(ret); + return NULL; +} + + +virStorageVolDefPtr +virStorageVolDefParse(virConnectPtr conn, + virStoragePoolDefPtr pool, + const char *xmlStr, + const char *filename) { + virStorageVolDefPtr ret = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr ctxt = NULL; + + if (!(xml = xmlReadDoc(BAD_CAST xmlStr, filename ? filename : "storage.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("malformed xml document")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("xmlXPathContext")); + goto cleanup; + } + + node = xmlDocGetRootElement(xml); + if (node == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("missing root element")); + goto cleanup; + } + + ret = virStorageVolDefParseDoc(conn, pool, ctxt, node); + + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + + return ret; + + cleanup: + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + return NULL; +} + + + +char * +virStorageVolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr pool, + virStorageVolDefPtr def) { + virStorageBackendVolOptionsPtr options; + virBufferPtr buf = virBufferNew(8192); + + options = virStorageBackendVolOptionsForType(pool->type); + if (options == NULL) + return NULL; + + if (!buf) + goto no_memory; + + if (virBufferAddLit(buf, "<volume>\n") < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <name>%s</name>\n", def->name) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <key>%s</key>\n", def->key) < 0) + goto no_memory; + + if (virBufferAddLit(buf, " <source>\n") < 0) + goto no_memory; + if (def->source.nextent) { + int i; + const char *thispath = NULL; + for (i = 0 ; i < def->source.nextent ; i++) { + if (thispath == NULL || + STRNEQ(thispath, def->source.extents[i].path)) { + if (thispath != NULL) + if (virBufferAddLit(buf, " </device>\n") < 0) + goto no_memory; + if (virBufferVSprintf(buf, " <device path='%s'>\n", + def->source.extents[i].path) < 0) + goto no_memory; + } + + if (virBufferVSprintf(buf, + " <extent start='%llu' end='%llu'/>\n", + def->source.extents[i].start, + def->source.extents[i].end) < 0) + goto no_memory; + thispath = def->source.extents[i].path; + } + if (thispath != NULL) + if (virBufferAddLit(buf, " </device>\n") < 0) + goto no_memory; + } + if (virBufferAddLit(buf, " </source>\n") < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <capacity>%llu</capacity>\n", + def->capacity) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <allocation>%llu</allocation>\n", + def->allocation) < 0) + goto no_memory; + + if (virBufferAddLit(buf, " <target>\n") < 0) + goto no_memory; + + if (def->target.path && + virBufferVSprintf(buf," <path>%s</path>\n", def->target.path) < 0) + goto no_memory; + + if (options->formatToString) { + const char *format = (options->formatToString)(conn, + def->target.format); + if (!format) + goto cleanup; + if (virBufferVSprintf(buf," <format type='%s'/>\n", format) < 0) + goto no_memory; + } + + if (virBufferAddLit(buf," <permissions>\n") < 0) + goto no_memory; + if (virBufferVSprintf(buf," <mode>0%o</mode>\n", + def->target.perms.mode) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <owner>%d</owner>\n", + def->target.perms.uid) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <group>%d</group>\n", + def->target.perms.gid) < 0) + goto no_memory; + + if (def->target.perms.label && + virBufferVSprintf(buf," <label>%s</label>\n", + def->target.perms.label) < 0) + goto no_memory; + if (virBufferAddLit(buf," </permissions>\n") < 0) + goto no_memory; + + if (virBufferAddLit(buf, " </target>\n") < 0) + goto no_memory; + + if (virBufferAddLit(buf,"</volume>\n") < 0) + goto no_memory; + + return virBufferContentAndFree(buf); + + no_memory: + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("xml")); + cleanup: + virBufferFree(buf); + return NULL; +} + + +virStoragePoolObjPtr +virStoragePoolObjFindByUUID(virStorageDriverStatePtr driver, + const unsigned char *uuid) { + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (!memcmp(pool->def->uuid, uuid, VIR_UUID_BUFLEN)) + return pool; + pool = pool->next; + } + + return NULL; +} + +virStoragePoolObjPtr +virStoragePoolObjFindByName(virStorageDriverStatePtr driver, + const char *name) { + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (STREQ(pool->def->name, name)) + return pool; + pool = pool->next; + } + + return NULL; +} + +void +virStoragePoolObjClearVols(virStoragePoolObjPtr pool) +{ + virStorageVolDefPtr vol = pool->volumes; + while (vol) { + virStorageVolDefPtr next = vol->next; + virStorageVolDefFree(vol); + vol = next; + } + pool->volumes = NULL; + pool->nvolumes = 0; +} + +virStorageVolDefPtr +virStorageVolDefFindByKey(virStoragePoolObjPtr pool, + const char *key) { + virStorageVolDefPtr vol = pool->volumes; + + while (vol) { + if (STREQ(vol->key, key)) + return vol; + vol = vol->next; + } + + return NULL; +} + +virStorageVolDefPtr +virStorageVolDefFindByPath(virStoragePoolObjPtr pool, + const char *path) { + virStorageVolDefPtr vol = pool->volumes; + + while (vol) { + if (STREQ(vol->target.path, path)) + return vol; + vol = vol->next; + } + + return NULL; +} + +virStorageVolDefPtr +virStorageVolDefFindByName(virStoragePoolObjPtr pool, + const char *name) { + virStorageVolDefPtr vol = pool->volumes; + + while (vol) { + if (STREQ(vol->name, name)) + return vol; + vol = vol->next; + } + + return NULL; +} + +virStoragePoolObjPtr +virStoragePoolObjAssignDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolDefPtr def) { + virStoragePoolObjPtr pool; + + if ((pool = virStoragePoolObjFindByName(driver, def->name))) { + if (!virStoragePoolObjIsActive(pool)) { + virStoragePoolDefFree(pool->def); + pool->def = def; + } else { + if (pool->newDef) + virStoragePoolDefFree(pool->newDef); + pool->newDef = def; + } + return pool; + } + + if (!(pool = calloc(1, sizeof(virStoragePoolObj)))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("pool")); + return NULL; + } + + pool->active = 0; + pool->def = def; + pool->next = driver->pools; + + driver->pools = pool; + driver->ninactivePools++; + + return pool; +} + +static virStoragePoolObjPtr +virStoragePoolObjLoad(virStorageDriverStatePtr driver, + const char *file, + const char *path, + const char *xml, + const char *autostartLink) { + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + + if (!(def = virStoragePoolDefParse(NULL, xml, file))) { + virErrorPtr err = virGetLastError(); + virStorageLog("Error parsing storage pool config '%s' : %s", + path, err->message); + return NULL; + } + + if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { + virStorageLog("Storage pool config filename '%s' does not match pool name '%s'", + path, def->name); + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(NULL, driver, def))) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + + pool->configFile = strdup(path); + if (pool->configFile == NULL) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + pool->autostartLink = strdup(autostartLink); + if (pool->autostartLink == NULL) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + + pool->autostart = virFileLinkPointsTo(pool->autostartLink, + pool->configFile); + + return pool; +} + + +int +virStoragePoolObjScanConfigs(virStorageDriverStatePtr driver) { + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(driver->configDir))) { + if (errno == ENOENT) + return 0; + virStorageLog("Failed to open dir '%s': %s", + driver->configDir, strerror(errno)); + return -1; + } + + while ((entry = readdir(dir))) { + char *xml = NULL; + char path[PATH_MAX]; + char autostartLink[PATH_MAX]; + + if (entry->d_name[0] == '.') + continue; + + if (!virFileHasSuffix(entry->d_name, ".xml")) + continue; + + if (virFileBuildPath(driver->configDir, entry->d_name, + NULL, path, PATH_MAX) < 0) { + virStorageLog("Config filename '%s/%s' is too long", + driver->configDir, entry->d_name); + continue; + } + + if (virFileBuildPath(driver->autostartDir, entry->d_name, + NULL, autostartLink, PATH_MAX) < 0) { + virStorageLog("Autostart link path '%s/%s' is too long", + driver->autostartDir, entry->d_name); + continue; + } + + if (virFileReadAll(path, 8192, &xml) < 0) + continue; + + virStoragePoolObjLoad(driver, entry->d_name, path, xml, autostartLink); + + free(xml); + } + + closedir(dir); + + return 0; +} + +int +virStoragePoolObjSaveDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool, + virStoragePoolDefPtr def) { + char *xml; + int fd = -1, ret = -1; + ssize_t towrite; + + if (!pool->configFile) { + int err; + char path[PATH_MAX]; + + if ((err = virFileMakePath(driver->configDir))) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create config directory %s: %s"), + driver->configDir, strerror(err)); + return -1; + } + + if (virFileBuildPath(driver->configDir, def->name, ".xml", + path, sizeof(path)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot construct config file path")); + return -1; + } + if (!(pool->configFile = strdup(path))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("configFile")); + return -1; + } + + if (virFileBuildPath(driver->autostartDir, def->name, ".xml", + path, sizeof(path)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot construct autostart link path")); + free(pool->configFile); + pool->configFile = NULL; + return -1; + } + if (!(pool->autostartLink = strdup(path))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("config file")); + free(pool->configFile); + pool->configFile = NULL; + return -1; + } + } + + if (!(xml = virStoragePoolDefFormat(conn, def))) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to generate XML")); + return -1; + } + + if ((fd = open(pool->configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create config file %s: %s"), + pool->configFile, strerror(errno)); + goto cleanup; + } + + towrite = strlen(xml); + if (safewrite(fd, xml, towrite) != towrite) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot write config file %s: %s"), + pool->configFile, strerror(errno)); + goto cleanup; + } + + if (close(fd) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot save config file %s: %s"), + pool->configFile, strerror(errno)); + goto cleanup; + } + + ret = 0; + + cleanup: + if (fd != -1) + close(fd); + + free(xml); + + return ret; +} + +int +virStoragePoolObjDeleteDef(virConnectPtr conn, + virStoragePoolObjPtr pool) { + if (!pool->configFile) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("no config file for %s"), pool->def->name); + return -1; + } + + if (unlink(pool->configFile) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot remove config for %s"), + pool->def->name); + return -1; + } + + return 0; +} + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r d043e3715bce src/storage_conf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_conf.h Tue Feb 19 16:59:10 2008 -0500 @@ -0,0 +1,314 @@ +/* + * storage_conf.h: config handling for storage driver + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_CONF_H__ +#define __VIR_STORAGE_CONF_H__ + +#include <libvirt/libvirt.h> +#include "internal.h" + +/* Shared structs */ + + + +typedef struct _virStoragePerms virStoragePerms; +typedef virStoragePerms *virStoragePermsPtr; +struct _virStoragePerms { + int mode; + int uid; + int gid; + char *label; +}; + +/* Storage volumes */ + + +/* + * How the volume's data is stored on underlying + * physical devices - can potentially span many + * devices in LVM case. + */ +typedef struct _virStorageVolSourceExtent virStorageVolSourceExtent; +typedef virStorageVolSourceExtent *virStorageVolSourceExtentPtr; +struct _virStorageVolSourceExtent { + char *path; + unsigned long long start; + unsigned long long end; +}; + +typedef struct _virStorageVolSource virStorageVolSource; +typedef virStorageVolSource *virStorageVolSourcePtr; +struct _virStorageVolSource { + int nextent; + virStorageVolSourceExtentPtr extents; +}; + + +/* + * How the volume appears on the host + */ +typedef struct _virStorageVolTarget virStorageVolTarget; +typedef virStorageVolTarget *virStorageVolTargetPtr; +struct _virStorageVolTarget { + char *path; + int format; + virStoragePerms perms; +}; + + +typedef struct _virStorageVolDef virStorageVolDef; +typedef virStorageVolDef *virStorageVolDefPtr; +struct _virStorageVolDef { + char *name; + char *key; + + unsigned long long allocation; + unsigned long long capacity; + + virStorageVolSource source; + virStorageVolTarget target; + + virStorageVolDefPtr next; +}; + + + + +/* Storage pools */ + +enum virStoragePoolType { + VIR_STORAGE_POOL_DIR = 1, /* Local directory */ + VIR_STORAGE_POOL_FS, /* Local filesystem */ + VIR_STORAGE_POOL_NETFS, /* Networked filesystem - eg NFS, GFS, etc */ + VIR_STORAGE_POOL_LOGICAL, /* Logical volume groups / volumes */ + VIR_STORAGE_POOL_DISK, /* Disk partitions */ + VIR_STORAGE_POOL_ISCSI, /* iSCSI targets */ + VIR_STORAGE_POOL_SCSI, /* SCSI HBA */ +}; + + +enum virStoragePoolAuthType { + VIR_STORAGE_POOL_AUTH_NONE, + VIR_STORAGE_POOL_AUTH_CHAP, +}; + +typedef struct _virStoragePoolAuthChap virStoragePoolAuthChap; +typedef virStoragePoolAuthChap *virStoragePoolAuthChapPtr; +struct _virStoragePoolAuthChap { + char *login; + char *passwd; +}; + + +/* + * For remote pools, info on how to reach the host + */ +typedef struct _virStoragePoolSourceHost virStoragePoolSourceHost; +typedef virStoragePoolSourceHost *virStoragePoolSourceHostPtr; +struct _virStoragePoolSourceHost { + char *name; + int port; + int protocol; +}; + + +/* + * Available extents on the underlying storage + */ +typedef struct _virStoragePoolSourceDeviceExtent virStoragePoolSourceDeviceExtent; +typedef virStoragePoolSourceDeviceExtent *virStoragePoolSourceDeviceExtentPtr; +struct _virStoragePoolSourceDeviceExtent { + unsigned long long start; + unsigned long long end; +}; + + +/* + * Pools can be backed by one or more devices, and some + * allow us to track free space on underlying devices. + */ +typedef struct _virStoragePoolSourceDevice virStoragePoolSourceDevice; +typedef virStoragePoolSourceDevice *virStoragePoolSourceDevicePtr; +struct _virStoragePoolSourceDevice { + int nfreeExtent; + virStoragePoolSourceDeviceExtentPtr freeExtents; + char *path; + int format; /* Pool specific source format */ +}; + + + +typedef struct _virStoragePoolSource virStoragePoolSource; +typedef virStoragePoolSource *virStoragePoolSourcePtr; +struct _virStoragePoolSource { + /* An optional host */ + virStoragePoolSourceHost host; + + /* And either one or more devices ... */ + int ndevice; + virStoragePoolSourceDevicePtr devices; + + /* Or a directory */ + char *dir; + + /* Or an adapter */ + char *adapter; + + int authType; /* virStoragePoolAuthType */ + union { + virStoragePoolAuthChap chap; + } auth; + + int format; /* Pool type specific format such as filesystem type, or lvm version, etc */ +}; + + +typedef struct _virStoragePoolTarget virStoragePoolTarget; +typedef virStoragePoolTarget *virStoragePoolTargetPtr; +struct _virStoragePoolTarget { + char *path; /* Optional local filesystem mapping */ + virStoragePerms perms; /* Default permissions for volumes */ +}; + + +typedef struct _virStoragePoolDef virStoragePoolDef; +typedef virStoragePoolDef *virStoragePoolDefPtr; +struct _virStoragePoolDef { + /* General metadata */ + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; + int type; /* virStoragePoolType */ + + unsigned long long allocation; + unsigned long long capacity; + unsigned long long available; + + virStoragePoolSource source; + virStoragePoolTarget target; +}; + +typedef struct _virStoragePoolObj virStoragePoolObj; +typedef virStoragePoolObj *virStoragePoolObjPtr; + +struct _virStoragePoolObj { + char *configFile; + char *autostartLink; + int active; + int autostart; + + virStoragePoolDefPtr def; + virStoragePoolDefPtr newDef; + + int nvolumes; + virStorageVolDefPtr volumes; + + virStoragePoolObjPtr next; +}; + + + + +typedef struct _virStorageDriverState virStorageDriverState; +typedef virStorageDriverState *virStorageDriverStatePtr; + +struct _virStorageDriverState { + int nactivePools; + int ninactivePools; + virStoragePoolObjPtr pools; + char *configDir; + char *autostartDir; +}; + + +static inline int virStoragePoolObjIsActive(virStoragePoolObjPtr pool) { + return pool->active; +} + +void virStorageReportError(virConnectPtr conn, + int code, + const char *fmt, ...) + ATTRIBUTE_FORMAT(printf, 3, 4); + +int virStoragePoolObjScanConfigs(virStorageDriverStatePtr driver); + +virStoragePoolObjPtr virStoragePoolObjFindByUUID(virStorageDriverStatePtr driver, + const unsigned char *uuid); +virStoragePoolObjPtr virStoragePoolObjFindByName(virStorageDriverStatePtr driver, + const char *name); + +virStorageVolDefPtr virStorageVolDefFindByKey(virStoragePoolObjPtr pool, + const char *key); +virStorageVolDefPtr virStorageVolDefFindByPath(virStoragePoolObjPtr pool, + const char *path); +virStorageVolDefPtr virStorageVolDefFindByName(virStoragePoolObjPtr pool, + const char *name); + +void virStoragePoolObjClearVols(virStoragePoolObjPtr pool); + +virStoragePoolDefPtr virStoragePoolDefParse(virConnectPtr conn, + const char *xml, + const char *filename); +char *virStoragePoolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr def); + +virStorageVolDefPtr virStorageVolDefParse(virConnectPtr conn, + virStoragePoolDefPtr pool, + const char *xml, + const char *filename); +char *virStorageVolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr pool, + virStorageVolDefPtr def); + +virStoragePoolObjPtr virStoragePoolObjAssignDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolDefPtr def); + +int virStoragePoolObjSaveDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool, + virStoragePoolDefPtr def); +int virStoragePoolObjDeleteDef(virConnectPtr conn, + virStoragePoolObjPtr pool); + +void virStorageVolDefFree(virStorageVolDefPtr def); +void virStoragePoolDefFree(virStoragePoolDefPtr def); +void virStoragePoolObjFree(virStoragePoolObjPtr pool); +void virStoragePoolObjRemove(virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool); + +#endif /* __VIR_STORAGE_DRIVER_H__ */ + + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r d043e3715bce src/storage_driver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_driver.c Tue Feb 19 16:59:10 2008 -0500 @@ -0,0 +1,1261 @@ +/* + * storage_driver.c: core driver for storage APIs + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <errno.h> +#include <string.h> + +#include "driver.h" +#include "util.h" +#include "storage_driver.h" +#include "storage_conf.h" + +#include "storage_backend.h" + +#define storageLog(msg...) fprintf(stderr, msg) + +static virStorageDriverStatePtr driverState; + +static int storageDriverShutdown(void); + + +static void +storageDriverAutostart(virStorageDriverStatePtr driver) { + virStoragePoolObjPtr pool; + + pool = driver->pools; + while (pool != NULL) { + virStoragePoolObjPtr next = pool->next; + + if (pool->autostart && + !virStoragePoolObjIsActive(pool)) { + virStorageBackendPtr backend; + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + storageLog("Missing backend %d", + pool->def->type); + pool = next; + continue; + } + + if (backend->startPool && + backend->startPool(NULL, pool) < 0) { + virErrorPtr err = virGetLastError(); + storageLog("Failed to autostart storage pool '%s': %s", + pool->def->name, err ? err->message : NULL); + pool = next; + continue; + } + + if (backend->refreshPool(NULL, pool) < 0) { + virErrorPtr err = virGetLastError(); + if (backend->stopPool) + backend->stopPool(NULL, pool); + storageLog("Failed to autostart storage pool '%s': %s", + pool->def->name, err ? err->message : NULL); + pool = next; + continue; + } + pool->active = 1; + driver->nactivePools++; + driver->ninactivePools--; + } + + pool = next; + } +} + +/** + * virStorageStartup: + * + * Initialization function for the QEmu daemon + */ +static int +storageDriverStartup(void) { + uid_t uid = geteuid(); + struct passwd *pw; + char *base = NULL; + char driverConf[PATH_MAX]; + + if (!(driverState = calloc(1, sizeof(virStorageDriverState)))) { + return -1; + } + + if (!uid) { + if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) + goto out_of_memory; + } else { + if (!(pw = getpwuid(uid))) { + storageLog("Failed to find user record for uid '%d': %s", + uid, strerror(errno)); + goto out_of_memory; + } + + if (asprintf (&base, "%s/.libvirt", pw->pw_dir) == -1) { + storageLog("out of memory in asprintf"); + goto out_of_memory; + } + } + + /* Configuration paths are either ~/.libvirt/storage/... (session) or + * /etc/libvirt/storage/... (system). + */ + if (snprintf (driverConf, sizeof(driverConf), + "%s/storage.conf", base) == -1) + goto out_of_memory; + driverConf[sizeof(driverConf)-1] = '\0'; + + if (asprintf (&driverState->configDir, + "%s/storage", base) == -1) + goto out_of_memory; + + if (asprintf (&driverState->autostartDir, + "%s/storage/autostart", base) == -1) + goto out_of_memory; + + free(base); + base = NULL; + + /* + if (virStorageLoadDriverConfig(driver, driverConf) < 0) { + virStorageDriverShutdown(); + return -1; + } + */ + + if (virStoragePoolObjScanConfigs(driverState) < 0) { + storageDriverShutdown(); + return -1; + } + storageDriverAutostart(driverState); + + return 0; + + out_of_memory: + storageLog("virStorageStartup: out of memory"); + free(base); + free(driverState); + driverState = NULL; + return -1; +} + +/** + * virStorageReload: + * + * Function to restart the storage driver, it will recheck the configuration + * files and update its state + */ +static int +storageDriverReload(void) { + virStoragePoolObjScanConfigs(driverState); + storageDriverAutostart(driverState); + + return 0; +} + +/** + * virStorageActive: + * + * Checks if the storage driver is active, i.e. has an active pool + * + * Returns 1 if active, 0 otherwise + */ +static int +storageDriverActive(void) { + /* If we've any active networks or guests, then we + * mark this driver as active + */ + if (driverState->nactivePools) + return 1; + + /* Otherwise we're happy to deal with a shutdown */ + return 0; +} + +/** + * virStorageShutdown: + * + * Shutdown the storage driver, it will stop all active storage pools + */ +static int +storageDriverShutdown(void) { + virStoragePoolObjPtr pool; + + if (!driverState) + return -1; + + /* shutdown active pools */ + pool = driverState->pools; + while (pool) { + virStoragePoolObjPtr next = pool->next; + if (virStoragePoolObjIsActive(pool)) { + virStorageBackendPtr backend; + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + storageLog("Missing backend"); + continue; + } + + if (backend->stopPool && + backend->stopPool(NULL, pool) < 0) { + virErrorPtr err = virGetLastError(); + storageLog("Failed to stop storage pool '%s': %s", + pool->def->name, err->message); + } + virStoragePoolObjClearVols(pool); + } + pool = next; + } + + /* free inactive pools */ + pool = driverState->pools; + while (pool) { + virStoragePoolObjPtr next = pool->next; + virStoragePoolObjFree(pool); + pool = next; + } + driverState->pools = NULL; + driverState->nactivePools = 0; + driverState->ninactivePools = 0; + + free(driverState->configDir); + free(driverState->autostartDir); + free(driverState); + driverState = NULL; + + return 0; +} + + + +static virStoragePoolPtr +storagePoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, uuid); + virStoragePoolPtr ret; + + if (!pool) { + virStorageReportError(conn, VIR_ERR_NO_STORAGE_POOL, + _("no pool with matching uuid")); + return NULL; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + return ret; +} + +static virStoragePoolPtr +storagePoolLookupByName(virConnectPtr conn, + const char *name) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, name); + virStoragePoolPtr ret; + + if (!pool) { + virStorageReportError(conn, VIR_ERR_NO_STORAGE_POOL, + _("no pool with matching name")); + return NULL; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + return ret; +} + +static virStoragePoolPtr +storagePoolLookupByVolume(virStorageVolPtr vol) { + return storagePoolLookupByName(vol->conn, vol->pool); +} + +static virDrvOpenStatus +storageOpen(virConnectPtr conn, + xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) { + if (!driverState) + return VIR_DRV_OPEN_DECLINED; + + conn->storagePrivateData = driverState; + return VIR_DRV_OPEN_SUCCESS; +} + +static int +storageClose(virConnectPtr conn) { + conn->storagePrivateData = NULL; + return 0; +} + +static int +storageNumPools(virConnectPtr conn) { + virStorageDriverStatePtr driver + = (virStorageDriverStatePtr)conn->storagePrivateData; + return driver->nactivePools; +} + +static int +storageListPools(virConnectPtr conn, + char **const names, + int nnames) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + int got = 0, i; + while (pool && got < nnames) { + if (virStoragePoolObjIsActive(pool)) { + if (!(names[got] = strdup(pool->def->name))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("names")); + goto cleanup; + } + got++; + } + pool = pool->next; + } + return got; + + cleanup: + for (i = 0 ; i < got ; i++) { + free(names[i]); + names[i] = NULL; + } + memset(names, 0, nnames); + return -1; +} + +static int +storageNumDefinedPools(virConnectPtr conn) { + virStorageDriverStatePtr driver + = (virStorageDriverStatePtr)conn->storagePrivateData; + return driver->ninactivePools; +} + +static int +storageListDefinedPools(virConnectPtr conn, + char **const names, + int nnames) { + virStorageDriverStatePtr driver + = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + int got = 0, i; + while (pool && got < nnames) { + if (!virStoragePoolObjIsActive(pool)) { + if (!(names[got] = strdup(pool->def->name))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("names")); + goto cleanup; + } + got++; + } + pool = pool->next; + } + return got; + + cleanup: + for (i = 0 ; i < got ; i++) { + free(names[i]); + names[i] = NULL; + } + memset(names, 0, nnames); + return -1; +} + +static virStoragePoolPtr +storagePoolCreate(virConnectPtr conn, + const char *xml, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr )conn->storagePrivateData; + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + virStoragePoolPtr ret; + virStorageBackendPtr backend; + + if (!(def = virStoragePoolDefParse(conn, xml, NULL))) + return NULL; + + if (virStoragePoolObjFindByUUID(driver, def->uuid) || + virStoragePoolObjFindByName(driver, def->name)) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool already exists")); + virStoragePoolDefFree(def); + return NULL; + } + + if ((backend = virStorageBackendForType(def->type)) == NULL) { + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(conn, driver, def))) { + virStoragePoolDefFree(def); + return NULL; + } + + if (backend->startPool(conn, pool) < 0) { + virStoragePoolObjRemove(driver, pool); + return NULL; + } + pool->active = 1; + driver->nactivePools++; + driver->ninactivePools--; + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + + return ret; +} + +static virStoragePoolPtr +storagePoolDefine(virConnectPtr conn, + const char *xml, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver + = (virStorageDriverStatePtr )conn->storagePrivateData; + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + virStoragePoolPtr ret; + virStorageBackendPtr backend; + + if (!(def = virStoragePoolDefParse(conn, xml, NULL))) + return NULL; + + if ((backend = virStorageBackendForType(def->type)) == NULL) { + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(conn, driver, def))) { + virStoragePoolDefFree(def); + return NULL; + } + + if (virStoragePoolObjSaveDef(conn, driver, pool, def) < 0) { + virStoragePoolObjRemove(driver, pool); + return NULL; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + return ret; +} + +static int +storagePoolUndefine(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("pool is still active")); + return -1; + } + + if (virStoragePoolObjDeleteDef(obj->conn, pool) < 0) + return -1; + + if (unlink(pool->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) + storageLog("Failed to delete autostart link '%s': %s", + pool->autostartLink, strerror(errno)); + + free(pool->configFile); + pool->configFile = NULL; + free(pool->autostartLink); + pool->autostartLink = NULL; + + virStoragePoolObjRemove(driver, pool); + + return 0; +} + +static int +storagePoolStart(virStoragePoolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("pool already active")); + return -1; + } + if (backend->startPool && + backend->startPool(obj->conn, pool) < 0) + return -1; + if (backend->refreshPool(obj->conn, pool) < 0) { + if (backend->stopPool) + backend->stopPool(obj->conn, pool); + return -1; + } + + pool->active = 1; + driver->nactivePools++; + driver->ninactivePools--; + + return 0; +} + +static int +storagePoolBuild(virStoragePoolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver + = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is already active")); + return -1; + } + + if (backend->buildPool && + backend->buildPool(obj->conn, pool, flags) < 0) + return -1; + + return 0; +} + + +static int +storagePoolDestroy(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is not active")); + return -1; + } + + if (backend->stopPool && + backend->stopPool(obj->conn, pool) < 0) + return -1; + + virStoragePoolObjClearVols(pool); + + pool->active = 0; + driver->nactivePools--; + driver->ninactivePools++; + + if (pool->configFile == NULL) + virStoragePoolObjRemove(driver, pool); + + return 0; +} + + +static int +storagePoolDelete(virStoragePoolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is still active")); + return -1; + } + + if (!backend->deletePool) { + virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, + _("pool does not support volume delete")); + return -1; + } + if (backend->deletePool(obj->conn, pool, flags) < 0) + return -1; + + return 0; +} + + +static int +storagePoolRefresh(virStoragePoolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + int ret = 0; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is not active")); + return -1; + } + + virStoragePoolObjClearVols(pool); + if ((ret = backend->refreshPool(obj->conn, pool)) < 0) { + if (backend->stopPool) + backend->stopPool(obj->conn, pool); + + pool->active = 0; + driver->nactivePools--; + driver->ninactivePools++; + + if (pool->configFile == NULL) + virStoragePoolObjRemove(driver, pool); + } + + return ret; +} + + +static int +storagePoolGetInfo(virStoragePoolPtr obj, + virStoragePoolInfoPtr info) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + memset(info, 0, sizeof(virStoragePoolInfo)); + if (pool->active) + info->state = VIR_STORAGE_POOL_RUNNING; + else + info->state = VIR_STORAGE_POOL_INACTIVE; + info->capacity = pool->def->capacity; + info->allocation = pool->def->allocation; + info->available = pool->def->available; + + return 0; +} + +static char * +storagePoolDumpXML(virStoragePoolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return NULL; + } + + return virStoragePoolDefFormat(obj->conn, pool->def); +} + +static int +storagePoolGetAutostart(virStoragePoolPtr obj, + int *autostart) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no pool with matching uuid")); + return -1; + } + + if (!pool->configFile) { + *autostart = 0; + } else { + *autostart = pool->autostart; + } + + return 0; +} + +static int +storagePoolSetAutostart(virStoragePoolPtr obj, + int autostart) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no pool with matching uuid")); + return -1; + } + + if (!pool->configFile) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_ARG, + _("pool has no config file")); + return -1; + } + + autostart = (autostart != 0); + + if (pool->autostart == autostart) + return 0; + + if (autostart) { + int err; + + if ((err = virFileMakePath(driver->autostartDir))) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create autostart directory %s: %s"), + driver->autostartDir, strerror(err)); + return -1; + } + + if (symlink(pool->configFile, pool->autostartLink) < 0) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to create symlink '%s' to '%s': %s"), + pool->autostartLink, pool->configFile, + strerror(errno)); + return -1; + } + } else { + if (unlink(pool->autostartLink) < 0 && + errno != ENOENT && errno != ENOTDIR) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to delete symlink '%s': %s"), + pool->autostartLink, strerror(errno)); + return -1; + } + } + + pool->autostart = autostart; + + return 0; +} + + +static int +storagePoolNumVolumes(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is not active")); + return -1; + } + + return pool->nvolumes; +} + +static int +storagePoolListVolumes(virStoragePoolPtr obj, + char **const names, + int maxnames) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + int i = 0; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is not active")); + return -1; + } + + memset(names, 0, maxnames); + vol = pool->volumes; + while (vol && i < maxnames) { + names[i] = strdup(vol->name); + if (names[i] == NULL) { + virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, _("name")); + goto cleanup; + } + vol = vol->next; + i++; + } + + return i; + + cleanup: + for (i = 0 ; i < maxnames ; i++) { + free(names[i]); + names[i] = NULL; + } + memset(names, 0, maxnames); + return -1; +} + + +static virStorageVolPtr +storageVolumeLookupByName(virStoragePoolPtr obj, + const char *name) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is not active")); + return NULL; + } + + vol = virStorageVolDefFindByName(pool, name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage vol with matching name")); + return NULL; + } + + return virGetStorageVol(obj->conn, pool->def->name, vol->name, vol->key); +} + + +static virStorageVolPtr +storageVolumeLookupByKey(virConnectPtr conn, + const char *key) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (virStoragePoolObjIsActive(pool)) { + virStorageVolDefPtr vol = virStorageVolDefFindByKey(pool, key); + + if (vol) + return virGetStorageVol(conn, + pool->def->name, + vol->name, + vol->key); + } + pool = pool->next; + } + + virStorageReportError(conn, VIR_ERR_INVALID_STORAGE_VOL, + _("no storage vol with matching key")); + return NULL; +} + +static virStorageVolPtr +storageVolumeLookupByPath(virConnectPtr conn, + const char *path) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (virStoragePoolObjIsActive(pool)) { + virStorageVolDefPtr vol = virStorageVolDefFindByPath(pool, path); + + if (vol) + return virGetStorageVol(conn, + pool->def->name, + vol->name, + vol->key); + } + pool = pool->next; + } + + virStorageReportError(conn, VIR_ERR_INVALID_STORAGE_VOL, + _("no storage vol with matching path")); + return NULL; +} + +static virStorageVolPtr +storageVolumeCreateXML(virStoragePoolPtr obj, + const char *xmldesc, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is not active")); + return NULL; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return NULL; + + vol = virStorageVolDefParse(obj->conn, pool->def, xmldesc, NULL); + if (vol == NULL) + return NULL; + + if (virStorageVolDefFindByName(pool, vol->name)) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("storage vol already exists")); + virStorageVolDefFree(vol); + return NULL; + } + + if (!backend->createVol) { + virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, + _("storage pool does not support volume creation")); + virStorageVolDefFree(vol); + return NULL; + } + + if (backend->createVol(obj->conn, pool, vol) < 0) { + virStorageVolDefFree(vol); + return NULL; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + + return virGetStorageVol(obj->conn, pool->def->name, vol->name, vol->key); +} + +static int +storageVolumeDelete(virStorageVolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageBackendPtr backend; + virStorageVolDefPtr vol, tmp, prev; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is not active")); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return -1; + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage vol with matching name")); + return -1; + } + + if (!backend->deleteVol) { + virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, + _("storage pool does not support vol deletion")); + virStorageVolDefFree(vol); + return -1; + } + + if (backend->deleteVol(obj->conn, pool, vol, flags) < 0) { + return -1; + } + + prev = NULL; + tmp = pool->volumes; + while (tmp) { + if (tmp == vol) { + break; + } + prev = tmp; + tmp = tmp->next; + } + if (prev) { + prev->next = vol->next; + } else { + pool->volumes = vol->next; + } + pool->nvolumes--; + virStorageVolDefFree(vol); + + return 0; +} + +static int +storageVolumeGetInfo(virStorageVolPtr obj, + virStorageVolInfoPtr info) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageBackendPtr backend; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is not active")); + return -1; + } + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage vol with matching name")); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return -1; + + if (backend->refreshVol && + backend->refreshVol(obj->conn, pool, vol) < 0) + return -1; + + memset(info, 0, sizeof(*info)); + info->type = backend->volType; + info->capacity = vol->capacity; + info->allocation = vol->allocation; + + return 0; +} + +static char * +storageVolumeGetXMLDesc(virStorageVolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageBackendPtr backend; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is not active")); + return NULL; + } + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage vol with matching name")); + return NULL; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return NULL; + + return virStorageVolDefFormat(obj->conn, pool->def, vol); +} + +static char * +storageVolumeGetPath(virStorageVolPtr obj) { + virStorageDriverStatePtr driver = + (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageVolDefPtr vol; + char *ret; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage pool with matching uuid")); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool is not active")); + return NULL; + } + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("no storage vol with matching name")); + return NULL; + } + + ret = strdup(vol->target.path); + if (ret == NULL) { + virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, _("path")); + return NULL; + } + return ret; +} + + + + + +static virStorageDriver storageDriver = { + "storage", + storageOpen, + storageClose, + storageNumPools, + storageListPools, + storageNumDefinedPools, + storageListDefinedPools, + storagePoolLookupByName, + storagePoolLookupByUUID, + storagePoolLookupByVolume, + storagePoolCreate, + storagePoolDefine, + storagePoolBuild, + storagePoolUndefine, + storagePoolStart, + storagePoolDestroy, + storagePoolDelete, + storagePoolRefresh, + storagePoolGetInfo, + storagePoolDumpXML, + storagePoolGetAutostart, + storagePoolSetAutostart, + storagePoolNumVolumes, + storagePoolListVolumes, + storageVolumeLookupByName, + storageVolumeLookupByKey, + storageVolumeLookupByPath, + storageVolumeCreateXML, + storageVolumeDelete, + storageVolumeGetInfo, + storageVolumeGetXMLDesc, + storageVolumeGetPath +}; + + +static virStateDriver stateDriver = { + storageDriverStartup, + storageDriverShutdown, + storageDriverReload, + storageDriverActive, +}; + +int storageRegister(void) { + virRegisterStorageDriver(&storageDriver); + virRegisterStateDriver(&stateDriver); + return 0; +} + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r d043e3715bce src/storage_driver.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_driver.h Tue Feb 19 16:59:10 2008 -0500 @@ -0,0 +1,45 @@ +/* + * storage_driver.h: core driver for storage APIs + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_DRIVER_H__ +#define __VIR_STORAGE_DRIVER_H__ + +#include "storage_conf.h" + +int storageRegister(void); + +#endif /* __VIR_STORAGE_DRIVER_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r d043e3715bce src/virterror.c --- a/src/virterror.c Tue Feb 19 16:26:35 2008 -0500 +++ b/src/virterror.c Tue Feb 19 16:59:10 2008 -0500 @@ -678,6 +678,18 @@ __virErrorMsg(virErrorNumber error, cons else errmsg = _("authentication failed: %s"); break; + case VIR_ERR_NO_STORAGE_POOL: + if (info == NULL) + errmsg = _("Storage pool not found"); + else + errmsg = _("Storage pool not found: %s"); + break; + case VIR_ERR_NO_STORAGE_VOL: + if (info == NULL) + errmsg = _("Storage volume not found"); + else + errmsg = _("Storage volume not found: %s"); + break; case VIR_ERR_INVALID_STORAGE_POOL: if (info == NULL) errmsg = _("invalid storage pool pointer in"); -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This patch provides a couple of helper APIs used by the forthcoming driver backends. - virStorageBackendUpdateVolInfo - take a filename and virStorageVolPtr and update the capacity/allocation information based on the file stat() results. Also update the permissions data on owner/mode and SELinux label. - virStorageBackendUpdateVolInfoFD - same as above but with a pre-opened filehandle - virStorageBackendStablePath - given a /dev/XXX node, and a directory of symlinks (eg /dev/disk/by-path), attempts to find a symlink pointing to the desired file. - virStorageBackendRunProgRegex - given one or more regexes and a command line argv[], execute the program and attempt to match its STDOUT against the regex. When complete matches are found invoke a callback. - virStorageBackendRunProgNul - given a command line argv[] and an expected number of fields per record, tokenize the STDOUT into records splitting on NULL, and invoke the callback per record. The SELinux code is optional, and can be replaced with calls to any other library which can provide MAC file labels, or just disabled completely.. configure.in | 39 ++++ libvirt.spec.in | 1 src/Makefile.am | 3 src/storage_backend.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/storage_backend.h | 38 ++++ tests/Makefile.am | 2 6 files changed, 537 insertions(+), 1 deletion(-) diff -r 4f8d1242609a configure.in --- a/configure.in Wed Feb 06 22:14:01 2008 -0500 +++ b/configure.in Thu Feb 07 11:14:53 2008 -0500 @@ -473,6 +473,40 @@ AC_SUBST(AVAHI_CFLAGS) AC_SUBST(AVAHI_CFLAGS) AC_SUBST(AVAHI_LIBS) +dnl SELinux +AC_ARG_WITH(selinux, + [ --with-selinux use SELinux to manage security], + [], + [with_selinux=check]) + +SELINUX_CFLAGS= +SELINUX_LIBS= +if test "$with_selinux" != "no"; then + old_cflags="$CFLAGS" + old_libs="$LIBS" + if test "$with_selinux" = "check"; then + AC_CHECK_HEADER([selinux/selinux.h],[],[with_selinux=no]) + AC_CHECK_LIB(selinux, fgetfilecon,[],[with_selinux=no]) + if test "$with_selinux" != "no"; then + with_selinux="yes" + fi + else + AC_CHECK_HEADER([selinux/selinux.h],[], + [AC_MSG_ERROR([You must install the SELinux development package in order to compile libvirt])]) + AC_CHECK_LIB(selinux, fgetfilecon,[], + [AC_MSG_ERROR([You must install the SELinux development package in order to compile and run libvirt])]) + fi + CFLAGS="$old_cflags" + LIBS="$old_libs" +fi +if test "$with_selinux" = "yes"; then + SELINUX_LIBS="-lselinux" + AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, [whether SELinux is available for security]) +fi +AM_CONDITIONAL(HAVE_SELINUX, [test "$with_selinux" != "no"]) +AC_SUBST(SELINUX_CFLAGS) +AC_SUBST(SELINUX_LIBS) + dnl virsh libraries AC_CHECK_HEADERS([readline/readline.h]) @@ -745,6 +779,11 @@ else else AC_MSG_NOTICE([ polkit: no]) fi +if test "$with_selinux" = "yes" ; then +AC_MSG_NOTICE([ selinux: $SELINUX_CFLAGS $SELINUX_LIBS]) +else +AC_MSG_NOTICE([ selinux: no]) +fi AC_MSG_NOTICE([]) AC_MSG_NOTICE([Miscellaneous]) AC_MSG_NOTICE([]) diff -r 4f8d1242609a libvirt.spec.in --- a/libvirt.spec.in Wed Feb 06 22:14:01 2008 -0500 +++ b/libvirt.spec.in Thu Feb 07 11:14:53 2008 -0500 @@ -41,6 +41,7 @@ BuildRequires: gettext BuildRequires: gettext BuildRequires: gnutls-devel BuildRequires: avahi-devel +BuildRequires: libselinux-devel BuildRequires: dnsmasq BuildRequires: bridge-utils BuildRequires: qemu diff -r 4f8d1242609a src/Makefile.am --- a/src/Makefile.am Wed Feb 06 22:14:01 2008 -0500 +++ b/src/Makefile.am Thu Feb 07 11:14:53 2008 -0500 @@ -8,6 +8,7 @@ INCLUDES = \ $(LIBXML_CFLAGS) \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ + $(SELINUX_CFLAGS) \ -DBINDIR=\""$(libexecdir)"\" \ -DSBINDIR=\""$(sbindir)"\" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" \ @@ -68,7 +69,7 @@ SERVER_SOURCES = \ ../qemud/remote_protocol.c ../qemud/remote_protocol.h libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) -libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) \ +libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) $(SELINUX_LIBS) \ @CYGWIN_EXTRA_LIBADD@ ../gnulib/lib/libgnu.la libvirt_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvirt_sym.version \ -version-info @LIBVIRT_VERSION_INFO@ \ diff -r 4f8d1242609a src/storage_backend.c --- a/src/storage_backend.c Wed Feb 06 22:14:01 2008 -0500 +++ b/src/storage_backend.c Thu Feb 07 11:14:53 2008 -0500 @@ -28,6 +28,14 @@ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> +#include <fcntl.h> +#include <stdint.h> +#include <sys/stat.h> +#include <dirent.h> + +#if HAVE_SELINUX +#include <selinux/selinux.h> +#endif #include "util.h" @@ -62,6 +70,453 @@ const char *virStorageBackendToString(in const char *virStorageBackendToString(int type) { virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %d", type); return NULL; +} + + +int virStorageBackendUpdateVolInfo(virConnectPtr conn, + virStorageVolDefPtr vol, + int withCapacity) +{ + int ret, fd; + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot open volume '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + return -1; + } + + ret = virStorageBackendUpdateVolInfoFD(conn, + vol, + fd, + withCapacity); + + close(fd); + + return ret; +} + +int virStorageBackendUpdateVolInfoFD(virConnectPtr conn, + virStorageVolDefPtr vol, + int fd, + int withCapacity) +{ + struct stat sb; +#if HAVE_SELINUX + security_context_t filecon = NULL; +#endif + + if (fstat(fd, &sb) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot stat file '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + return -1; + } + + if (!S_ISREG(sb.st_mode) && + !S_ISCHR(sb.st_mode) && + !S_ISBLK(sb.st_mode)) + return -2; + + if (S_ISREG(sb.st_mode)) { + vol->allocation = (unsigned long long)sb.st_blocks * (unsigned long long)512; + /* Regular files may be sparse, so logical size (capacity) is not same + * as actual allocation above + */ + if (withCapacity) + vol->capacity = sb.st_size; + } else { + off_t end; + /* XXX this is POSIX compliant, but doesn't work for for CHAR files, + * only BLOCK. There is a Linux specific ioctl() for getting + * size of both CHAR / BLOCK devices we should check for in + * configure + */ + end = lseek(fd, 0, SEEK_END); + if (end == (off_t)-1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot seek to end of file '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + return -1; + } + vol->allocation = end; + if (withCapacity) vol->capacity = end; + } + + vol->target.perms.mode = sb.st_mode; + vol->target.perms.uid = sb.st_uid; + vol->target.perms.gid = sb.st_gid; + + free(vol->target.perms.label); + vol->target.perms.label = NULL; + +#if HAVE_SELINUX + if (fgetfilecon(fd, &filecon) == -1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot get file context of %s: %s (%d)", + vol->target.path, strerror(errno), errno); + return -1; + } + vol->target.perms.label = strdup(filecon); + if (vol->target.perms.label == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "context"); + return -1; + } + freecon(filecon); +#else + vol->target.perms.label = NULL; +#endif + + return 0; +} + +/* + * Given a volume path directly in /dev/XXX, iterate over the + * entries in the directory pool->def->target.path and find the + * first symlink pointing to the volume path. + * + * If, the target.path is /dev/, then return the original volume + * path. + * + * If no symlink is found, then return the original volume path + * + * Typically target.path is one of the /dev/disk/by-XXX dirs + * with stable paths. + */ +char *virStorageBackendStablePath(virConnectPtr conn, + virStoragePoolObjPtr pool, + char *devpath) +{ + DIR *dh; + struct dirent *dent; + + /* Short circuit if pool has no target, or if its /dev */ + if (pool->def->target.path == NULL || + STREQ(pool->def->target.path, "/dev") || + STREQ(pool->def->target.path, "/dev/")) + return devpath; + + /* The pool is pointing somewhere like /dev/disk/by-path + * or /dev/disk/by-id, so we need to check all symlinks in + * the target directory and figure out which one points + * to this device node + */ + if ((dh = opendir(pool->def->target.path)) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot read dir %s: %s (%d)", + pool->def->target.path, + strerror(errno), errno); + return NULL; + } + + while ((dent = readdir(dh)) != NULL) { + char *stablepath; + if (dent->d_name[0] == '.') + continue; + + stablepath = malloc(strlen(pool->def->target.path) + 1 + strlen(dent->d_name) + 1); + if (stablepath == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "path"); + closedir(dh); + return NULL; + } + + strcpy(stablepath, pool->def->target.path); + strcat(stablepath, "/"); + strcat(stablepath, dent->d_name); + + if (virFileLinkPointsTo(stablepath, devpath)) { + closedir(dh); + return stablepath; + } + + free(stablepath); + } + + closedir(dh); + + /* Couldn't find any matching stable link so give back + * the original non-stable dev path + */ + return devpath; +} + +/* + * Run an external program. + * + * Read its output and apply a series of regexes to each line + * When the entire set of regexes has matched consequetively + * then run a callback passing in all the matches + */ +int virStorageBackendRunProgRegex(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + int nregex, + const char **regex, + int *nvars, + virStorageBackendListVolRegexFunc func, + void *data) +{ + int child = 0, fd = -1, exitstatus, err, failed = 1; + FILE *list = NULL; + regex_t *reg; + regmatch_t *vars = NULL; + char line[1024]; + int maxReg = 0, i, j; + int totgroups = 0, ngroup = 0, maxvars = 0; + char **groups; + + /* Compile all regular expressions */ + if ((reg = calloc(nregex, sizeof(*reg))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "regex"); + return -1; + } + + for (i = 0 ; i < nregex ; i++) { + err = regcomp(®[i], regex[i], REG_EXTENDED); + if (err != 0) { + char error[100]; + regerror(err, ®[i], error, sizeof(error)); + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "Failed to compile regex %s", error); + for (j = 0 ; j <= i ; j++) + regfree(®[j]); + free(reg); + return -1; + } + + totgroups += nvars[i]; + if (nvars[i] > maxvars) + maxvars = nvars[i]; + + } + + /* Storage for matched variables */ + if ((groups = calloc(totgroups, sizeof(*groups))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "regex groups"); + goto cleanup; + } + if ((vars = calloc(maxvars+1, sizeof(*vars))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "regex groups"); + goto cleanup; + } + + + /* Run the program and capture its output */ + if (virExec(conn, (char**)prog, &child, -1, &fd, NULL) < 0) { + goto cleanup; + } + + if ((list = fdopen(fd, "r")) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read fd"); + goto cleanup; + } + + while (fgets(line, sizeof(line), list) != NULL) { + /* Strip trailing newline */ + int len = strlen(line); + if (len && line[len-1] == '\n') + line[len-1] = '\0'; + + for (i = 0 ; i <= maxReg && i < nregex ; i++) { + if (regexec(®[i], line, nvars[i]+1, vars, 0) == 0) { + maxReg++; + + if (i == 0) + ngroup = 0; + + /* NULL terminate each captured group in the line */ + for (j = 0 ; j < nvars[i] ; j++) { + /* NB vars[0] is the full pattern, so we offset j by 1 */ + line[vars[j+1].rm_eo] = '\0'; + if ((groups[ngroup++] = strdup(line + vars[j+1].rm_so)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "regex groups"); + goto cleanup; + } + } + + /* We're matching on the last regex, so callback time */ + if (i == (nregex-1)) { + if (((*func)(conn, pool, groups, data)) < 0) + goto cleanup; + + /* Release matches & restart to matching the first regex */ + for (j = 0 ; j < totgroups ; j++) { + free(groups[j]); + groups[j] = NULL; + } + maxReg = 0; + ngroup = 0; + } + } + } + } + + failed = 0; + + cleanup: + if (groups) { + for (j = 0 ; j < totgroups ; j++) + free(groups[j]); + free(groups); + } + free(vars); + + for (i = 0 ; i < nregex ; i++) + regfree(®[i]); + + free(reg); + + if (list) + fclose(list); + else + close(fd); + + while ((err = waitpid(child, &exitstatus, 0) == -1) && errno == EINTR); + + /* Don't bother checking exit status if we already failed */ + if (failed) + return -1; + + if (err == -1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "failed to wait for command: %s (%d)", + strerror(errno), errno); + return -1; + } else { + if (WIFEXITED(exitstatus)) { + if (WEXITSTATUS(exitstatus) != 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "non-zero exit status from command %d", + WEXITSTATUS(exitstatus)); + return -1; + } + } else { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "command did not exit cleanly"); + return -1; + } + } + + return 0; +} + +/* + * Run an external program and read from its standard output + * a stream of tokens from IN_STREAM, applying FUNC to + * each successive sequence of N_COLUMNS tokens. + * If FUNC returns < 0, stop processing input and return -1. + * Return -1 if N_COLUMNS == 0. + * Return -1 upon memory allocation error. + * If the number of input tokens is not a multiple of N_COLUMNS, + * then the final FUNC call will specify a number smaller than N_COLUMNS. + * If there are no input tokens (empty input), call FUNC with N_COLUMNS == 0. + */ +int virStorageBackendRunProgNul(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + size_t n_columns, + virStorageBackendListVolNulFunc func, + void *data) +{ + size_t n_tok = 0; + int child = 0, fd = -1, exitstatus; + FILE *fp = NULL; + char **v; + int err = -1; + int w_err; + int i; + + if (n_columns == 0) + return -1; + + if (n_columns > SIZE_MAX / sizeof *v + || (v = malloc (n_columns * sizeof *v)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "n_columns too large"); + return -1; + } + for (i = 0; i < n_columns; i++) + v[i] = NULL; + + /* Run the program and capture its output */ + if (virExec(conn, (char**)prog, &child, -1, &fd, NULL) < 0) { + goto cleanup; + } + + if ((fp = fdopen(fd, "r")) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read fd"); + goto cleanup; + } + + while (1) { + char *buf = NULL; + size_t buf_len = 0; + /* Be careful: even when it returns -1, + this use of getdelim allocates memory. */ + ssize_t tok_len = getdelim (&buf, &buf_len, 0, fp); + v[n_tok] = buf; + if (tok_len < 0) { + /* Maybe EOF, maybe an error. + If n_tok > 0, then we know it's an error. */ + if (n_tok && func (conn, pool, n_tok, v, data) < 0) + goto cleanup; + break; + } + ++n_tok; + if (n_tok == n_columns) { + if (func (conn, pool, n_tok, v, data) < 0) + goto cleanup; + n_tok = 0; + for (i = 0; i < n_columns; i++) { + free (v[i]); + v[i] = NULL; + } + } + } + + if (feof (fp)) + err = 0; + else + virStorageReportError (conn, VIR_ERR_INTERNAL_ERROR, + "read error: %s", strerror (errno)); + + cleanup: + for (i = 0; i < n_columns; i++) + free (v[i]); + free (v); + + if (fp) + fclose (fp); + else + close (fd); + + while ((w_err = waitpid (child, &exitstatus, 0) == -1) && errno == EINTR) + /* empty */ ; + + /* Don't bother checking exit status if we already failed */ + if (err < 0) + return -1; + + if (w_err == -1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "failed to wait for command: %s (%d)", + strerror(errno), errno); + return -1; + } else { + if (WIFEXITED(exitstatus)) { + if (WEXITSTATUS(exitstatus) != 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "non-zero exit status from command %d", + WEXITSTATUS(exitstatus)); + return -1; + } + } else { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "command did not exit cleanly"); + return -1; + } + } + + return 0; } diff -r 4f8d1242609a src/storage_backend.h --- a/src/storage_backend.h Wed Feb 06 22:14:01 2008 -0500 +++ b/src/storage_backend.h Thu Feb 07 11:14:53 2008 -0500 @@ -95,6 +95,44 @@ int virStorageBackendFromString(const ch int virStorageBackendFromString(const char *type); const char *virStorageBackendToString(int type); +int virStorageBackendUpdateVolInfo(virConnectPtr conn, + virStorageVolDefPtr vol, + int withCapacity); + +int virStorageBackendUpdateVolInfoFD(virConnectPtr conn, + virStorageVolDefPtr vol, + int fd, + int withCapacity); + +char *virStorageBackendStablePath(virConnectPtr conn, + virStoragePoolObjPtr pool, + char *devpath); + +typedef int (*virStorageBackendListVolRegexFunc)(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + void *data); +typedef int (*virStorageBackendListVolNulFunc)(virConnectPtr conn, + virStoragePoolObjPtr pool, + size_t n_tokens, + char **const groups, + void *data); + +int virStorageBackendRunProgRegex(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + int nregex, + const char **regex, + int *nvars, + virStorageBackendListVolRegexFunc func, + void *data); + +int virStorageBackendRunProgNul(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + size_t n_columns, + virStorageBackendListVolNulFunc func, + void *data); #endif /* __VIR_STORAGE_BACKEND_H__ */ diff -r 4f8d1242609a tests/Makefile.am --- a/tests/Makefile.am Wed Feb 06 22:14:01 2008 -0500 +++ b/tests/Makefile.am Thu Feb 07 11:14:53 2008 -0500 @@ -20,6 +20,7 @@ INCLUDES = \ $(LIBXML_CFLAGS) \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ + $(SELINUX_CFLAGS) \ -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=199506L \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" \ $(COVERAGE_CFLAGS) \ @@ -31,6 +32,7 @@ LDADDS = \ $(LIBXML_LIBS) \ $(GNUTLS_LIBS) \ $(SASL_LIBS) \ + $(SELINUX_LIBS) \ $(WARN_CFLAGS) \ $(LIBVIRT) \ ../gnulib/lib/libgnu.la \ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch provides a couple of helper APIs used by the forthcoming driver backends.
- virStorageBackendUpdateVolInfo - take a filename and virStorageVolPtr and update the capacity/allocation information based on the file stat() results. Also update the permissions data on owner/mode and SELinux label.
- virStorageBackendUpdateVolInfoFD - same as above but with a pre-opened filehandle
- virStorageBackendStablePath - given a /dev/XXX node, and a directory of symlinks (eg /dev/disk/by-path), attempts to find a symlink pointing to the desired file.
- virStorageBackendRunProgRegex - given one or more regexes and a command line argv[], execute the program and attempt to match its STDOUT against the regex. When complete matches are found invoke a callback.
- virStorageBackendRunProgNul - given a command line argv[] and an expected number of fields per record, tokenize the STDOUT into records splitting on NULL, and invoke the callback per record.
The SELinux code is optional, and can be replaced with calls to any other library which can provide MAC file labels, or just disabled completely..
ACK

On Tue, Feb 12, 2008 at 04:37:08AM +0000, Daniel P. Berrange wrote:
This patch provides a couple of helper APIs used by the forthcoming driver backends.
- virStorageBackendUpdateVolInfo - take a filename and virStorageVolPtr and update the capacity/allocation information based on the file stat() results. Also update the permissions data on owner/mode and SELinux label.
- virStorageBackendUpdateVolInfoFD - same as above but with a pre-opened filehandle
- virStorageBackendStablePath - given a /dev/XXX node, and a directory of symlinks (eg /dev/disk/by-path), attempts to find a symlink pointing to the desired file.
- virStorageBackendRunProgRegex - given one or more regexes and a command line argv[], execute the program and attempt to match its STDOUT against the regex. When complete matches are found invoke a callback.
- virStorageBackendRunProgNul - given a command line argv[] and an expected number of fields per record, tokenize the STDOUT into records splitting on NULL, and invoke the callback per record.
The SELinux code is optional, and can be replaced with calls to any other library which can provide MAC file labels, or just disabled completely.. [...] +SELINUX_CFLAGS= +SELINUX_LIBS= +if test "$with_selinux" != "no"; then + old_cflags="$CFLAGS" + old_libs="$LIBS" + if test "$with_selinux" = "check"; then + AC_CHECK_HEADER([selinux/selinux.h],[],[with_selinux=no]) + AC_CHECK_LIB(selinux, fgetfilecon,[],[with_selinux=no]) + if test "$with_selinux" != "no"; then + with_selinux="yes" + fi + else + AC_CHECK_HEADER([selinux/selinux.h],[], + [AC_MSG_ERROR([You must install the SELinux development package in order to compile libvirt])]) + AC_CHECK_LIB(selinux, fgetfilecon,[], + [AC_MSG_ERROR([You must install the SELinux development package in order to compile and run libvirt])])
Hum, does that mean that withough any configure flags and if SELinux is not found then configure would fail ? It may be the right thing to do since it's a security option (and hence deactivation as an opt-in looks reasonable) but this may be viewed as a bit too Fedora centric :-) [...]
+ if (S_ISREG(sb.st_mode)) { + vol->allocation = (unsigned long long)sb.st_blocks * (unsigned long long)512;
Hum, I think this should be sb.st_blocks * sb.st_blksize since you can have 1k or 4k kind of filesystems too or at least to ensure portability.
+ /* Regular files may be sparse, so logical size (capacity) is not same + * as actual allocation above + */ + if (withCapacity) + vol->capacity = sb.st_size; + } else { + off_t end; + /* XXX this is POSIX compliant, but doesn't work for for CHAR files, + * only BLOCK. There is a Linux specific ioctl() for getting + * size of both CHAR / BLOCK devices we should check for in + * configure + */ + end = lseek(fd, 0, SEEK_END); + if (end == (off_t)-1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot seek to end of file '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + return -1; + } + vol->allocation = end; + if (withCapacity) vol->capacity = end; + }
It would be good to see what this gives on Solaris, Windows ... [...]
+/* + * Run an external program. + * + * Read its output and apply a series of regexes to each line + * When the entire set of regexes has matched consequetively + * then run a callback passing in all the matches + */ +int virStorageBackendRunProgRegex(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + int nregex, + const char **regex, + int *nvars, + virStorageBackendListVolRegexFunc func, + void *data)
Ouch, that's very very specialized ... but if needed okay
+ +/* + * Run an external program and read from its standard output + * a stream of tokens from IN_STREAM, applying FUNC to + * each successive sequence of N_COLUMNS tokens. + * If FUNC returns < 0, stop processing input and return -1. + * Return -1 if N_COLUMNS == 0. + * Return -1 upon memory allocation error. + * If the number of input tokens is not a multiple of N_COLUMNS, + * then the final FUNC call will specify a number smaller than N_COLUMNS. + * If there are no input tokens (empty input), call FUNC with N_COLUMNS == 0. + */ +int virStorageBackendRunProgNul(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + size_t n_columns, + virStorageBackendListVolNulFunc func, + void *data)
yeah we are reaching an impressive level of specialization there... okay, +1 Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Mon, Feb 18, 2008 at 10:36:06AM -0500, Daniel Veillard wrote:
On Tue, Feb 12, 2008 at 04:37:08AM +0000, Daniel P. Berrange wrote:
The SELinux code is optional, and can be replaced with calls to any other library which can provide MAC file labels, or just disabled completely..
[...]
+SELINUX_CFLAGS= +SELINUX_LIBS= +if test "$with_selinux" != "no"; then + old_cflags="$CFLAGS" + old_libs="$LIBS" + if test "$with_selinux" = "check"; then + AC_CHECK_HEADER([selinux/selinux.h],[],[with_selinux=no]) + AC_CHECK_LIB(selinux, fgetfilecon,[],[with_selinux=no]) + if test "$with_selinux" != "no"; then + with_selinux="yes" + fi + else + AC_CHECK_HEADER([selinux/selinux.h],[], + [AC_MSG_ERROR([You must install the SELinux development package in order to compile libvirt])]) + AC_CHECK_LIB(selinux, fgetfilecon,[], + [AC_MSG_ERROR([You must install the SELinux development package in order to compile and run libvirt])])
Hum, does that mean that withough any configure flags and if SELinux is not found then configure would fail ? It may be the right thing to do since it's a security option (and hence deactivation as an opt-in looks reasonable) but this may be viewed as a bit too Fedora centric :-)
The --with-selinux argument to configure has 3 possible values 'yes', 'no', 'check'. It will default to the latter. If it finds selinux libs it'll turn it on, otherwise it'll turn it off. It'll only cause configure to fail if you have it set to 'yes' and selinux is missing, which is not default behavuour.
[...]
+ if (S_ISREG(sb.st_mode)) { + vol->allocation = (unsigned long long)sb.st_blocks * (unsigned long long)512;
Hum, I think this should be
sb.st_blocks * sb.st_blksize
since you can have 1k or 4k kind of filesystems too or at least to ensure portability.
Good point.
+ } else { + off_t end; + /* XXX this is POSIX compliant, but doesn't work for for CHAR files, + * only BLOCK. There is a Linux specific ioctl() for getting + * size of both CHAR / BLOCK devices we should check for in + * configure + */ + end = lseek(fd, 0, SEEK_END); + if (end == (off_t)-1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot seek to end of file '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + return -1; + } + vol->allocation = end; + if (withCapacity) vol->capacity = end; + }
It would be good to see what this gives on Solaris, Windows ...
I'll have to double check, but I think with these patches that comment is now obsolete. IIRC we should only ever execute this code path when we have a plain file, so the fact that it doesn't work with block/char devices should no longer matter.
+/* + * Run an external program. + * + * Read its output and apply a series of regexes to each line + * When the entire set of regexes has matched consequetively + * then run a callback passing in all the matches + */ +int virStorageBackendRunProgRegex(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + int nregex, + const char **regex, + int *nvars, + virStorageBackendListVolRegexFunc func, + void *data)
Ouch, that's very very specialized ... but if needed okay
Actually it is very generalized ;-) It lets you spawn any program and trivially tokenize its output based on regexes - we use this in basically all the storage drivers for talking to parted, lvs, pvs, iscsiadm, etc
+ +/* + * Run an external program and read from its standard output + * a stream of tokens from IN_STREAM, applying FUNC to + * each successive sequence of N_COLUMNS tokens. + * If FUNC returns < 0, stop processing input and return -1. + * Return -1 if N_COLUMNS == 0. + * Return -1 upon memory allocation error. + * If the number of input tokens is not a multiple of N_COLUMNS, + * then the final FUNC call will specify a number smaller than N_COLUMNS. + * If there are no input tokens (empty input), call FUNC with N_COLUMNS == 0. + */ +int virStorageBackendRunProgNul(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + size_t n_columns, + virStorageBackendListVolNulFunc func, + void *data)
yeah we are reaching an impressive level of specialization there...
Again, fairly general purpose, except instead of regexes, it uses NULL to separate fields. We only use this in one place currently though. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Mon, Feb 18, 2008 at 03:45:52PM +0000, Daniel P. Berrange wrote:
On Mon, Feb 18, 2008 at 10:36:06AM -0500, Daniel Veillard wrote:
On Tue, Feb 12, 2008 at 04:37:08AM +0000, Daniel P. Berrange wrote:
The SELinux code is optional, and can be replaced with calls to any other library which can provide MAC file labels, or just disabled completely..
[...]
+SELINUX_CFLAGS= +SELINUX_LIBS= +if test "$with_selinux" != "no"; then + old_cflags="$CFLAGS" + old_libs="$LIBS" + if test "$with_selinux" = "check"; then + AC_CHECK_HEADER([selinux/selinux.h],[],[with_selinux=no]) + AC_CHECK_LIB(selinux, fgetfilecon,[],[with_selinux=no]) + if test "$with_selinux" != "no"; then + with_selinux="yes" + fi + else + AC_CHECK_HEADER([selinux/selinux.h],[], + [AC_MSG_ERROR([You must install the SELinux development package in order to compile libvirt])]) + AC_CHECK_LIB(selinux, fgetfilecon,[], + [AC_MSG_ERROR([You must install the SELinux development package in order to compile and run libvirt])])
Hum, does that mean that withough any configure flags and if SELinux is not found then configure would fail ? It may be the right thing to do since it's a security option (and hence deactivation as an opt-in looks reasonable) but this may be viewed as a bit too Fedora centric :-)
The --with-selinux argument to configure has 3 possible values 'yes', 'no', 'check'. It will default to the latter. If it finds selinux libs it'll turn it on, otherwise it'll turn it off. It'll only cause configure to fail if you have it set to 'yes' and selinux is missing, which is not default behavuour.
oh, perfect :-) thanks ! Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Tue, Feb 12, 2008 at 04:37:08AM +0000, Daniel P. Berrange wrote:
This patch provides a couple of helper APIs used by the forthcoming driver backends.
- virStorageBackendUpdateVolInfo - take a filename and virStorageVolPtr and update the capacity/allocation information based on the file stat() results. Also update the permissions data on owner/mode and SELinux label.
- virStorageBackendUpdateVolInfoFD - same as above but with a pre-opened filehandle
- virStorageBackendStablePath - given a /dev/XXX node, and a directory of symlinks (eg /dev/disk/by-path), attempts to find a symlink pointing to the desired file.
- virStorageBackendRunProgRegex - given one or more regexes and a command line argv[], execute the program and attempt to match its STDOUT against the regex. When complete matches are found invoke a callback.
- virStorageBackendRunProgNul - given a command line argv[] and an expected number of fields per record, tokenize the STDOUT into records splitting on NULL, and invoke the callback per record.
The SELinux code is optional, and can be replaced with calls to any other library which can provide MAC file labels, or just disabled completely..
configure.in | 39 ++++ libvirt.spec.in | 1 src/Makefile.am | 3 src/storage_backend.c | 470 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/storage_backend.h | 38 ++++ tests/Makefile.am | 2 6 files changed, 552 insertions(+), 1 deletion(-) diff -r afe06dcc2b64 configure.in --- a/configure.in Tue Feb 19 16:59:10 2008 -0500 +++ b/configure.in Tue Feb 19 17:04:59 2008 -0500 @@ -473,6 +473,40 @@ AC_SUBST(AVAHI_CFLAGS) AC_SUBST(AVAHI_CFLAGS) AC_SUBST(AVAHI_LIBS) +dnl SELinux +AC_ARG_WITH(selinux, + [ --with-selinux use SELinux to manage security], + [], + [with_selinux=check]) + +SELINUX_CFLAGS= +SELINUX_LIBS= +if test "$with_selinux" != "no"; then + old_cflags="$CFLAGS" + old_libs="$LIBS" + if test "$with_selinux" = "check"; then + AC_CHECK_HEADER([selinux/selinux.h],[],[with_selinux=no]) + AC_CHECK_LIB(selinux, fgetfilecon,[],[with_selinux=no]) + if test "$with_selinux" != "no"; then + with_selinux="yes" + fi + else + AC_CHECK_HEADER([selinux/selinux.h],[], + [AC_MSG_ERROR([You must install the SELinux development package in order to compile libvirt])]) + AC_CHECK_LIB(selinux, fgetfilecon,[], + [AC_MSG_ERROR([You must install the SELinux development package in order to compile and run libvirt])]) + fi + CFLAGS="$old_cflags" + LIBS="$old_libs" +fi +if test "$with_selinux" = "yes"; then + SELINUX_LIBS="-lselinux" + AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, [whether SELinux is available for security]) +fi +AM_CONDITIONAL(HAVE_SELINUX, [test "$with_selinux" != "no"]) +AC_SUBST(SELINUX_CFLAGS) +AC_SUBST(SELINUX_LIBS) + dnl virsh libraries AC_CHECK_HEADERS([readline/readline.h]) @@ -745,6 +779,11 @@ else else AC_MSG_NOTICE([ polkit: no]) fi +if test "$with_selinux" = "yes" ; then +AC_MSG_NOTICE([ selinux: $SELINUX_CFLAGS $SELINUX_LIBS]) +else +AC_MSG_NOTICE([ selinux: no]) +fi AC_MSG_NOTICE([]) AC_MSG_NOTICE([Miscellaneous]) AC_MSG_NOTICE([]) diff -r afe06dcc2b64 libvirt.spec.in --- a/libvirt.spec.in Tue Feb 19 16:59:10 2008 -0500 +++ b/libvirt.spec.in Tue Feb 19 17:04:59 2008 -0500 @@ -41,6 +41,7 @@ BuildRequires: gettext BuildRequires: gettext BuildRequires: gnutls-devel BuildRequires: avahi-devel +BuildRequires: libselinux-devel BuildRequires: dnsmasq BuildRequires: bridge-utils BuildRequires: qemu diff -r afe06dcc2b64 src/Makefile.am --- a/src/Makefile.am Tue Feb 19 16:59:10 2008 -0500 +++ b/src/Makefile.am Tue Feb 19 17:04:59 2008 -0500 @@ -8,6 +8,7 @@ INCLUDES = \ $(LIBXML_CFLAGS) \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ + $(SELINUX_CFLAGS) \ -DBINDIR=\""$(libexecdir)"\" \ -DSBINDIR=\""$(sbindir)"\" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" \ @@ -67,7 +68,7 @@ SERVER_SOURCES = \ ../qemud/remote_protocol.c ../qemud/remote_protocol.h libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) -libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) \ +libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) $(SELINUX_LIBS) \ @CYGWIN_EXTRA_LIBADD@ ../gnulib/lib/libgnu.la libvirt_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvirt_sym.version \ -version-info @LIBVIRT_VERSION_INFO@ \ diff -r afe06dcc2b64 src/storage_backend.c --- a/src/storage_backend.c Tue Feb 19 16:59:10 2008 -0500 +++ b/src/storage_backend.c Tue Feb 19 17:04:59 2008 -0500 @@ -28,6 +28,14 @@ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> +#include <fcntl.h> +#include <stdint.h> +#include <sys/stat.h> +#include <dirent.h> + +#if HAVE_SELINUX +#include <selinux/selinux.h> +#endif #include "util.h" @@ -73,6 +81,468 @@ virStorageBackendToString(int type) { } +int +virStorageBackendUpdateVolInfo(virConnectPtr conn, + virStorageVolDefPtr vol, + int withCapacity) +{ + int ret, fd; + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot open volume '%s': %s"), + vol->target.path, strerror(errno)); + return -1; + } + + ret = virStorageBackendUpdateVolInfoFD(conn, + vol, + fd, + withCapacity); + + close(fd); + + return ret; +} + +int +virStorageBackendUpdateVolInfoFD(virConnectPtr conn, + virStorageVolDefPtr vol, + int fd, + int withCapacity) +{ + struct stat sb; +#if HAVE_SELINUX + security_context_t filecon = NULL; +#endif + + if (fstat(fd, &sb) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot stat file '%s': %s"), + vol->target.path, strerror(errno)); + return -1; + } + + if (!S_ISREG(sb.st_mode) && + !S_ISCHR(sb.st_mode) && + !S_ISBLK(sb.st_mode)) + return -2; + + if (S_ISREG(sb.st_mode)) { + vol->allocation = (unsigned long long)sb.st_blocks * + (unsigned long long)sb.st_blksize; + /* Regular files may be sparse, so logical size (capacity) is not same + * as actual allocation above + */ + if (withCapacity) + vol->capacity = sb.st_size; + } else { + off_t end; + /* XXX this is POSIX compliant, but doesn't work for for CHAR files, + * only BLOCK. There is a Linux specific ioctl() for getting + * size of both CHAR / BLOCK devices we should check for in + * configure + */ + end = lseek(fd, 0, SEEK_END); + if (end == (off_t)-1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot seek to end of file '%s':%s"), + vol->target.path, strerror(errno)); + return -1; + } + vol->allocation = end; + if (withCapacity) vol->capacity = end; + } + + vol->target.perms.mode = sb.st_mode; + vol->target.perms.uid = sb.st_uid; + vol->target.perms.gid = sb.st_gid; + + free(vol->target.perms.label); + vol->target.perms.label = NULL; + +#if HAVE_SELINUX + if (fgetfilecon(fd, &filecon) == -1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot get file context of %s: %s"), + vol->target.path, strerror(errno)); + return -1; + } + vol->target.perms.label = strdup(filecon); + if (vol->target.perms.label == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("context")); + return -1; + } + freecon(filecon); +#else + vol->target.perms.label = NULL; +#endif + + return 0; +} + +/* + * Given a volume path directly in /dev/XXX, iterate over the + * entries in the directory pool->def->target.path and find the + * first symlink pointing to the volume path. + * + * If, the target.path is /dev/, then return the original volume + * path. + * + * If no symlink is found, then return the original volume path + * + * Typically target.path is one of the /dev/disk/by-XXX dirs + * with stable paths. + */ +char * +virStorageBackendStablePath(virConnectPtr conn, + virStoragePoolObjPtr pool, + char *devpath) +{ + DIR *dh; + struct dirent *dent; + + /* Short circuit if pool has no target, or if its /dev */ + if (pool->def->target.path == NULL || + STREQ(pool->def->target.path, "/dev") || + STREQ(pool->def->target.path, "/dev/")) + return devpath; + + /* The pool is pointing somewhere like /dev/disk/by-path + * or /dev/disk/by-id, so we need to check all symlinks in + * the target directory and figure out which one points + * to this device node + */ + if ((dh = opendir(pool->def->target.path)) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot read dir %s: %s"), + pool->def->target.path, + strerror(errno)); + return NULL; + } + + while ((dent = readdir(dh)) != NULL) { + char *stablepath; + if (dent->d_name[0] == '.') + continue; + + stablepath = malloc(strlen(pool->def->target.path) + + 1 + strlen(dent->d_name) + 1); + if (stablepath == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("path")); + closedir(dh); + return NULL; + } + + strcpy(stablepath, pool->def->target.path); + strcat(stablepath, "/"); + strcat(stablepath, dent->d_name); + + if (virFileLinkPointsTo(stablepath, devpath)) { + closedir(dh); + return stablepath; + } + + free(stablepath); + } + + closedir(dh); + + /* Couldn't find any matching stable link so give back + * the original non-stable dev path + */ + return devpath; +} + +/* + * Run an external program. + * + * Read its output and apply a series of regexes to each line + * When the entire set of regexes has matched consequetively + * then run a callback passing in all the matches + */ +int +virStorageBackendRunProgRegex(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + int nregex, + const char **regex, + int *nvars, + virStorageBackendListVolRegexFunc func, + void *data) +{ + int child = 0, fd = -1, exitstatus, err, failed = 1; + FILE *list = NULL; + regex_t *reg; + regmatch_t *vars = NULL; + char line[1024]; + int maxReg = 0, i, j; + int totgroups = 0, ngroup = 0, maxvars = 0; + char **groups; + + /* Compile all regular expressions */ + if ((reg = calloc(nregex, sizeof(*reg))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("regex")); + return -1; + } + + for (i = 0 ; i < nregex ; i++) { + err = regcomp(®[i], regex[i], REG_EXTENDED); + if (err != 0) { + char error[100]; + regerror(err, ®[i], error, sizeof(error)); + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to compile regex %s"), error); + for (j = 0 ; j <= i ; j++) + regfree(®[j]); + free(reg); + return -1; + } + + totgroups += nvars[i]; + if (nvars[i] > maxvars) + maxvars = nvars[i]; + + } + + /* Storage for matched variables */ + if ((groups = calloc(totgroups, sizeof(*groups))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("regex groups")); + goto cleanup; + } + if ((vars = calloc(maxvars+1, sizeof(*vars))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("regex groups")); + goto cleanup; + } + + + /* Run the program and capture its output */ + if (virExec(conn, (char**)prog, &child, -1, &fd, NULL) < 0) { + goto cleanup; + } + + if ((list = fdopen(fd, "r")) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot read fd")); + goto cleanup; + } + + while (fgets(line, sizeof(line), list) != NULL) { + /* Strip trailing newline */ + int len = strlen(line); + if (len && line[len-1] == '\n') + line[len-1] = '\0'; + + for (i = 0 ; i <= maxReg && i < nregex ; i++) { + if (regexec(®[i], line, nvars[i]+1, vars, 0) == 0) { + maxReg++; + + if (i == 0) + ngroup = 0; + + /* NULL terminate each captured group in the line */ + for (j = 0 ; j < nvars[i] ; j++) { + /* NB vars[0] is the full pattern, so we offset j by 1 */ + line[vars[j+1].rm_eo] = '\0'; + if ((groups[ngroup++] = + strdup(line + vars[j+1].rm_so)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("regex groups")); + goto cleanup; + } + } + + /* We're matching on the last regex, so callback time */ + if (i == (nregex-1)) { + if (((*func)(conn, pool, groups, data)) < 0) + goto cleanup; + + /* Release matches & restart to matching the first regex */ + for (j = 0 ; j < totgroups ; j++) { + free(groups[j]); + groups[j] = NULL; + } + maxReg = 0; + ngroup = 0; + } + } + } + } + + failed = 0; + + cleanup: + if (groups) { + for (j = 0 ; j < totgroups ; j++) + free(groups[j]); + free(groups); + } + free(vars); + + for (i = 0 ; i < nregex ; i++) + regfree(®[i]); + + free(reg); + + if (list) + fclose(list); + else + close(fd); + + while ((err = waitpid(child, &exitstatus, 0) == -1) && errno == EINTR); + + /* Don't bother checking exit status if we already failed */ + if (failed) + return -1; + + if (err == -1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to wait for command: %s"), + strerror(errno)); + return -1; + } else { + if (WIFEXITED(exitstatus)) { + if (WEXITSTATUS(exitstatus) != 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("non-zero exit status from command %d"), + WEXITSTATUS(exitstatus)); + return -1; + } + } else { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("command did not exit cleanly")); + return -1; + } + } + + return 0; +} + +/* + * Run an external program and read from its standard output + * a stream of tokens from IN_STREAM, applying FUNC to + * each successive sequence of N_COLUMNS tokens. + * If FUNC returns < 0, stop processing input and return -1. + * Return -1 if N_COLUMNS == 0. + * Return -1 upon memory allocation error. + * If the number of input tokens is not a multiple of N_COLUMNS, + * then the final FUNC call will specify a number smaller than N_COLUMNS. + * If there are no input tokens (empty input), call FUNC with N_COLUMNS == 0. + */ +int +virStorageBackendRunProgNul(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + size_t n_columns, + virStorageBackendListVolNulFunc func, + void *data) +{ + size_t n_tok = 0; + int child = 0, fd = -1, exitstatus; + FILE *fp = NULL; + char **v; + int err = -1; + int w_err; + int i; + + if (n_columns == 0) + return -1; + + if (n_columns > SIZE_MAX / sizeof *v + || (v = malloc (n_columns * sizeof *v)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("n_columns too large")); + return -1; + } + for (i = 0; i < n_columns; i++) + v[i] = NULL; + + /* Run the program and capture its output */ + if (virExec(conn, (char**)prog, &child, -1, &fd, NULL) < 0) { + goto cleanup; + } + + if ((fp = fdopen(fd, "r")) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot read fd")); + goto cleanup; + } + + while (1) { + char *buf = NULL; + size_t buf_len = 0; + /* Be careful: even when it returns -1, + this use of getdelim allocates memory. */ + ssize_t tok_len = getdelim (&buf, &buf_len, 0, fp); + v[n_tok] = buf; + if (tok_len < 0) { + /* Maybe EOF, maybe an error. + If n_tok > 0, then we know it's an error. */ + if (n_tok && func (conn, pool, n_tok, v, data) < 0) + goto cleanup; + break; + } + ++n_tok; + if (n_tok == n_columns) { + if (func (conn, pool, n_tok, v, data) < 0) + goto cleanup; + n_tok = 0; + for (i = 0; i < n_columns; i++) { + free (v[i]); + v[i] = NULL; + } + } + } + + if (feof (fp)) + err = 0; + else + virStorageReportError (conn, VIR_ERR_INTERNAL_ERROR, + _("read error: %s"), strerror (errno)); + + cleanup: + for (i = 0; i < n_columns; i++) + free (v[i]); + free (v); + + if (fp) + fclose (fp); + else + close (fd); + + while ((w_err = waitpid (child, &exitstatus, 0) == -1) && errno == EINTR) + /* empty */ ; + + /* Don't bother checking exit status if we already failed */ + if (err < 0) + return -1; + + if (w_err == -1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to wait for command: %s"), + strerror(errno)); + return -1; + } else { + if (WIFEXITED(exitstatus)) { + if (WEXITSTATUS(exitstatus) != 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("non-zero exit status from command %d"), + WEXITSTATUS(exitstatus)); + return -1; + } + } else { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("command did not exit cleanly")); + return -1; + } + } + + return 0; +} + + /* * vim: set tabstop=4: * vim: set shiftwidth=4: diff -r afe06dcc2b64 src/storage_backend.h --- a/src/storage_backend.h Tue Feb 19 16:59:10 2008 -0500 +++ b/src/storage_backend.h Tue Feb 19 17:04:59 2008 -0500 @@ -103,6 +103,44 @@ int virStorageBackendFromString(const ch int virStorageBackendFromString(const char *type); const char *virStorageBackendToString(int type); +int virStorageBackendUpdateVolInfo(virConnectPtr conn, + virStorageVolDefPtr vol, + int withCapacity); + +int virStorageBackendUpdateVolInfoFD(virConnectPtr conn, + virStorageVolDefPtr vol, + int fd, + int withCapacity); + +char *virStorageBackendStablePath(virConnectPtr conn, + virStoragePoolObjPtr pool, + char *devpath); + +typedef int (*virStorageBackendListVolRegexFunc)(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + void *data); +typedef int (*virStorageBackendListVolNulFunc)(virConnectPtr conn, + virStoragePoolObjPtr pool, + size_t n_tokens, + char **const groups, + void *data); + +int virStorageBackendRunProgRegex(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + int nregex, + const char **regex, + int *nvars, + virStorageBackendListVolRegexFunc func, + void *data); + +int virStorageBackendRunProgNul(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + size_t n_columns, + virStorageBackendListVolNulFunc func, + void *data); #endif /* __VIR_STORAGE_BACKEND_H__ */ diff -r afe06dcc2b64 tests/Makefile.am --- a/tests/Makefile.am Tue Feb 19 16:59:10 2008 -0500 +++ b/tests/Makefile.am Tue Feb 19 17:04:59 2008 -0500 @@ -20,6 +20,7 @@ INCLUDES = \ $(LIBXML_CFLAGS) \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ + $(SELINUX_CFLAGS) \ -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=199506L \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" \ $(COVERAGE_CFLAGS) \ @@ -31,6 +32,7 @@ LDADDS = \ $(LIBXML_LIBS) \ $(GNUTLS_LIBS) \ $(SASL_LIBS) \ + $(SELINUX_LIBS) \ $(WARN_CFLAGS) \ $(LIBVIRT) \ ../gnulib/lib/libgnu.la \ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This patch implements 3 storage pools in one go. This provides the base level 'directory' storage pool, where an existing directory contains files as volumes. This is guarenteed to be available on any OS since it just uses plain POSIX apis for creation/deletion. If qemu-img or qcow2-create command line tools are available it can also create various non-raw fileformats such as QCow2, VMDK. If the mount/unmount commands are available, the 'fs' and 'netfs' pools become available. This allow a local disk, or remote filesystem to be mounted on the local filesystem, and files managed within. This driver contains code for probing the non-raw file formats to determine their logical capacity. b/docs/storage/pool-dir.xml | 6 b/docs/storage/pool-fs.xml | 15 b/docs/storage/pool-netfs.xml | 16 b/docs/storage/vol-cow.xml | 10 b/docs/storage/vol-qcow.xml | 10 b/docs/storage/vol-qcow2.xml | 10 b/docs/storage/vol-raw.xml | 7 b/docs/storage/vol-sparse.xml | 7 b/docs/storage/vol-vmdk.xml | 10 b/src/storage_backend_fs.c | 1047 ++++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_fs.h | 49 + configure.in | 52 ++ libvirt.spec.in | 24 src/Makefile.am | 1 src/storage_backend.c | 33 + 15 files changed, 1297 insertions(+) diff -r 739490b4a2f6 configure.in --- a/configure.in Thu Feb 07 12:59:42 2008 -0500 +++ b/configure.in Thu Feb 07 13:44:25 2008 -0500 @@ -551,6 +551,52 @@ AC_SUBST(VIRSH_LIBS) AC_SUBST(WITH_XEN) AC_SUBST(LIBVIRT_FEATURES) + + +dnl +dnl Storage driver checks +dnl + +AC_ARG_WITH(storage-fs, +[ --with-storage-fs with FileSystem backend for the storage driver (on)],[],[with_storage_fs=check]) + +if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then + AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(UMOUNT, [umount], [], [$PATH:/sbin:/usr/sbin]) + if test "$with_storage_fs" = "yes" ; then + if test -z "$MOUNT" ; then AC_MSG_ERROR(We need mount for FS storage driver) ; fi + if test -z "$UMOUNT" ; then AC_MSG_ERROR(We need mount for FS storage driver) ; fi + else + if test -z "$MOUNT" ; then with_storage_fs=no ; fi + if test -z "$UMOUNT" ; then with_storage_fs=no ; fi + + if test "$with_storage_fs" = "check" ; then with_storage_fs=yes ; fi + fi + + if test "$with_storage_fs" = "yes" ; then + AC_DEFINE_UNQUOTED(WITH_STORAGE_FS, 1, [whether FS backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([MOUNT],["$MOUNT"], + [Location or name of the mount program]) + AC_DEFINE_UNQUOTED([UMOUNT],["$UMOUNT"], + [Location or name of the mount program]) + fi +fi +AM_CONDITIONAL(WITH_STORAGE_FS, [test "$with_storage_fs" = "yes"]) + +AC_PATH_PROG(QEMU_IMG, [qemu-img], [], [$PATH:/sbin:/usr/sbin:/bin:/usr/bin]) +if test -n "$QEMU_IMG" ; then + AC_DEFINE_UNQUOTED(HAVE_QEMU_IMG, 1, [whether qemu-img is available for non-raw files]) + AC_DEFINE_UNQUOTED([QEMU_IMG],["$QEMU_IMG"], + [Location or name of the qemu-img program]) +fi + +AC_PATH_PROG(QCOW_CREATE, [qcow-create], [], [$PATH:/sbin:/usr/sbin:/bin:/usr/bin]) +if test -n "$QCOW_CREATE" ; then + AC_DEFINE_UNQUOTED(HAVE_QCOW_CREATE, 1, [whether qcow-create is available for non-raw files]) + AC_DEFINE_UNQUOTED([QCOW_CREATE],["$QCOW_CREATE"], + [Location or name of the qcow-create program]) +fi + dnl dnl check for python @@ -760,6 +806,12 @@ AC_MSG_NOTICE([ Remote: $with_remote]) AC_MSG_NOTICE([ Remote: $with_remote]) AC_MSG_NOTICE([Libvirtd: $with_libvirtd]) AC_MSG_NOTICE([]) +AC_MSG_NOTICE([Storage Drivers]) +AC_MSG_NOTICE([]) +AC_MSG_NOTICE([ Dir: yes]) +AC_MSG_NOTICE([ FS: $with_storage_fs]) +AC_MSG_NOTICE([ NetFS: $with_storage_fs]) +AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ libxml: $LIBXML_CFLAGS $LIBXML_LIBS]) diff -r 739490b4a2f6 docs/storage/pool-dir.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-dir.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,6 @@ +<pool type="dir"> + <name>virtimages</name> + <target> + <path>/var/lib/virt/images</path> + </target> +</pool> diff -r 739490b4a2f6 docs/storage/pool-fs.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-fs.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,15 @@ +<pool type="fs"> + <name>virtimages</name> + <source> + <device>/dev/VolGroup00/VirtImages</device> + </source> + <target> + <path>/var/lib/virt/images</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + <label>xen_image_t</label> + </permissions> + </target> +</pool> diff -r 739490b4a2f6 docs/storage/pool-netfs.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-netfs.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,16 @@ +<pool type="netfs"> + <name>virtimages</name> + <source> + <host>nfs.example.com</host> + <path>/var/lib/virt/images</path> + </source> + <target> + <path>/var/lib/virt/images</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + <label>xen_image_t</label> + </permissions> + </target> +</pool> diff -r 739490b4a2f6 docs/storage/vol-cow.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-cow.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>cow.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="cow"/> + </target> +</volume> diff -r 739490b4a2f6 docs/storage/vol-qcow.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-qcow.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>qcow.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="qcow"/> + </target> +</volume> diff -r 739490b4a2f6 docs/storage/vol-qcow2.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-qcow2.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>qcow2.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="qcow2"/> + </target> +</volume> diff -r 739490b4a2f6 docs/storage/vol-raw.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-raw.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,7 @@ +<volume type="file"> + <name>raw.img</name> + <storage> + <allocation unit="M">10</allocation> + <capacity unit="M">1000</capacity> + </storage> +</volume> diff -r 739490b4a2f6 docs/storage/vol-sparse.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-sparse.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,7 @@ +<volume type="file"> + <name>sparse.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> +</volume> diff -r 739490b4a2f6 docs/storage/vol-vmdk.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-vmdk.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>vmdk3.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="vmdk"/> + </target> +</volume> diff -r 739490b4a2f6 libvirt.spec.in --- a/libvirt.spec.in Thu Feb 07 12:59:42 2008 -0500 +++ b/libvirt.spec.in Thu Feb 07 13:44:25 2008 -0500 @@ -6,6 +6,12 @@ %else %define with_polkit 0 %define with_proxy yes +%endif + +%if "%{fedora}" +%define with_qemu 1 +%else +%define with_qemu 0 %endif Summary: Library providing a simple API virtualization @@ -34,6 +40,15 @@ Requires: cyrus-sasl-md5 %if %{with_polkit} Requires: PolicyKit >= 0.6 %endif +# For mount/umount in FS driver +BuildRequires: util-linux +%if %{with_qemu} +# From QEMU RPMs +Requires: /usr/bin/qemu-img +%else +# From Xen RPMs +Requires: /usr/sbin/qcow-create +%endif BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -48,6 +63,15 @@ BuildRequires: cyrus-sasl-devel BuildRequires: cyrus-sasl-devel %if %{with_polkit} BuildRequires: PolicyKit-devel >= 0.6 +%endif +# For mount/umount in FS driver +BuildRequires: util-linux +%if %{with_qemu} +# From QEMU RPMs +BuildRequires: /usr/bin/qemu-img +%else +# From Xen RPMs +BuildRequires: /usr/sbin/qcow-create %endif Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 diff -r 739490b4a2f6 src/Makefile.am --- a/src/Makefile.am Thu Feb 07 12:59:42 2008 -0500 +++ b/src/Makefile.am Thu Feb 07 13:44:25 2008 -0500 @@ -63,6 +63,7 @@ CLIENT_SOURCES = \ storage_conf.h storage_conf.c \ storage_driver.h storage_driver.c \ storage_backend.h storage_backend.c \ + storage_backend_fs.h storage_backend_fs.c \ util.c util.h SERVER_SOURCES = \ diff -r 739490b4a2f6 src/storage_backend.c --- a/src/storage_backend.c Thu Feb 07 12:59:42 2008 -0500 +++ b/src/storage_backend.c Thu Feb 07 13:44:25 2008 -0500 @@ -40,9 +40,23 @@ #include "util.h" #include "storage_backend.h" +#include "storage_backend_fs.h" + +static virStorageBackendPtr backends[] = { + &virStorageBackendDirectory, +#if WITH_STORAGE_FS + &virStorageBackendFileSystem, + &virStorageBackendNetFileSystem, +#endif +}; virStorageBackendPtr virStorageBackendForType(int type) { + unsigned int i; + for (i = 0 ; i < (sizeof(backends)/sizeof(backends[0])) ; i++) + if (backends[i]->type == type) + return backends[i]; + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "missing backend for pool type %d", type); return NULL; } @@ -63,11 +77,30 @@ virStorageBackendVolOptionsPtr virStorag int virStorageBackendFromString(const char *type) { + if (STREQ(type, "dir")) + return VIR_STORAGE_POOL_DIR; +#if WITH_STORAGE_FS + if (STREQ(type, "fs")) + return VIR_STORAGE_POOL_FS; + if (STREQ(type, "netfs")) + return VIR_STORAGE_POOL_NETFS; +#endif virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %s", type); return -1; } const char *virStorageBackendToString(int type) { + switch (type) { + case VIR_STORAGE_POOL_DIR: + return "dir"; +#if WITH_STORAGE_FS + case VIR_STORAGE_POOL_FS: + return "fs"; + case VIR_STORAGE_POOL_NETFS: + return "netfs"; +#endif + } + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %d", type); return NULL; } diff -r 739490b4a2f6 src/storage_backend_fs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_fs.c Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,1047 @@ +/* + * storage_backend_fs.c: storage backend for FS and directory handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <sys/statvfs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <endian.h> +#include <byteswap.h> +#include <mntent.h> +#include <string.h> + +#include "storage_backend_fs.h" +#include "storage_conf.h" +#include "util.h" +#include "config.h" + + +enum { + VIR_STORAGE_POOL_FS_AUTO = 0, + VIR_STORAGE_POOL_FS_EXT2, + VIR_STORAGE_POOL_FS_EXT3, + VIR_STORAGE_POOL_FS_EXT4, + VIR_STORAGE_POOL_FS_UFS, + VIR_STORAGE_POOL_FS_ISO, + VIR_STORAGE_POOL_FS_UDF, + VIR_STORAGE_POOL_FS_GFS, + VIR_STORAGE_POOL_FS_GFS2, + VIR_STORAGE_POOL_FS_VFAT, + VIR_STORAGE_POOL_FS_HFSPLUS, + VIR_STORAGE_POOL_FS_XFS, +}; + +enum { + VIR_STORAGE_POOL_NETFS_AUTO = 0, + VIR_STORAGE_POOL_NETFS_NFS, +}; + + + +enum { + VIR_STORAGE_VOL_RAW, + VIR_STORAGE_VOL_BOCHS, + VIR_STORAGE_VOL_CLOOP, + VIR_STORAGE_VOL_COW, + VIR_STORAGE_VOL_DMG, + VIR_STORAGE_VOL_ISO, + VIR_STORAGE_VOL_QCOW, + VIR_STORAGE_VOL_QCOW2, + VIR_STORAGE_VOL_VMDK, + VIR_STORAGE_VOL_VPC, +}; + +/* Either 'magic' or 'extension' *must* be provided */ +struct { + int type; /* One of the constants above */ + const char *magic; /* Optional string of file magic + * to check at head of file */ + const char *extension; /* Optional file extension to check */ + int endian; /* Endianness of file format */ + int versionOffset; /* Byte offset from start of file + * where we find version number, + * -1 to skip version test */ + int versionNumber; /* Version number to validate */ + int sizeOffset; /* Byte offset from start of file + * where we find capacity info, + * -1 to use st_size as capacity */ + int sizeBytes; /* Number of bytes for size field */ + int sizeMultiplier; /* A scaling factor if size is not in bytes */ +} fileTypeInfo[] = { + /* Bochs */ + /* XXX Untested + { VIR_STORAGE_VOL_BOCHS, "Bochs Virtual HD Image", NULL, + __LITTLE_ENDIAN, 64, 0x20000, + 32+16+16+4+4+4+4+4, 8, 1 },*/ + /* CLoop */ + /* XXX Untested + { VIR_STORAGE_VOL_CLOOP, "#!/bin/sh\n#V2.0 Format\nmodprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n", NULL, + __LITTLE_ENDIAN, -1, 0, + -1, 0, 0 }, */ + /* Cow */ + { VIR_STORAGE_VOL_COW, "OOOM", NULL, + __BIG_ENDIAN, 4, 2, + 4+4+1024+4, 8, 1 }, + /* DMG */ + /* XXX QEMU says there's no magic for dmg, but we should check... */ + { VIR_STORAGE_VOL_DMG, NULL, ".dmg", + 0, -1, 0, + -1, 0, 0 }, + /* XXX there's probably some magic for iso we can validate too... */ + { VIR_STORAGE_VOL_ISO, NULL, ".iso", + 0, -1, 0, + -1, 0, 0 }, + /* Parallels */ + /* XXX Untested + { VIR_STORAGE_VOL_PARALLELS, "WithoutFreeSpace", NULL, + __LITTLE_ENDIAN, 16, 2, + 16+4+4+4+4, 4, 512 }, + */ + /* QCow */ + { VIR_STORAGE_VOL_QCOW, "QFI", NULL, + __BIG_ENDIAN, 4, 1, + 4+4+8+4+4, 8, 1 }, + /* QCow 2 */ + { VIR_STORAGE_VOL_QCOW2, "QFI", NULL, + __BIG_ENDIAN, 4, 2, + 4+4+8+4+4, 8, 1 }, + /* VMDK 3 */ + /* XXX Untested + { VIR_STORAGE_VOL_VMDK, "COWD", NULL, + __LITTLE_ENDIAN, 4, 1, + 4+4+4, 4, 512 }, + */ + /* VMDK 4 */ + { VIR_STORAGE_VOL_VMDK, "KDMV", NULL, + __LITTLE_ENDIAN, 4, 1, + 4+4+4, 8, 512 }, + /* Connectix / VirtualPC */ + /* XXX Untested + { VIR_STORAGE_VOL_VPC, "conectix", NULL, + __BIG_ENDIAN, -1, 0, + -1, 0, 0}, + */ +}; + + + + +static int virStorageBackendFileSystemVolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_VOL_RAW; + + if (STREQ(format, "raw")) + return VIR_STORAGE_VOL_RAW; + if (STREQ(format, "bochs")) + return VIR_STORAGE_VOL_BOCHS; + if (STREQ(format, "cow")) + return VIR_STORAGE_VOL_COW; + if (STREQ(format, "cloop")) + return VIR_STORAGE_VOL_CLOOP; + if (STREQ(format, "dmg")) + return VIR_STORAGE_VOL_DMG; + if (STREQ(format, "iso")) + return VIR_STORAGE_VOL_ISO; + if (STREQ(format, "qcow")) + return VIR_STORAGE_VOL_QCOW; + if (STREQ(format, "qcow2")) + return VIR_STORAGE_VOL_QCOW2; + if (STREQ(format, "vmdk")) + return VIR_STORAGE_VOL_VMDK; + if (STREQ(format, "vpc")) + return VIR_STORAGE_VOL_VPC; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %s", format); + return -1; +} + +static const char *virStorageBackendFileSystemVolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_VOL_RAW: + return "raw"; + case VIR_STORAGE_VOL_BOCHS: + return "bochs"; + case VIR_STORAGE_VOL_CLOOP: + return "cloop"; + case VIR_STORAGE_VOL_COW: + return "cow"; + case VIR_STORAGE_VOL_DMG: + return "dmg"; + case VIR_STORAGE_VOL_ISO: + return "iso"; + case VIR_STORAGE_VOL_QCOW: + return "qcow"; + case VIR_STORAGE_VOL_QCOW2: + return "qcow2"; + case VIR_STORAGE_VOL_VMDK: + return "vmdk"; + case VIR_STORAGE_VOL_VPC: + return "vpc"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %d", format); + return NULL; +} + + +static int virStorageBackendFileSystemPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_FS_AUTO; + + if (STREQ(format, "auto")) + return VIR_STORAGE_POOL_FS_AUTO; + if (STREQ(format, "ext2")) + return VIR_STORAGE_POOL_FS_EXT2; + if (STREQ(format, "ext3")) + return VIR_STORAGE_POOL_FS_EXT3; + if (STREQ(format, "ext4")) + return VIR_STORAGE_POOL_FS_EXT4; + if (STREQ(format, "ufs")) + return VIR_STORAGE_POOL_FS_UFS; + if (STREQ(format, "iso9660")) + return VIR_STORAGE_POOL_FS_ISO; + if (STREQ(format, "udf")) + return VIR_STORAGE_POOL_FS_UDF; + if (STREQ(format, "gfs")) + return VIR_STORAGE_POOL_FS_GFS; + if (STREQ(format, "gfs2")) + return VIR_STORAGE_POOL_FS_GFS2; + if (STREQ(format, "vfat")) + return VIR_STORAGE_POOL_FS_VFAT; + if (STREQ(format, "hfs+")) + return VIR_STORAGE_POOL_FS_HFSPLUS; + if (STREQ(format, "xfs")) + return VIR_STORAGE_POOL_FS_XFS; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %s", format); + return -1; +} + +static const char *virStorageBackendFileSystemPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_FS_AUTO: + return "auto"; + case VIR_STORAGE_POOL_FS_EXT2: + return "ext2"; + case VIR_STORAGE_POOL_FS_EXT3: + return "ext3"; + case VIR_STORAGE_POOL_FS_EXT4: + return "ext4"; + case VIR_STORAGE_POOL_FS_UFS: + return "ufs"; + case VIR_STORAGE_POOL_FS_ISO: + return "iso"; + case VIR_STORAGE_POOL_FS_UDF: + return "udf"; + case VIR_STORAGE_POOL_FS_GFS: + return "gfs"; + case VIR_STORAGE_POOL_FS_GFS2: + return "gfs2"; + case VIR_STORAGE_POOL_FS_VFAT: + return "vfat"; + case VIR_STORAGE_POOL_FS_HFSPLUS: + return "hfs+"; + case VIR_STORAGE_POOL_FS_XFS: + return "xfs"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %d", format); + return NULL; +} + + +static int virStorageBackendFileSystemNetPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_NETFS_AUTO; + + if (STREQ(format, "auto")) + return VIR_STORAGE_POOL_NETFS_AUTO; + if (STREQ(format, "nfs")) + return VIR_STORAGE_POOL_NETFS_NFS; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %s", format); + return -1; +} + +static const char *virStorageBackendFileSystemNetPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_NETFS_AUTO: + return "auto"; + case VIR_STORAGE_POOL_NETFS_NFS: + return "nfs"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %d", format); + return NULL; +} + + +/** + * Probe the header of a file to determine what type of disk image + * it is, and info about its capacity if available. + */ +static int virStorageBackendProbeFile(virConnectPtr conn, + virStorageVolDefPtr def) { + int fd; + char head[4096]; + int len, i, ret; + + if ((fd = open(def->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot open volume '%s': %d (%s)", + def->target.path, errno, strerror(errno)); + return -1; + } + + if ((ret = virStorageBackendUpdateVolInfoFD(conn, def, fd, 1)) < 0) { + close(fd); + return ret; /* Take care to propagate ret, it is not always -1 */ + } + + if ((len = read(fd, head, sizeof(head))) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read header '%s': %d (%s)", + def->target, errno, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + + /* First check file magic */ + for (i = 0 ; i < sizeof(fileTypeInfo)/sizeof(fileTypeInfo[0]) ; i++) { + int mlen; + if (fileTypeInfo[i].magic == NULL) + continue; + + /* Validate magic data */ + mlen = strlen(fileTypeInfo[i].magic); + if (mlen > len) + continue; + if (memcmp(head, fileTypeInfo[i].magic, mlen) != 0) + continue; + + /* Validate version number info */ + if (fileTypeInfo[i].versionNumber != -1) { + int version; + + if (fileTypeInfo[i].endian == __LITTLE_ENDIAN) { + version = (head[fileTypeInfo[i].versionOffset+3] << 24) | + (head[fileTypeInfo[i].versionOffset+2] << 16) | + (head[fileTypeInfo[i].versionOffset+1] << 8) | + head[fileTypeInfo[i].versionOffset]; + } else { + version = (head[fileTypeInfo[i].versionOffset] << 24) | + (head[fileTypeInfo[i].versionOffset+1] << 16) | + (head[fileTypeInfo[i].versionOffset+2] << 8) | + head[fileTypeInfo[i].versionOffset+3]; + } + if (version != fileTypeInfo[i].versionNumber) + continue; + } + + /* Optionally extract capacity from file */ + if (fileTypeInfo[i].sizeOffset != -1) { + if (fileTypeInfo[i].endian == __LITTLE_ENDIAN) { + def->capacity = + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7] << 56) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 48) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 40) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 32) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 24) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 16) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 8) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset]); + def->capacity *= fileTypeInfo[i].sizeMultiplier; + } else { + def->capacity = + ((unsigned long long)head[fileTypeInfo[i].sizeOffset] << 56) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 48) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 40) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 32) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 24) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 16) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 8) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]); + def->capacity *= fileTypeInfo[i].sizeMultiplier; + } + } + + /* Validation passed, we know the file format now */ + def->target.format = fileTypeInfo[i].type; + return 0; + } + + /* No magic, so check file extension */ + for (i = 0 ; i < sizeof(fileTypeInfo)/sizeof(fileTypeInfo[0]) ; i++) { + if (fileTypeInfo[i].extension == NULL) + continue; + + if (!virFileHasSuffix(def->target.path, fileTypeInfo[i].extension)) + continue; + + def->target.format = fileTypeInfo[i].type; + return 0; + } + + /* All fails, so call it a raw file */ + def->target.format = VIR_STORAGE_VOL_RAW; + return 0; +} + +#if WITH_STORAGE_FS +/** + * @conn connection to report errors against + * @pool storage pool to check for status + * + * Determine if a storage pool is already mounted + * + * Return 0 if not mounted, 1 if mounted, -1 on error + */ +static int virStorageBackendFileSystemIsMounted(virConnectPtr conn, + virStoragePoolObjPtr pool) { + FILE *mtab; + struct mntent *ent; + + if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read %s: %s", + _PATH_MOUNTED, strerror(errno)); + return -1; + } + + while ((ent = getmntent(mtab)) != NULL) { + if (STREQ(ent->mnt_dir, pool->def->target.path)) { + fclose(mtab); + return 1; + } + } + + fclose(mtab); + return 0; +} + +/** + * @conn connection to report errors against + * @pool storage pool to mount + * + * Ensure that a FS storage pool is mounted on its target location. + * If already mounted, this is a no-op + * + * Returns 0 if successfully mounted, -1 on error + */ +static int virStorageBackendFileSystemMount(virConnectPtr conn, + virStoragePoolObjPtr pool) { + char *src; + const char *mntargv[] = { + MOUNT, + "-t", + pool->def->type == VIR_STORAGE_POOL_FS ? + virStorageBackendFileSystemPoolFormatToString(conn, pool->def->source.format) : + virStorageBackendFileSystemNetPoolFormatToString(conn, pool->def->source.format), + NULL, /* Fill in shortly - careful not to add extra fields before this */ + pool->def->target.path, + NULL, + }; + int ret; + + if (pool->def->type == VIR_STORAGE_POOL_NETFS) { + if (pool->def->source.host.name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source host"); + return -1; + } + if (pool->def->source.dir == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source path"); + return -1; + } + } else { + if (pool->def->source.ndevice != 1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source device"); + return -1; + } + } + + /* Short-circuit is already mounted */ + if ((ret = virStorageBackendFileSystemIsMounted(conn, pool)) != 0) { + if (ret < 0) + return -1; + else + return 0; + } + + if (pool->def->type == VIR_STORAGE_POOL_NETFS) { + src = malloc(strlen(pool->def->source.host.name) + 1 + strlen(pool->def->source.dir) + 1); + strcpy(src, pool->def->source.host.name); + strcat(src, ":"); + strcat(src, pool->def->source.dir); + } else { + src = strdup(pool->def->source.devices[0].path); + } + if (src == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "source"); + return -1; + } + mntargv[3] = src; + + if (virRun(conn, (char**)mntargv, NULL) < 0) { + free(src); + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot mount %s on %s: %d", + src, pool->def->target.path, strerror(errno)); + return -1; + } + free(src); + return 0; +} + +/** + * @conn connection to report errors against + * @pool storage pool to unmount + * + * Ensure that a FS storage pool is not mounted on its target location. + * If already unmounted, this is a no-op + * + * Returns 0 if successfully unmounted, -1 on error + */ +static int virStorageBackendFileSystemUnmount(virConnectPtr conn, + virStoragePoolObjPtr pool) { + const char *mntargv[3]; + int ret; + + if (pool->def->type == VIR_STORAGE_POOL_NETFS) { + if (pool->def->source.host.name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source host"); + return -1; + } + if (pool->def->source.dir == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source dir"); + return -1; + } + } else { + if (pool->def->source.ndevice != 1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source device"); + return -1; + } + } + + /* Short-circuit is already unmounted */ + if ((ret = virStorageBackendFileSystemIsMounted(conn, pool)) != 1) { + if (ret < 0) + return -1; + else + return 0; + } + + mntargv[0] = UMOUNT; + mntargv[1] = pool->def->target.path; + mntargv[2] = NULL; + + if (virRun(conn, (char**)mntargv, NULL) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot unmount %s: %d", + pool->def->target.path, strerror(errno)); + return -1; + } + return 0; +} +#endif /* WITH_STORAGE_FS */ + + +/** + * @conn connection to report errors against + * @pool storage pool to start + * + * Starts a directory or FS based storage pool. + * + * - If it is a FS based pool, mounts the unlying source device on the pool + * + * Returns 0 on success, -1 on error + */ +#if WITH_STORAGE_FS +static int virStorageBackendFileSystemStart(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + if (pool->def->type != VIR_STORAGE_POOL_DIR && + virStorageBackendFileSystemMount(conn, pool) < 0) + return -1; + + return 0; +} +#endif /* WITH_STORAGE_FS */ + + +/** + * @conn connection to report errors against + * @pool storage pool to build + * + * Build a directory or FS based storage pool. + * + * - If it is a FS based pool, mounts the unlying source device on the pool + * + * Returns 0 on success, -1 on error + */ +static int virStorageBackendFileSystemBuild(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + if (virFileMakePath(pool->def->target.path) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot create path '%s': %d (%s)", + pool->def->target, errno, strerror(errno)); + return -1; + } + + return 0; +} + + +/** + * Iterate over the pool's directory and enumerate all disk images + * within it. This is non-recursive. + */ +static int virStorageBackendFileSystemRefresh(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + DIR *dir; + struct dirent *ent; + struct statvfs sb; + + if (!(dir = opendir(pool->def->target.path))) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot open path '%s': %d (%s)", + pool->def->target.path, errno, strerror(errno)); + goto cleanup; + } + + while ((ent = readdir(dir)) != NULL) { + virStorageVolDefPtr vol; + int ret; + + vol = calloc(1, sizeof(virStorageVolDef)); + if (vol == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + goto cleanup; + } + + vol->name = strdup(ent->d_name); + if (vol->name == NULL) { + free(vol); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume name"); + goto cleanup; + } + + vol->target.format = VIR_STORAGE_VOL_RAW; /* Real value is filled in during probe */ + vol->target.path = malloc(strlen(pool->def->target.path) + 1 + strlen(vol->name) + 1); + if (vol->target.path == NULL) { + free(vol->target.path); + free(vol); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume name"); + goto cleanup; + } + strcpy(vol->target.path, pool->def->target.path); + strcat(vol->target.path, "/"); + strcat(vol->target.path, vol->name); + if ((vol->key = strdup(vol->target.path)) == NULL) { + free(vol->name); + free(vol->target.path); + free(vol); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume key"); + goto cleanup; + } + + if ((ret = virStorageBackendProbeFile(conn, vol) < 0)) { + free(vol->key); + free(vol->name); + free(vol->target.path); + free(vol); + if (ret == -1) + goto cleanup; + else /* Silently ignore non-regular files, eg '.' '..', 'lost+found' */ + continue; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + continue; + } + closedir(dir); + + + if (statvfs(pool->def->target.path, &sb) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot statvfs path '%s': %d (%s)", + pool->def->target.path, errno, strerror(errno)); + return -1; + } + pool->def->capacity = (unsigned long long)sb.f_frsize * (unsigned long long)sb.f_blocks; + pool->def->available = ((unsigned long long)sb.f_bfree * (unsigned long long)sb.f_bsize); + pool->def->allocation = pool->def->capacity - pool->def->available; + + return 0; + + cleanup: + closedir(dir); + virStoragePoolObjClearVols(pool); + return -1; +} + + +/** + * @conn connection to report errors against + * @pool storage pool to start + * + * Stops a directory or FS based storage pool. + * + * - If it is a FS based pool, unmounts the unlying source device on the pool + * - Releases all cached data about volumes + */ +#if WITH_STORAGE_FS +static int virStorageBackendFileSystemStop(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + if (pool->def->type != VIR_STORAGE_POOL_DIR && + virStorageBackendFileSystemUnmount(conn, pool) < 0) + return -1; + + return 0; +} +#endif /* WITH_STORAGE_FS */ + + +/** + * @conn connection to report errors against + * @pool storage pool to build + * + * Build a directory or FS based storage pool. + * + * - If it is a FS based pool, mounts the unlying source device on the pool + * + * Returns 0 on success, -1 on error + */ +static int virStorageBackendFileSystemDelete(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + /* XXX delete all vols first ? */ + + if (unlink(pool->def->target.path) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot unlink path '%s': %d (%s)", + pool->def->target, errno, strerror(errno)); + return -1; + } + + return 0; +} + + +/** + * Allocate a new file as a volume. This is either done directly + * for raw/sparse files, or by calling qemu-img/qcow-create for + * special kinds of files + */ +static int virStorageBackendFileSystemVolCreate(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + int fd; + + vol->target.path = malloc(strlen(pool->def->target.path) + 1 + strlen(vol->name) + 1); + if (vol->target.path == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "target"); + return -1; + } + strcpy(vol->target.path, pool->def->target.path); + strcat(vol->target.path, "/"); + strcat(vol->target.path, vol->name); + vol->key = strdup(vol->target.path); + if (vol->key == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "storage vol key"); + return -1; + } + + if (vol->target.format == VIR_STORAGE_VOL_RAW) { + if ((fd = open(vol->target.path, O_WRONLY | O_CREAT | O_EXCL, vol->target.perms.mode)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot create path '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + return -1; + } + + /* Pre-allocate any data if requested */ + /* XXX slooooooooooooooooow. Need to add in progress bars & bg thread somehow */ + if (vol->allocation) { + unsigned long long remain = vol->allocation; + static const char const zeros[4096]; + while (remain) { + int bytes = sizeof(zeros); + if (bytes > remain) + bytes = remain; + if ((bytes = write(fd, zeros, bytes)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot fill file '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + remain -= bytes; + } + } + + /* Now seek to final size, possibly making the file sparse */ + if (ftruncate(fd, vol->capacity) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot extend file '%s': %d (%s)", + vol->target, errno, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + } else { +#if HAVE_QEMU_IMG + const char *type; + char size[100]; + const char *imgargv[7]; + + if ((type = virStorageBackendFileSystemVolFormatToString(conn, vol->target.format)) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "unknown storage vol type %d", vol->target.format); + return -1; + } + + /* Size in KB */ + snprintf(size, sizeof(size), "%llu", vol->capacity/1024); + + imgargv[0] = QEMU_IMG; + imgargv[1] = "create"; + imgargv[2] = "-f"; + imgargv[3] = type; + imgargv[4] = vol->target.path; + imgargv[5] = size; + imgargv[6] = NULL; + + if (virRun(conn, (char **)imgargv, NULL) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "unable to run 'qemu-img create -f %s %s %s': %s", + type, vol->target.path, size, strerror(errno)); + unlink(vol->target.path); + return -1; + } + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read path '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + return -1; + } +#elif HAVE_QCOW_CREATE + /* + * Xen removed the fully-functional qemu-img, and replaced it + * with a partially functional qcow-create. Go figure ??!? + */ + char size[100]; + const char *imgargv[4]; + + if (vol->target.format != VIR_STORAGE_VOL_QCOW2) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "unsupported storage vol type %d", vol->target.format); + return -1; + } + + /* Size in MB - yes different units to qemu-img :-( */ + snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024); + + imgargv[0] = QCOW_CREATE; + imgargv[1] = size; + imgargv[2] = vol->target.path; + imgargv[3] = NULL; + + if (virRun(conn, (char **)imgargv, NULL) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "unable to run 'qcow-create %s %s': %s", + size, vol->target.path, strerror(errno)); + unlink(vol->target.path); + return -1; + } + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read path '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + return -1; + } +#else + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "creation of non-raw images is not supported without qemu-img"); + return -1; +#endif + } + + /* We can only chown/grp if root */ + if (getuid() == 0) { + if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot set file owner '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + } + if (fchmod(fd, vol->target.perms.mode) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot set file mode '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + + /* Refresh allocation / permissions info, but not capacity */ + if (virStorageBackendUpdateVolInfoFD(conn, vol, fd, 0) < 0) { + unlink(vol->target.path); + close(fd); + return -1; + } + + if (close(fd) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot close file '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + return -1; + } + + return 0; +} + + +/** + * Remove a volume - just unlinks for now + */ +static int virStorageBackendFileSystemVolDelete(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + unsigned int flags ATTRIBUTE_UNUSED) +{ + if (unlink(vol->target.path) < 0) { + /* Silently ignore failures where the vol has already gone away */ + if (errno != ENOENT) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot unlink file '%s': %d (%s)", + vol->target, errno, strerror(errno)); + return -1; + } + } + return 0; +} + + +/** + * Update info about a volume's capacity/allocation + */ +static int virStorageBackendFileSystemVolRefresh(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol) +{ + /* Refresh allocation / permissions info in case its changed */ + return virStorageBackendUpdateVolInfo(conn, vol, 0); +} + +virStorageBackend virStorageBackendDirectory = { + .type = VIR_STORAGE_POOL_DIR, + + .buildPool = virStorageBackendFileSystemBuild, + .refreshPool = virStorageBackendFileSystemRefresh, + .deletePool = virStorageBackendFileSystemDelete, + .createVol = virStorageBackendFileSystemVolCreate, + .refreshVol = virStorageBackendFileSystemVolRefresh, + .deleteVol = virStorageBackendFileSystemVolDelete, + + .volOptions = { + .formatFromString = virStorageBackendFileSystemVolFormatFromString, + .formatToString = virStorageBackendFileSystemVolFormatToString, + }, + .volType = VIR_STORAGE_VOL_FILE, +}; + +#if WITH_STORAGE_FS +virStorageBackend virStorageBackendFileSystem = { + .type = VIR_STORAGE_POOL_FS, + + .buildPool = virStorageBackendFileSystemBuild, + .startPool = virStorageBackendFileSystemStart, + .refreshPool = virStorageBackendFileSystemRefresh, + .stopPool = virStorageBackendFileSystemStop, + .deletePool = virStorageBackendFileSystemDelete, + .createVol = virStorageBackendFileSystemVolCreate, + .refreshVol = virStorageBackendFileSystemVolRefresh, + .deleteVol = virStorageBackendFileSystemVolDelete, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE), + .formatFromString = virStorageBackendFileSystemPoolFormatFromString, + .formatToString = virStorageBackendFileSystemPoolFormatToString, + }, + .volOptions = { + .formatFromString = virStorageBackendFileSystemVolFormatFromString, + .formatToString = virStorageBackendFileSystemVolFormatToString, + }, + .volType = VIR_STORAGE_VOL_FILE, +}; +virStorageBackend virStorageBackendNetFileSystem = { + .type = VIR_STORAGE_POOL_NETFS, + + .buildPool = virStorageBackendFileSystemBuild, + .startPool = virStorageBackendFileSystemStart, + .refreshPool = virStorageBackendFileSystemRefresh, + .stopPool = virStorageBackendFileSystemStop, + .deletePool = virStorageBackendFileSystemDelete, + .createVol = virStorageBackendFileSystemVolCreate, + .refreshVol = virStorageBackendFileSystemVolRefresh, + .deleteVol = virStorageBackendFileSystemVolDelete, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_HOST | + VIR_STORAGE_BACKEND_POOL_SOURCE_DIR), + .formatFromString = virStorageBackendFileSystemNetPoolFormatFromString, + .formatToString = virStorageBackendFileSystemNetPoolFormatToString, + }, + .volOptions = { + .formatFromString = virStorageBackendFileSystemVolFormatFromString, + .formatToString = virStorageBackendFileSystemVolFormatToString, + }, + .volType = VIR_STORAGE_VOL_FILE, +}; +#endif /* WITH_STORAGE_FS */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 739490b4a2f6 src/storage_backend_fs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_fs.h Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,49 @@ +/* + * storage_backend_fs.h: storage backend for FS and directory handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_BACKEND_FS_H__ +#define __VIR_STORAGE_BACKEND_FS_H__ + +#include "storage_backend.h" + +#if WITH_STORAGE_FS +extern virStorageBackend virStorageBackendFileSystem; +extern virStorageBackend virStorageBackendNetFileSystem; +#endif +extern virStorageBackend virStorageBackendDirectory; + +#endif /* __VIR_STORAGE_BACKEND_FS_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "missing backend for pool type %d", type);
Not specific to this patch, but any new error-reporting functions like virStorageReportError need to be added to the err_func_re in Makefile.maint so we're reminded to add _(...) around their messages so they get translated. This is of course no reason to change anything about your patch, but rather just a reminder to me.

"Daniel P. Berrange" <berrange@redhat.com> wrote:
diff -r 739490b4a2f6 src/storage_backend_fs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_fs.c Thu Feb 07 13:44:25 2008 -0500 ... + /* Optionally extract capacity from file */ + if (fileTypeInfo[i].sizeOffset != -1) { + if (fileTypeInfo[i].endian == __LITTLE_ENDIAN) { + def->capacity = + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7] << 56) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 48) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 40) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 32) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 24) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 16) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 8) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset]); + def->capacity *= fileTypeInfo[i].sizeMultiplier; + } else { + def->capacity = + ((unsigned long long)head[fileTypeInfo[i].sizeOffset] << 56) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 48) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 40) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 32) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 24) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 16) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 8) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]); + def->capacity *= fileTypeInfo[i].sizeMultiplier; + } + } + + /* Validation passed, we know the file format now */ + def->target.format = fileTypeInfo[i].type; + return 0; + }
Hi Dan, It'd be good to pull the duplicate capacity update out of the if- and else- blocks. def->capacity *= ... Also, just in case the file data is bogus enough to make the resulting product overflow, you can detect that with e.g., if (def->capacity > ULLONG_MAX / fileTypeInfo[i].sizeMultiplier) continue; def->capacity *= fileTypeInfo[i].sizeMultiplier; If the test fails, I'd be tempted to give a diagnostic about the corrupt "size" data.

On Fri, Feb 15, 2008 at 09:37:53PM +0100, Jim Meyering wrote:
It'd be good to pull the duplicate capacity update out of the if- and else- blocks.
def->capacity *= ...
Also, just in case the file data is bogus enough to make the resulting product overflow, you can detect that with e.g.,
if (def->capacity > ULLONG_MAX / fileTypeInfo[i].sizeMultiplier) continue; def->capacity *= fileTypeInfo[i].sizeMultiplier;
If the test fails, I'd be tempted to give a diagnostic about the corrupt "size" data.
Originally I thought there would be no overflow risk, since the only file types with a sizeMultiplier were those with a 4 byte size field. But I see now that actually VMDK 4 format has an 8 byte size and a 512 multiplier. So I'll add the sanity check... Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
+ if (virRun(conn, (char**)mntargv, NULL) < 0) { + free(src); + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot mount %s on %s: %d", + src, pool->def->target.path, strerror(errno));
format string mismatch. You probably wanted to include ", error" at the end of the arg list, to match the trailing %d. If this gets by gcc -Wformat then virStorageReportError needs the usual __attribute__((printf business, too. BTW, I've seen many cases where a diagnostic includes both the string, strerrno(error), and the %d-formatted errno value. Does anyone know why these diagnostics include the actual errno value?

On Fri, Feb 15, 2008 at 10:03:30PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
+ if (virRun(conn, (char**)mntargv, NULL) < 0) { + free(src); + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot mount %s on %s: %d", + src, pool->def->target.path, strerror(errno));
format string mismatch. You probably wanted to include ", error" at the end of the arg list, to match the trailing %d.
If this gets by gcc -Wformat then virStorageReportError needs the usual __attribute__((printf business, too.
Good idea.
BTW, I've seen many cases where a diagnostic includes both the string, strerrno(error), and the %d-formatted errno value. Does anyone know why these diagnostics include the actual errno value?
Historical habit - we should either remove all the errno values, or add them everywhere we use strerror. The former is probably best - users seeing these message don't know what the numbers mean Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
diff -r 739490b4a2f6 src/storage_backend_fs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_fs.c Thu Feb 07 13:44:25 2008 -0500 ... + /* Short-circuit is already mounted */
Barely worth mentioning, but it threw me for a sec: s/is/if/

On Tue, Feb 12, 2008 at 04:37:57AM +0000, Daniel P. Berrange wrote:
This patch implements 3 storage pools in one go. This provides the base level 'directory' storage pool, where an existing directory contains files as volumes. This is guarenteed to be available on any OS since it just uses plain POSIX apis for creation/deletion. If qemu-img or qcow2-create command line tools are available it can also create various non-raw fileformats such as QCow2, VMDK. If the mount/unmount commands are available, the 'fs' and 'netfs' pools become available. This allow a local disk, or remote filesystem to be mounted on the local filesystem, and files managed within. This driver contains code for probing the non-raw file formats to determine their logical capacity.
b/docs/storage/pool-dir.xml | 6 b/docs/storage/pool-fs.xml | 15 b/docs/storage/pool-netfs.xml | 16 b/docs/storage/vol-cow.xml | 10 b/docs/storage/vol-qcow.xml | 10 b/docs/storage/vol-qcow2.xml | 10 b/docs/storage/vol-raw.xml | 7 b/docs/storage/vol-sparse.xml | 7 b/docs/storage/vol-vmdk.xml | 10 b/src/storage_backend_fs.c | 1123 ++++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_fs.h | 49 + configure.in | 52 + libvirt.spec.in | 24 po/POTFILES.in | 1 src/Makefile.am | 1 src/storage_backend.c | 34 + 16 files changed, 1375 insertions(+) diff -r 686cf593fe28 configure.in --- a/configure.in Tue Feb 19 17:04:59 2008 -0500 +++ b/configure.in Tue Feb 19 17:22:04 2008 -0500 @@ -551,6 +551,52 @@ AC_SUBST(VIRSH_LIBS) AC_SUBST(WITH_XEN) AC_SUBST(LIBVIRT_FEATURES) + + +dnl +dnl Storage driver checks +dnl + +AC_ARG_WITH(storage-fs, +[ --with-storage-fs with FileSystem backend for the storage driver (on)],[],[with_storage_fs=check]) + +if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then + AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(UMOUNT, [umount], [], [$PATH:/sbin:/usr/sbin]) + if test "$with_storage_fs" = "yes" ; then + if test -z "$MOUNT" ; then AC_MSG_ERROR(We need mount for FS storage driver) ; fi + if test -z "$UMOUNT" ; then AC_MSG_ERROR(We need mount for FS storage driver) ; fi + else + if test -z "$MOUNT" ; then with_storage_fs=no ; fi + if test -z "$UMOUNT" ; then with_storage_fs=no ; fi + + if test "$with_storage_fs" = "check" ; then with_storage_fs=yes ; fi + fi + + if test "$with_storage_fs" = "yes" ; then + AC_DEFINE_UNQUOTED(WITH_STORAGE_FS, 1, [whether FS backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([MOUNT],["$MOUNT"], + [Location or name of the mount program]) + AC_DEFINE_UNQUOTED([UMOUNT],["$UMOUNT"], + [Location or name of the mount program]) + fi +fi +AM_CONDITIONAL(WITH_STORAGE_FS, [test "$with_storage_fs" = "yes"]) + +AC_PATH_PROG(QEMU_IMG, [qemu-img], [], [$PATH:/sbin:/usr/sbin:/bin:/usr/bin]) +if test -n "$QEMU_IMG" ; then + AC_DEFINE_UNQUOTED(HAVE_QEMU_IMG, 1, [whether qemu-img is available for non-raw files]) + AC_DEFINE_UNQUOTED([QEMU_IMG],["$QEMU_IMG"], + [Location or name of the qemu-img program]) +fi + +AC_PATH_PROG(QCOW_CREATE, [qcow-create], [], [$PATH:/sbin:/usr/sbin:/bin:/usr/bin]) +if test -n "$QCOW_CREATE" ; then + AC_DEFINE_UNQUOTED(HAVE_QCOW_CREATE, 1, [whether qcow-create is available for non-raw files]) + AC_DEFINE_UNQUOTED([QCOW_CREATE],["$QCOW_CREATE"], + [Location or name of the qcow-create program]) +fi + dnl dnl check for python @@ -760,6 +806,12 @@ AC_MSG_NOTICE([ Remote: $with_remote]) AC_MSG_NOTICE([ Remote: $with_remote]) AC_MSG_NOTICE([Libvirtd: $with_libvirtd]) AC_MSG_NOTICE([]) +AC_MSG_NOTICE([Storage Drivers]) +AC_MSG_NOTICE([]) +AC_MSG_NOTICE([ Dir: yes]) +AC_MSG_NOTICE([ FS: $with_storage_fs]) +AC_MSG_NOTICE([ NetFS: $with_storage_fs]) +AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ libxml: $LIBXML_CFLAGS $LIBXML_LIBS]) diff -r 686cf593fe28 docs/storage/pool-dir.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-dir.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,6 @@ +<pool type="dir"> + <name>virtimages</name> + <target> + <path>/var/lib/virt/images</path> + </target> +</pool> diff -r 686cf593fe28 docs/storage/pool-fs.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-fs.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,15 @@ +<pool type="fs"> + <name>virtimages</name> + <source> + <device path="/dev/VolGroup00/VirtImages"/> + </source> + <target> + <path>/var/lib/virt/images</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + <label>system_u:object_r:xen_image_t:s0</label> + </permissions> + </target> +</pool> diff -r 686cf593fe28 docs/storage/pool-netfs.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-netfs.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,16 @@ +<pool type="netfs"> + <name>virtimages</name> + <source> + <host name="nfs.example.com"/> + <directory path="/var/lib/virt/images"/> + </source> + <target> + <path>/var/lib/virt/images</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + <label>system_u:object_r:xen_image_t:s0</label> + </permissions> + </target> +</pool> diff -r 686cf593fe28 docs/storage/vol-cow.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-cow.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>cow.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="cow"/> + </target> +</volume> diff -r 686cf593fe28 docs/storage/vol-qcow.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-qcow.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>qcow.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="qcow"/> + </target> +</volume> diff -r 686cf593fe28 docs/storage/vol-qcow2.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-qcow2.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>qcow2.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="qcow2"/> + </target> +</volume> diff -r 686cf593fe28 docs/storage/vol-raw.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-raw.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,7 @@ +<volume type="file"> + <name>raw.img</name> + <storage> + <allocation unit="M">10</allocation> + <capacity unit="M">1000</capacity> + </storage> +</volume> diff -r 686cf593fe28 docs/storage/vol-sparse.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-sparse.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,7 @@ +<volume type="file"> + <name>sparse.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> +</volume> diff -r 686cf593fe28 docs/storage/vol-vmdk.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-vmdk.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>vmdk3.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="vmdk"/> + </target> +</volume> diff -r 686cf593fe28 libvirt.spec.in --- a/libvirt.spec.in Tue Feb 19 17:04:59 2008 -0500 +++ b/libvirt.spec.in Tue Feb 19 17:22:04 2008 -0500 @@ -6,6 +6,12 @@ %else %define with_polkit 0 %define with_proxy yes +%endif + +%if "%{fedora}" +%define with_qemu 1 +%else +%define with_qemu 0 %endif Summary: Library providing a simple API virtualization @@ -34,6 +40,15 @@ Requires: cyrus-sasl-md5 %if %{with_polkit} Requires: PolicyKit >= 0.6 %endif +# For mount/umount in FS driver +BuildRequires: util-linux +%if %{with_qemu} +# From QEMU RPMs +Requires: /usr/bin/qemu-img +%else +# From Xen RPMs +Requires: /usr/sbin/qcow-create +%endif BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -48,6 +63,15 @@ BuildRequires: cyrus-sasl-devel BuildRequires: cyrus-sasl-devel %if %{with_polkit} BuildRequires: PolicyKit-devel >= 0.6 +%endif +# For mount/umount in FS driver +BuildRequires: util-linux +%if %{with_qemu} +# From QEMU RPMs +BuildRequires: /usr/bin/qemu-img +%else +# From Xen RPMs +BuildRequires: /usr/sbin/qcow-create %endif Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 diff -r 686cf593fe28 po/POTFILES.in --- a/po/POTFILES.in Tue Feb 19 17:04:59 2008 -0500 +++ b/po/POTFILES.in Tue Feb 19 17:22:04 2008 -0500 @@ -11,6 +11,7 @@ src/qemu_driver.c src/qemu_driver.c src/remote_internal.c src/storage_backend.c +src/storage_backend_fs.c src/storage_conf.c src/storage_driver.c src/sexpr.c diff -r 686cf593fe28 src/Makefile.am --- a/src/Makefile.am Tue Feb 19 17:04:59 2008 -0500 +++ b/src/Makefile.am Tue Feb 19 17:22:04 2008 -0500 @@ -62,6 +62,7 @@ CLIENT_SOURCES = \ storage_conf.h storage_conf.c \ storage_driver.h storage_driver.c \ storage_backend.h storage_backend.c \ + storage_backend_fs.h storage_backend_fs.c \ util.c util.h SERVER_SOURCES = \ diff -r 686cf593fe28 src/storage_backend.c --- a/src/storage_backend.c Tue Feb 19 17:04:59 2008 -0500 +++ b/src/storage_backend.c Tue Feb 19 17:22:04 2008 -0500 @@ -40,10 +40,24 @@ #include "util.h" #include "storage_backend.h" +#include "storage_backend_fs.h" + +static virStorageBackendPtr backends[] = { + &virStorageBackendDirectory, +#if WITH_STORAGE_FS + &virStorageBackendFileSystem, + &virStorageBackendNetFileSystem, +#endif +}; virStorageBackendPtr virStorageBackendForType(int type) { + unsigned int i; + for (i = 0 ; i < (sizeof(backends)/sizeof(backends[0])) ; i++) + if (backends[i]->type == type) + return backends[i]; + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, _("missing backend for pool type %d"), type); return NULL; @@ -68,6 +82,15 @@ virStorageBackendVolOptionsForType(int t int virStorageBackendFromString(const char *type) { + if (STREQ(type, "dir")) + return VIR_STORAGE_POOL_DIR; +#if WITH_STORAGE_FS + if (STREQ(type, "fs")) + return VIR_STORAGE_POOL_FS; + if (STREQ(type, "netfs")) + return VIR_STORAGE_POOL_NETFS; +#endif + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, _("unknown storage backend type %s"), type); return -1; @@ -75,6 +98,17 @@ virStorageBackendFromString(const char * const char * virStorageBackendToString(int type) { + switch (type) { + case VIR_STORAGE_POOL_DIR: + return "dir"; +#if WITH_STORAGE_FS + case VIR_STORAGE_POOL_FS: + return "fs"; + case VIR_STORAGE_POOL_NETFS: + return "netfs"; +#endif + } + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, _("unknown storage backend type %d"), type); return NULL; diff -r 686cf593fe28 src/storage_backend_fs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_fs.c Tue Feb 19 17:22:05 2008 -0500 @@ -0,0 +1,1123 @@ +/* + * storage_backend_fs.c: storage backend for FS and directory handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <sys/statvfs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <endian.h> +#include <byteswap.h> +#include <mntent.h> +#include <string.h> + +#include "storage_backend_fs.h" +#include "storage_conf.h" +#include "util.h" +#include "config.h" + + +enum { + VIR_STORAGE_POOL_FS_AUTO = 0, + VIR_STORAGE_POOL_FS_EXT2, + VIR_STORAGE_POOL_FS_EXT3, + VIR_STORAGE_POOL_FS_EXT4, + VIR_STORAGE_POOL_FS_UFS, + VIR_STORAGE_POOL_FS_ISO, + VIR_STORAGE_POOL_FS_UDF, + VIR_STORAGE_POOL_FS_GFS, + VIR_STORAGE_POOL_FS_GFS2, + VIR_STORAGE_POOL_FS_VFAT, + VIR_STORAGE_POOL_FS_HFSPLUS, + VIR_STORAGE_POOL_FS_XFS, +}; + +enum { + VIR_STORAGE_POOL_NETFS_AUTO = 0, + VIR_STORAGE_POOL_NETFS_NFS, +}; + + + +enum { + VIR_STORAGE_VOL_RAW, + VIR_STORAGE_VOL_DIR, + VIR_STORAGE_VOL_BOCHS, + VIR_STORAGE_VOL_CLOOP, + VIR_STORAGE_VOL_COW, + VIR_STORAGE_VOL_DMG, + VIR_STORAGE_VOL_ISO, + VIR_STORAGE_VOL_QCOW, + VIR_STORAGE_VOL_QCOW2, + VIR_STORAGE_VOL_VMDK, + VIR_STORAGE_VOL_VPC, +}; + +/* Either 'magic' or 'extension' *must* be provided */ +struct { + int type; /* One of the constants above */ + const char *magic; /* Optional string of file magic + * to check at head of file */ + const char *extension; /* Optional file extension to check */ + int endian; /* Endianness of file format */ + int versionOffset; /* Byte offset from start of file + * where we find version number, + * -1 to skip version test */ + int versionNumber; /* Version number to validate */ + int sizeOffset; /* Byte offset from start of file + * where we find capacity info, + * -1 to use st_size as capacity */ + int sizeBytes; /* Number of bytes for size field */ + int sizeMultiplier; /* A scaling factor if size is not in bytes */ +} fileTypeInfo[] = { + /* Bochs */ + /* XXX Untested + { VIR_STORAGE_VOL_BOCHS, "Bochs Virtual HD Image", NULL, + __LITTLE_ENDIAN, 64, 0x20000, + 32+16+16+4+4+4+4+4, 8, 1 },*/ + /* CLoop */ + /* XXX Untested + { VIR_STORAGE_VOL_CLOOP, "#!/bin/sh\n#V2.0 Format\nmodprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n", NULL, + __LITTLE_ENDIAN, -1, 0, + -1, 0, 0 }, */ + /* Cow */ + { VIR_STORAGE_VOL_COW, "OOOM", NULL, + __BIG_ENDIAN, 4, 2, + 4+4+1024+4, 8, 1 }, + /* DMG */ + /* XXX QEMU says there's no magic for dmg, but we should check... */ + { VIR_STORAGE_VOL_DMG, NULL, ".dmg", + 0, -1, 0, + -1, 0, 0 }, + /* XXX there's probably some magic for iso we can validate too... */ + { VIR_STORAGE_VOL_ISO, NULL, ".iso", + 0, -1, 0, + -1, 0, 0 }, + /* Parallels */ + /* XXX Untested + { VIR_STORAGE_VOL_PARALLELS, "WithoutFreeSpace", NULL, + __LITTLE_ENDIAN, 16, 2, + 16+4+4+4+4, 4, 512 }, + */ + /* QCow */ + { VIR_STORAGE_VOL_QCOW, "QFI", NULL, + __BIG_ENDIAN, 4, 1, + 4+4+8+4+4, 8, 1 }, + /* QCow 2 */ + { VIR_STORAGE_VOL_QCOW2, "QFI", NULL, + __BIG_ENDIAN, 4, 2, + 4+4+8+4+4, 8, 1 }, + /* VMDK 3 */ + /* XXX Untested + { VIR_STORAGE_VOL_VMDK, "COWD", NULL, + __LITTLE_ENDIAN, 4, 1, + 4+4+4, 4, 512 }, + */ + /* VMDK 4 */ + { VIR_STORAGE_VOL_VMDK, "KDMV", NULL, + __LITTLE_ENDIAN, 4, 1, + 4+4+4, 8, 512 }, + /* Connectix / VirtualPC */ + /* XXX Untested + { VIR_STORAGE_VOL_VPC, "conectix", NULL, + __BIG_ENDIAN, -1, 0, + -1, 0, 0}, + */ +}; + + + + +static int +virStorageBackendFileSystemVolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_VOL_RAW; + + if (STREQ(format, "raw")) + return VIR_STORAGE_VOL_RAW; + if (STREQ(format, "dir")) + return VIR_STORAGE_VOL_DIR; + if (STREQ(format, "bochs")) + return VIR_STORAGE_VOL_BOCHS; + if (STREQ(format, "cow")) + return VIR_STORAGE_VOL_COW; + if (STREQ(format, "cloop")) + return VIR_STORAGE_VOL_CLOOP; + if (STREQ(format, "dmg")) + return VIR_STORAGE_VOL_DMG; + if (STREQ(format, "iso")) + return VIR_STORAGE_VOL_ISO; + if (STREQ(format, "qcow")) + return VIR_STORAGE_VOL_QCOW; + if (STREQ(format, "qcow2")) + return VIR_STORAGE_VOL_QCOW2; + if (STREQ(format, "vmdk")) + return VIR_STORAGE_VOL_VMDK; + if (STREQ(format, "vpc")) + return VIR_STORAGE_VOL_VPC; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %s"), format); + return -1; +} + +static const char * +virStorageBackendFileSystemVolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_VOL_RAW: + return "raw"; + case VIR_STORAGE_VOL_DIR: + return "dir"; + case VIR_STORAGE_VOL_BOCHS: + return "bochs"; + case VIR_STORAGE_VOL_CLOOP: + return "cloop"; + case VIR_STORAGE_VOL_COW: + return "cow"; + case VIR_STORAGE_VOL_DMG: + return "dmg"; + case VIR_STORAGE_VOL_ISO: + return "iso"; + case VIR_STORAGE_VOL_QCOW: + return "qcow"; + case VIR_STORAGE_VOL_QCOW2: + return "qcow2"; + case VIR_STORAGE_VOL_VMDK: + return "vmdk"; + case VIR_STORAGE_VOL_VPC: + return "vpc"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %d"), format); + return NULL; +} + + +static int +virStorageBackendFileSystemPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_FS_AUTO; + + if (STREQ(format, "auto")) + return VIR_STORAGE_POOL_FS_AUTO; + if (STREQ(format, "ext2")) + return VIR_STORAGE_POOL_FS_EXT2; + if (STREQ(format, "ext3")) + return VIR_STORAGE_POOL_FS_EXT3; + if (STREQ(format, "ext4")) + return VIR_STORAGE_POOL_FS_EXT4; + if (STREQ(format, "ufs")) + return VIR_STORAGE_POOL_FS_UFS; + if (STREQ(format, "iso9660")) + return VIR_STORAGE_POOL_FS_ISO; + if (STREQ(format, "udf")) + return VIR_STORAGE_POOL_FS_UDF; + if (STREQ(format, "gfs")) + return VIR_STORAGE_POOL_FS_GFS; + if (STREQ(format, "gfs2")) + return VIR_STORAGE_POOL_FS_GFS2; + if (STREQ(format, "vfat")) + return VIR_STORAGE_POOL_FS_VFAT; + if (STREQ(format, "hfs+")) + return VIR_STORAGE_POOL_FS_HFSPLUS; + if (STREQ(format, "xfs")) + return VIR_STORAGE_POOL_FS_XFS; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %s"), format); + return -1; +} + +static const char * +virStorageBackendFileSystemPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_FS_AUTO: + return "auto"; + case VIR_STORAGE_POOL_FS_EXT2: + return "ext2"; + case VIR_STORAGE_POOL_FS_EXT3: + return "ext3"; + case VIR_STORAGE_POOL_FS_EXT4: + return "ext4"; + case VIR_STORAGE_POOL_FS_UFS: + return "ufs"; + case VIR_STORAGE_POOL_FS_ISO: + return "iso"; + case VIR_STORAGE_POOL_FS_UDF: + return "udf"; + case VIR_STORAGE_POOL_FS_GFS: + return "gfs"; + case VIR_STORAGE_POOL_FS_GFS2: + return "gfs2"; + case VIR_STORAGE_POOL_FS_VFAT: + return "vfat"; + case VIR_STORAGE_POOL_FS_HFSPLUS: + return "hfs+"; + case VIR_STORAGE_POOL_FS_XFS: + return "xfs"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %d"), format); + return NULL; +} + + +static int +virStorageBackendFileSystemNetPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_NETFS_AUTO; + + if (STREQ(format, "auto")) + return VIR_STORAGE_POOL_NETFS_AUTO; + if (STREQ(format, "nfs")) + return VIR_STORAGE_POOL_NETFS_NFS; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %s"), format); + return -1; +} + +static const char * +virStorageBackendFileSystemNetPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_NETFS_AUTO: + return "auto"; + case VIR_STORAGE_POOL_NETFS_NFS: + return "nfs"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %d"), format); + return NULL; +} + + +/** + * Probe the header of a file to determine what type of disk image + * it is, and info about its capacity if available. + */ +static int virStorageBackendProbeFile(virConnectPtr conn, + virStorageVolDefPtr def) { + int fd; + char head[4096]; + int len, i, ret; + + if ((fd = open(def->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot open volume '%s': %s"), + def->target.path, strerror(errno)); + return -1; + } + + if ((ret = virStorageBackendUpdateVolInfoFD(conn, def, fd, 1)) < 0) { + close(fd); + return ret; /* Take care to propagate ret, it is not always -1 */ + } + + if ((len = read(fd, head, sizeof(head))) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot read header '%s': %s"), + def->target.path, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + + /* First check file magic */ + for (i = 0 ; i < sizeof(fileTypeInfo)/sizeof(fileTypeInfo[0]) ; i++) { + int mlen; + if (fileTypeInfo[i].magic == NULL) + continue; + + /* Validate magic data */ + mlen = strlen(fileTypeInfo[i].magic); + if (mlen > len) + continue; + if (memcmp(head, fileTypeInfo[i].magic, mlen) != 0) + continue; + + /* Validate version number info */ + if (fileTypeInfo[i].versionNumber != -1) { + int version; + + if (fileTypeInfo[i].endian == __LITTLE_ENDIAN) { + version = (head[fileTypeInfo[i].versionOffset+3] << 24) | + (head[fileTypeInfo[i].versionOffset+2] << 16) | + (head[fileTypeInfo[i].versionOffset+1] << 8) | + head[fileTypeInfo[i].versionOffset]; + } else { + version = (head[fileTypeInfo[i].versionOffset] << 24) | + (head[fileTypeInfo[i].versionOffset+1] << 16) | + (head[fileTypeInfo[i].versionOffset+2] << 8) | + head[fileTypeInfo[i].versionOffset+3]; + } + if (version != fileTypeInfo[i].versionNumber) + continue; + } + + /* Optionally extract capacity from file */ + if (fileTypeInfo[i].sizeOffset != -1) { + if (fileTypeInfo[i].endian == __LITTLE_ENDIAN) { + def->capacity = + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7] << 56) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 48) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 40) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 32) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 24) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 16) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 8) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset]); + } else { + def->capacity = + ((unsigned long long)head[fileTypeInfo[i].sizeOffset] << 56) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 48) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 40) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 32) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 24) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 16) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 8) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]); + } + /* Avoid unlikely, but theoretically possible overflow */ + if (def->capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier)) + continue; + def->capacity *= fileTypeInfo[i].sizeMultiplier; + } + + /* Validation passed, we know the file format now */ + def->target.format = fileTypeInfo[i].type; + return 0; + } + + /* No magic, so check file extension */ + for (i = 0 ; i < sizeof(fileTypeInfo)/sizeof(fileTypeInfo[0]) ; i++) { + if (fileTypeInfo[i].extension == NULL) + continue; + + if (!virFileHasSuffix(def->target.path, fileTypeInfo[i].extension)) + continue; + + def->target.format = fileTypeInfo[i].type; + return 0; + } + + /* All fails, so call it a raw file */ + def->target.format = VIR_STORAGE_VOL_RAW; + return 0; +} + +#if WITH_STORAGE_FS +/** + * @conn connection to report errors against + * @pool storage pool to check for status + * + * Determine if a storage pool is already mounted + * + * Return 0 if not mounted, 1 if mounted, -1 on error + */ +static int +virStorageBackendFileSystemIsMounted(virConnectPtr conn, + virStoragePoolObjPtr pool) { + FILE *mtab; + struct mntent *ent; + + if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot read %s: %s"), + _PATH_MOUNTED, strerror(errno)); + return -1; + } + + while ((ent = getmntent(mtab)) != NULL) { + if (STREQ(ent->mnt_dir, pool->def->target.path)) { + fclose(mtab); + return 1; + } + } + + fclose(mtab); + return 0; +} + +/** + * @conn connection to report errors against + * @pool storage pool to mount + * + * Ensure that a FS storage pool is mounted on its target location. + * If already mounted, this is a no-op + * + * Returns 0 if successfully mounted, -1 on error + */ +static int +virStorageBackendFileSystemMount(virConnectPtr conn, + virStoragePoolObjPtr pool) { + char *src; + const char *mntargv[] = { + MOUNT, + "-t", + pool->def->type == VIR_STORAGE_POOL_FS ? + virStorageBackendFileSystemPoolFormatToString(conn, + pool->def->source.format) : + virStorageBackendFileSystemNetPoolFormatToString(conn, + pool->def->source.format), + NULL, /* Fill in shortly - careful not to add extra fields before this */ + pool->def->target.path, + NULL, + }; + int ret; + + if (pool->def->type == VIR_STORAGE_POOL_NETFS) { + if (pool->def->source.host.name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("missing source host")); + return -1; + } + if (pool->def->source.dir == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("missing source path")); + return -1; + } + } else { + if (pool->def->source.ndevice != 1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("missing source device")); + return -1; + } + } + + /* Short-circuit is already mounted */ + if ((ret = virStorageBackendFileSystemIsMounted(conn, pool)) != 0) { + if (ret < 0) + return -1; + else + return 0; + } + + if (pool->def->type == VIR_STORAGE_POOL_NETFS) { + src = malloc(strlen(pool->def->source.host.name) + + 1 + strlen(pool->def->source.dir) + 1); + strcpy(src, pool->def->source.host.name); + strcat(src, ":"); + strcat(src, pool->def->source.dir); + } else { + src = strdup(pool->def->source.devices[0].path); + } + if (src == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("source")); + return -1; + } + mntargv[3] = src; + + if (virRun(conn, (char**)mntargv, NULL) < 0) { + free(src); + return -1; + } + free(src); + return 0; +} + +/** + * @conn connection to report errors against + * @pool storage pool to unmount + * + * Ensure that a FS storage pool is not mounted on its target location. + * If already unmounted, this is a no-op + * + * Returns 0 if successfully unmounted, -1 on error + */ +static int +virStorageBackendFileSystemUnmount(virConnectPtr conn, + virStoragePoolObjPtr pool) { + const char *mntargv[3]; + int ret; + + if (pool->def->type == VIR_STORAGE_POOL_NETFS) { + if (pool->def->source.host.name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("missing source host")); + return -1; + } + if (pool->def->source.dir == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("missing source dir")); + return -1; + } + } else { + if (pool->def->source.ndevice != 1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("missing source device")); + return -1; + } + } + + /* Short-circuit if already unmounted */ + if ((ret = virStorageBackendFileSystemIsMounted(conn, pool)) != 1) { + if (ret < 0) + return -1; + else + return 0; + } + + mntargv[0] = UMOUNT; + mntargv[1] = pool->def->target.path; + mntargv[2] = NULL; + + if (virRun(conn, (char**)mntargv, NULL) < 0) { + return -1; + } + return 0; +} +#endif /* WITH_STORAGE_FS */ + + +/** + * @conn connection to report errors against + * @pool storage pool to start + * + * Starts a directory or FS based storage pool. + * + * - If it is a FS based pool, mounts the unlying source device on the pool + * + * Returns 0 on success, -1 on error + */ +#if WITH_STORAGE_FS +static int +virStorageBackendFileSystemStart(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + if (pool->def->type != VIR_STORAGE_POOL_DIR && + virStorageBackendFileSystemMount(conn, pool) < 0) + return -1; + + return 0; +} +#endif /* WITH_STORAGE_FS */ + + +/** + * @conn connection to report errors against + * @pool storage pool to build + * + * Build a directory or FS based storage pool. + * + * - If it is a FS based pool, mounts the unlying source device on the pool + * + * Returns 0 on success, -1 on error + */ +static int +virStorageBackendFileSystemBuild(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + if (virFileMakePath(pool->def->target.path) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create path '%s': %s"), + pool->def->target.path, strerror(errno)); + return -1; + } + + return 0; +} + + +/** + * Iterate over the pool's directory and enumerate all disk images + * within it. This is non-recursive. + */ +static int +virStorageBackendFileSystemRefresh(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + DIR *dir; + struct dirent *ent; + struct statvfs sb; + + if (!(dir = opendir(pool->def->target.path))) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot open path '%s': %s"), + pool->def->target.path, strerror(errno)); + goto cleanup; + } + + while ((ent = readdir(dir)) != NULL) { + virStorageVolDefPtr vol; + int ret; + + vol = calloc(1, sizeof(virStorageVolDef)); + if (vol == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("volume")); + goto cleanup; + } + + vol->name = strdup(ent->d_name); + if (vol->name == NULL) { + free(vol); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("volume name")); + goto cleanup; + } + + vol->target.format = VIR_STORAGE_VOL_RAW; /* Real value is filled in during probe */ + vol->target.path = malloc(strlen(pool->def->target.path) + + 1 + strlen(vol->name) + 1); + if (vol->target.path == NULL) { + free(vol->target.path); + free(vol); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("volume name")); + goto cleanup; + } + strcpy(vol->target.path, pool->def->target.path); + strcat(vol->target.path, "/"); + strcat(vol->target.path, vol->name); + if ((vol->key = strdup(vol->target.path)) == NULL) { + free(vol->name); + free(vol->target.path); + free(vol); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("volume key")); + goto cleanup; + } + + if ((ret = virStorageBackendProbeFile(conn, vol) < 0)) { + free(vol->key); + free(vol->name); + free(vol->target.path); + free(vol); + if (ret == -1) + goto cleanup; + else + /* Silently ignore non-regular files, + * eg '.' '..', 'lost+found' */ + continue; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + continue; + } + closedir(dir); + + + if (statvfs(pool->def->target.path, &sb) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot statvfs path '%s': %s"), + pool->def->target.path, strerror(errno)); + return -1; + } + pool->def->capacity = ((unsigned long long)sb.f_frsize * + (unsigned long long)sb.f_blocks); + pool->def->available = ((unsigned long long)sb.f_bfree * + (unsigned long long)sb.f_bsize); + pool->def->allocation = pool->def->capacity - pool->def->available; + + return 0; + + cleanup: + closedir(dir); + virStoragePoolObjClearVols(pool); + return -1; +} + + +/** + * @conn connection to report errors against + * @pool storage pool to start + * + * Stops a directory or FS based storage pool. + * + * - If it is a FS based pool, unmounts the unlying source device on the pool + * - Releases all cached data about volumes + */ +#if WITH_STORAGE_FS +static int +virStorageBackendFileSystemStop(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + if (pool->def->type != VIR_STORAGE_POOL_DIR && + virStorageBackendFileSystemUnmount(conn, pool) < 0) + return -1; + + return 0; +} +#endif /* WITH_STORAGE_FS */ + + +/** + * @conn connection to report errors against + * @pool storage pool to build + * + * Build a directory or FS based storage pool. + * + * - If it is a FS based pool, mounts the unlying source device on the pool + * + * Returns 0 on success, -1 on error + */ +static int +virStorageBackendFileSystemDelete(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + /* XXX delete all vols first ? */ + + if (unlink(pool->def->target.path) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot unlink path '%s': %s"), + pool->def->target.path, strerror(errno)); + return -1; + } + + return 0; +} + + +/** + * Allocate a new file as a volume. This is either done directly + * for raw/sparse files, or by calling qemu-img/qcow-create for + * special kinds of files + */ +static int +virStorageBackendFileSystemVolCreate(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + int fd; + + vol->target.path = malloc(strlen(pool->def->target.path) + + 1 + strlen(vol->name) + 1); + if (vol->target.path == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("target")); + return -1; + } + strcpy(vol->target.path, pool->def->target.path); + strcat(vol->target.path, "/"); + strcat(vol->target.path, vol->name); + vol->key = strdup(vol->target.path); + if (vol->key == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("storage vol key")); + return -1; + } + + if (vol->target.format == VIR_STORAGE_VOL_RAW) { + if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL, + vol->target.perms.mode)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create path '%s': %s"), + vol->target.path, strerror(errno)); + return -1; + } + + /* Pre-allocate any data if requested */ + /* XXX slooooooooooooooooow. + * Need to add in progress bars & bg thread somehow */ + if (vol->allocation) { + unsigned long long remain = vol->allocation; + static const char const zeros[4096]; + while (remain) { + int bytes = sizeof(zeros); + if (bytes > remain) + bytes = remain; + if ((bytes = write(fd, zeros, bytes)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot fill file '%s': %s"), + vol->target.path, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + remain -= bytes; + } + } + + /* Now seek to final size, possibly making the file sparse */ + if (ftruncate(fd, vol->capacity) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot extend file '%s': %s"), + vol->target.path, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + } else if (vol->target.format == VIR_STORAGE_VOL_DIR) { + if (mkdir(vol->target.path, vol->target.perms.mode) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create path '%s': %s"), + vol->target.path, strerror(errno)); + return -1; + } + + if ((fd = open(vol->target.path, O_RDWR)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot read path '%s': %s"), + vol->target.path, strerror(errno)); + return -1; + } + } else { +#if HAVE_QEMU_IMG + const char *type; + char size[100]; + const char *imgargv[7]; + + if ((type = virStorageBackendFileSystemVolFormatToString(conn, + vol->target.format)) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown storage vol type %d"), + vol->target.format); + return -1; + } + + /* Size in KB */ + snprintf(size, sizeof(size), "%llu", vol->capacity/1024); + + imgargv[0] = QEMU_IMG; + imgargv[1] = "create"; + imgargv[2] = "-f"; + imgargv[3] = type; + imgargv[4] = vol->target.path; + imgargv[5] = size; + imgargv[6] = NULL; + + if (virRun(conn, (char **)imgargv, NULL) < 0) { + unlink(vol->target.path); + return -1; + } + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot read path '%s': %s"), + vol->target.path, strerror(errno)); + unlink(vol->target.path); + return -1; + } +#elif HAVE_QCOW_CREATE + /* + * Xen removed the fully-functional qemu-img, and replaced it + * with a partially functional qcow-create. Go figure ??!? + */ + char size[100]; + const char *imgargv[4]; + + if (vol->target.format != VIR_STORAGE_VOL_QCOW2) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported storage vol type %d"), + vol->target.format); + return -1; + } + + /* Size in MB - yes different units to qemu-img :-( */ + snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024); + + imgargv[0] = QCOW_CREATE; + imgargv[1] = size; + imgargv[2] = vol->target.path; + imgargv[3] = NULL; + + if (virRun(conn, (char **)imgargv, NULL) < 0) { + unlink(vol->target.path); + return -1; + } + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot read path '%s': %s"), + vol->target.path, strerror(errno)); + unlink(vol->target.path); + return -1; + } +#else + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("creation of non-raw images is not supported without qemu-img")); + return -1; +#endif + } + + /* We can only chown/grp if root */ + if (getuid() == 0) { + if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot set file owner '%s': %s"), + vol->target.path, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + } + if (fchmod(fd, vol->target.perms.mode) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot set file mode '%s': %s"), + vol->target.path, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + + /* Refresh allocation / permissions info, but not capacity */ + if (virStorageBackendUpdateVolInfoFD(conn, vol, fd, 0) < 0) { + unlink(vol->target.path); + close(fd); + return -1; + } + + if (close(fd) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot close file '%s': %s"), + vol->target.path, strerror(errno)); + unlink(vol->target.path); + return -1; + } + + return 0; +} + + +/** + * Remove a volume - just unlinks for now + */ +static int +virStorageBackendFileSystemVolDelete(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + unsigned int flags ATTRIBUTE_UNUSED) +{ + if (unlink(vol->target.path) < 0) { + /* Silently ignore failures where the vol has already gone away */ + if (errno != ENOENT) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot unlink file '%s': %s"), + vol->target.path, strerror(errno)); + return -1; + } + } + return 0; +} + + +/** + * Update info about a volume's capacity/allocation + */ +static int +virStorageBackendFileSystemVolRefresh(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol) +{ + /* Refresh allocation / permissions info in case its changed */ + return virStorageBackendUpdateVolInfo(conn, vol, 0); +} + +virStorageBackend virStorageBackendDirectory = { + .type = VIR_STORAGE_POOL_DIR, + + .buildPool = virStorageBackendFileSystemBuild, + .refreshPool = virStorageBackendFileSystemRefresh, + .deletePool = virStorageBackendFileSystemDelete, + .createVol = virStorageBackendFileSystemVolCreate, + .refreshVol = virStorageBackendFileSystemVolRefresh, + .deleteVol = virStorageBackendFileSystemVolDelete, + + .volOptions = { + .formatFromString = virStorageBackendFileSystemVolFormatFromString, + .formatToString = virStorageBackendFileSystemVolFormatToString, + }, + .volType = VIR_STORAGE_VOL_FILE, +}; + +#if WITH_STORAGE_FS +virStorageBackend virStorageBackendFileSystem = { + .type = VIR_STORAGE_POOL_FS, + + .buildPool = virStorageBackendFileSystemBuild, + .startPool = virStorageBackendFileSystemStart, + .refreshPool = virStorageBackendFileSystemRefresh, + .stopPool = virStorageBackendFileSystemStop, + .deletePool = virStorageBackendFileSystemDelete, + .createVol = virStorageBackendFileSystemVolCreate, + .refreshVol = virStorageBackendFileSystemVolRefresh, + .deleteVol = virStorageBackendFileSystemVolDelete, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE), + .formatFromString = virStorageBackendFileSystemPoolFormatFromString, + .formatToString = virStorageBackendFileSystemPoolFormatToString, + }, + .volOptions = { + .formatFromString = virStorageBackendFileSystemVolFormatFromString, + .formatToString = virStorageBackendFileSystemVolFormatToString, + }, + .volType = VIR_STORAGE_VOL_FILE, +}; +virStorageBackend virStorageBackendNetFileSystem = { + .type = VIR_STORAGE_POOL_NETFS, + + .buildPool = virStorageBackendFileSystemBuild, + .startPool = virStorageBackendFileSystemStart, + .refreshPool = virStorageBackendFileSystemRefresh, + .stopPool = virStorageBackendFileSystemStop, + .deletePool = virStorageBackendFileSystemDelete, + .createVol = virStorageBackendFileSystemVolCreate, + .refreshVol = virStorageBackendFileSystemVolRefresh, + .deleteVol = virStorageBackendFileSystemVolDelete, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_HOST | + VIR_STORAGE_BACKEND_POOL_SOURCE_DIR), + .formatFromString = virStorageBackendFileSystemNetPoolFormatFromString, + .formatToString = virStorageBackendFileSystemNetPoolFormatToString, + }, + .volOptions = { + .formatFromString = virStorageBackendFileSystemVolFormatFromString, + .formatToString = virStorageBackendFileSystemVolFormatToString, + }, + .volType = VIR_STORAGE_VOL_FILE, +}; +#endif /* WITH_STORAGE_FS */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 686cf593fe28 src/storage_backend_fs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_fs.h Tue Feb 19 17:22:05 2008 -0500 @@ -0,0 +1,49 @@ +/* + * storage_backend_fs.h: storage backend for FS and directory handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_BACKEND_FS_H__ +#define __VIR_STORAGE_BACKEND_FS_H__ + +#include "storage_backend.h" + +#if WITH_STORAGE_FS +extern virStorageBackend virStorageBackendFileSystem; +extern virStorageBackend virStorageBackendNetFileSystem; +#endif +extern virStorageBackend virStorageBackendDirectory; + +#endif /* __VIR_STORAGE_BACKEND_FS_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This patch implements a storage pool based on logical volume management. The underlying implementation calls out to LVM on Linux. A future implementation for Solaris would use the ZFS pool support to achieve the same functionality. The LVM impl uses the LVM command line tools, in particular lvs, and vgs for listing, and the *create, *remove tools for modifications. The 'build' operation will construct a new volume group, and initialize any physical volume members. The 'delete' operation will permanently remove the group. Volumes are allocated by carving out logical volumes. There are no placement constraints how the volume XML will show the actual storage per-volume on the underlying physical volumes. The LVM UUID is used for the unique volume keys. b/docs/storage/pool-logical.xml | 6 b/src/storage_backend_logical.c | 497 ++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_logical.h | 45 +++ configure.in | 58 ++++ libvirt.spec.in | 4 qemud/qemud.c | 4 src/Makefile.am | 8 src/storage_backend.c | 14 + 8 files changed, 635 insertions(+), 1 deletion(-) diff -r 31abfd8687b3 configure.in --- a/configure.in Thu Feb 07 13:44:25 2008 -0500 +++ b/configure.in Thu Feb 07 14:17:16 2008 -0500 @@ -559,6 +559,8 @@ dnl AC_ARG_WITH(storage-fs, [ --with-storage-fs with FileSystem backend for the storage driver (on)],[],[with_storage_fs=check]) +AC_ARG_WITH(storage-lvm, +[ --with-storage-lvm with LVM backend for the storage driver (on)],[],[with_storage_lvm=check]) if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin]) @@ -596,6 +598,61 @@ if test -n "$QCOW_CREATE" ; then AC_DEFINE_UNQUOTED([QCOW_CREATE],["$QCOW_CREATE"], [Location or name of the qcow-create program]) fi + + +if test "$with_storage_lvm" = "yes" -o "$with_storage_lvm" = "check"; then + AC_PATH_PROG(PVCREATE, [pvcreate], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(VGCREATE, [vgcreate], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(LVCREATE, [lvcreate], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(PVREMOVE, [pvremove], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(VGREMOVE, [vgremove], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(LVREMOVE, [lvremove], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(VGCHANGE, [vgchange], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(PVS, [pvs], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(VGS, [vgs], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(LVS, [lvs], [], [$PATH:/sbin:/usr/sbin]) + + if test "$with_storage_lvm" = "yes" ; then + if test -z "$PVCREATE" ; then AC_MSG_ERROR(We need pvcreate for LVM storage driver) ; fi + if test -z "$VGCREATE" ; then AC_MSG_ERROR(We need vgcreate for LVM storage driver) ; fi + if test -z "$LVCREATE" ; then AC_MSG_ERROR(We need lvcreate for LVM storage driver) ; fi + if test -z "$PVREMOVE" ; then AC_MSG_ERROR(We need pvremove for LVM storage driver) ; fi + if test -z "$VGREMOVE" ; then AC_MSG_ERROR(We need vgremove for LVM storage driver) ; fi + if test -z "$LVREMOVE" ; then AC_MSG_ERROR(We need lvremove for LVM storage driver) ; fi + if test -z "$VGCHANGE" ; then AC_MSG_ERROR(We need vgchange for LVM storage driver) ; fi + if test -z "$PVS" ; then AC_MSG_ERROR(We need pvs for LVM storage driver) ; fi + if test -z "$VGS" ; then AC_MSG_ERROR(We need vgs for LVM storage driver) ; fi + if test -z "$LVS" ; then AC_MSG_ERROR(We need lvs for LVM storage driver) ; fi + else + if test -z "$PVCREATE" ; then with_storage_lvm=no ; fi + if test -z "$VGCREATE" ; then with_storage_lvm=no ; fi + if test -z "$LVCREATE" ; then with_storage_lvm=no ; fi + if test -z "$PVREMOVE" ; then with_storage_lvm=no ; fi + if test -z "$VGREMOVE" ; then with_storage_lvm=no ; fi + if test -z "$LVREMOVE" ; then with_storage_lvm=no ; fi + if test -z "VGCHANGE" ; then with_storage_lvm=no ; fi + if test -z "$PVS" ; then with_storage_lvm=no ; fi + if test -z "$VGS" ; then with_storage_lvm=no ; fi + if test -z "$LVS" ; then with_storage_lvm=no ; fi + + if test "$with_storage_lvm" = "check" ; then with_storage_lvm=yes ; fi + fi + + if test "$with_storage_lvm" = "yes" ; then + AC_DEFINE_UNQUOTED(WITH_STORAGE_LVM, 1, [whether LVM backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([PVCREATE],["$PVCREATE"],[Location of pvcreate program]) + AC_DEFINE_UNQUOTED([VGCREATE],["$VGCREATE"],[Location of vgcreate program]) + AC_DEFINE_UNQUOTED([LVCREATE],["$LVCREATE"],[Location of lvcreate program]) + AC_DEFINE_UNQUOTED([PVREMOVE],["$PVREMOVE"],[Location of pvcreate program]) + AC_DEFINE_UNQUOTED([VGREMOVE],["$VGREMOVE"],[Location of vgcreate program]) + AC_DEFINE_UNQUOTED([LVREMOVE],["$LVREMOVE"],[Location of lvcreate program]) + AC_DEFINE_UNQUOTED([VGCHANGE],["$VGCHANGE"],[Location of vgchange program]) + AC_DEFINE_UNQUOTED([PVS],["$PVS"],[Location of pvs program]) + AC_DEFINE_UNQUOTED([VGS],["$VGS"],[Location of vgs program]) + AC_DEFINE_UNQUOTED([LVS],["$LVS"],[Location of lvs program]) + fi +fi +AM_CONDITIONAL(WITH_STORAGE_LVM, [test "$with_storage_lvm" = "yes"]) dnl @@ -811,6 +868,7 @@ AC_MSG_NOTICE([ Dir: yes]) AC_MSG_NOTICE([ Dir: yes]) AC_MSG_NOTICE([ FS: $with_storage_fs]) AC_MSG_NOTICE([ NetFS: $with_storage_fs]) +AC_MSG_NOTICE([ LVM: $with_storage_lvm]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) diff -r 31abfd8687b3 docs/storage/pool-logical.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-logical.xml Thu Feb 07 14:17:16 2008 -0500 @@ -0,0 +1,6 @@ +<pool type="logical"> + <name>HostVG</name> + <source> + <device>/dev/sda1</device> + </source> +</pool> diff -r 31abfd8687b3 libvirt.spec.in --- a/libvirt.spec.in Thu Feb 07 13:44:25 2008 -0500 +++ b/libvirt.spec.in Thu Feb 07 14:17:16 2008 -0500 @@ -49,6 +49,8 @@ Requires: /usr/bin/qemu-img # From Xen RPMs Requires: /usr/sbin/qcow-create %endif +# For LVM drivers +Requires: lvm2 BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -73,6 +75,8 @@ BuildRequires: /usr/bin/qemu-img # From Xen RPMs BuildRequires: /usr/sbin/qcow-create %endif +# For LVM drivers +BuildRequires: lvm2 Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 diff -r 31abfd8687b3 qemud/qemud.c --- a/qemud/qemud.c Thu Feb 07 13:44:25 2008 -0500 +++ b/qemud/qemud.c Thu Feb 07 14:17:16 2008 -0500 @@ -2089,7 +2089,9 @@ int main(int argc, char **argv) { if (pipe(sigpipe) < 0 || qemudSetNonBlock(sigpipe[0]) < 0 || - qemudSetNonBlock(sigpipe[1]) < 0) { + qemudSetNonBlock(sigpipe[1]) < 0 || + qemudSetCloseExec(sigpipe[0]) < 0 || + qemudSetCloseExec(sigpipe[1]) < 0) { qemudLog(QEMUD_ERR, _("Failed to create pipe: %s"), strerror(errno)); goto error1; diff -r 31abfd8687b3 src/Makefile.am --- a/src/Makefile.am Thu Feb 07 13:44:25 2008 -0500 +++ b/src/Makefile.am Thu Feb 07 14:17:16 2008 -0500 @@ -69,6 +69,14 @@ SERVER_SOURCES = \ SERVER_SOURCES = \ ../qemud/remote_protocol.c ../qemud/remote_protocol.h +if WITH_STORAGE_LVM +CLIENT_SOURCES += storage_backend_logical.h storage_backend_logical.c +else +EXTRA_DIST += storage_backend_logical.h storage_backend_logical.c +endif + + + libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) $(SELINUX_LIBS) \ @CYGWIN_EXTRA_LIBADD@ ../gnulib/lib/libgnu.la diff -r 31abfd8687b3 src/storage_backend.c --- a/src/storage_backend.c Thu Feb 07 13:44:25 2008 -0500 +++ b/src/storage_backend.c Thu Feb 07 14:17:16 2008 -0500 @@ -36,6 +36,9 @@ #if HAVE_SELINUX #include <selinux/selinux.h> #endif +#if WITH_STORAGE_LVM +#include "storage_backend_logical.h" +#endif #include "util.h" @@ -47,6 +50,9 @@ static virStorageBackendPtr backends[] = #if WITH_STORAGE_FS &virStorageBackendFileSystem, &virStorageBackendNetFileSystem, +#endif +#if WITH_STORAGE_LVM + &virStorageBackendLogical, #endif }; @@ -84,6 +90,10 @@ int virStorageBackendFromString(const ch return VIR_STORAGE_POOL_FS; if (STREQ(type, "netfs")) return VIR_STORAGE_POOL_NETFS; +#endif +#if WITH_STORAGE_LVM + if (STREQ(type, "logical")) + return VIR_STORAGE_POOL_LOGICAL; #endif virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %s", type); return -1; @@ -98,6 +108,10 @@ const char *virStorageBackendToString(in return "fs"; case VIR_STORAGE_POOL_NETFS: return "netfs"; +#endif +#if WITH_STORAGE_LVM + case VIR_STORAGE_POOL_LOGICAL: + return "logical"; #endif } diff -r 31abfd8687b3 src/storage_backend_logical.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_logical.c Thu Feb 07 14:17:16 2008 -0500 @@ -0,0 +1,497 @@ +/* + * storage_backend_logvol.c: storage backend for logical volume handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <sys/wait.h> +#include <sys/stat.h> +#include <stdio.h> +#include <regex.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include "storage_backend_logical.h" +#include "storage_conf.h" +#include "util.h" + + +enum { + VIR_STORAGE_POOL_LOGICAL_LVM2 = 0, +}; + + +static int virStorageBackendLogicalPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_LOGICAL_LVM2; + + if (STREQ(format, "lvm2")) + return VIR_STORAGE_POOL_LOGICAL_LVM2; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported pool format %s", format); + return -1; +} + +static const char *virStorageBackendLogicalPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_LOGICAL_LVM2: + return "lvm2"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported pool format %d", format); + return NULL; +} + +static int virStorageBackendLogicalSetActive(virConnectPtr conn, + virStoragePoolObjPtr pool, + int on) +{ + const char *cmdargv[4]; + + cmdargv[0] = VGCHANGE; + cmdargv[1] = on ? "-ay" : "-an"; + cmdargv[2] = pool->def->name; + cmdargv[3] = NULL; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + return 0; +} + + +static int virStorageBackendLogicalMakeVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + void *data) +{ + virStorageVolDefPtr vol = NULL; + virStorageVolSourceExtentPtr tmp; + unsigned long long offset, size, length; + + /* See if we're only looking for a specific volume */ + if (data != NULL) { + vol = data; + if (STRNEQ(vol->name, groups[0])) + return 0; + } + + /* Or filling in more data on an existing volume */ + if (vol == NULL) + vol = virStorageVolDefFindByName(pool, groups[0]); + + /* Or a completely new volume */ + if (vol == NULL) { + if ((vol = calloc(1, sizeof(*vol))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + return -1; + } + + if ((vol->name = strdup(groups[0])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + return -1; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + } + + if (vol->target.path == NULL) { + if ((vol->target.path = malloc(strlen(pool->def->target.path) + 1 + strlen(vol->name) + 1)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + return -1; + } + strcpy(vol->target.path, pool->def->target.path); + strcat(vol->target.path, "/"); + strcat(vol->target.path, vol->name); + } + + if (vol->key == NULL && + (vol->key = strdup(groups[1])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + return -1; + } + + if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0) + return -1; + + + /* Finally fill in extents information */ + if ((tmp = realloc(vol->source.extents, sizeof(*tmp) * (vol->source.nextent + 1))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "extents"); + return -1; + } + vol->source.extents = tmp; + + if ((vol->source.extents[vol->source.nextent].path = + strdup(groups[2])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "extents"); + return -1; + } + + if (xstrtol_ull(groups[3], NULL, 10, &offset) < 0) + return -1; + if (xstrtol_ull(groups[4], NULL, 10, &length) < 0) + return -1; + if (xstrtol_ull(groups[5], NULL, 10, &size) < 0) + return -1; + + vol->source.extents[vol->source.nextent].start = offset * size; + vol->source.extents[vol->source.nextent].end = (offset * size) + length; + vol->source.nextent++; + + return 0; +} + +static int virStorageBackendLogicalFindLVs(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + /* + * # lvs --separator : --noheadings --units b --unbuffered --nosuffix --options "lv_name,uuid,devices,seg_size,vg_extent_size" VGNAME + * RootLV:06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky:/dev/hda2(0):5234491392:33554432 + * SwapLV:oHviCK-8Ik0-paqS-V20c-nkhY-Bm1e-zgzU0M:/dev/hda2(156):1040187392:33554432 + * Test2:3pg3he-mQsA-5Sui-h0i6-HNmc-Cz7W-QSndcR:/dev/hda2(219):1073741824:33554432 + * Test3:UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht:/dev/hda2(251):2181038080:33554432 + * Test3:UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht:/dev/hda2(187):1040187392:33554432 + * + * Pull out name & uuid, device, device extent start #, segment size, extent size. + * + * NB can be multiple rows per volume if they have many extents + */ + const char *regexes[] = { + "^\\s*(\\S+):(\\S+):(\\S+)\\((\\S+)\\):(\\S+):(\\S+)\\s*$" + }; + int vars[] = { + 6 + }; + const char *prog[] = { + LVS, "--separator", ":", "--noheadings", "--units", "b", "--unbuffered", + "--nosuffix", "--options", "lv_name,uuid,devices,seg_size,vg_extent_size", + pool->def->name, NULL + }; + + return virStorageBackendRunProgRegex(conn, + pool, + prog, + 1, + regexes, + vars, + virStorageBackendLogicalMakeVol, + vol); +} + +static int virStorageBackendLogicalRefreshPoolFunc(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + char **const groups, + void *data ATTRIBUTE_UNUSED) +{ + if (xstrtol_ull(groups[0], NULL, 10, &pool->def->capacity) < 0) + return -1; + if (xstrtol_ull(groups[1], NULL, 10, &pool->def->available) < 0) + return -1; + pool->def->allocation = pool->def->capacity - pool->def->available; + + return 0; +} + + +static int virStorageBackendLogicalStartPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + if (virStorageBackendLogicalSetActive(conn, pool, 1) < 0) + return -1; + + return 0; +} + + +static int virStorageBackendLogicalBuildPool(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + const char **vgargv; + const char *pvargv[3]; + int n = 0, i; + + /* XXX multiple pvs */ + if ((vgargv = malloc(sizeof(char*) * (1))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "argv"); + return -1; + } + + vgargv[n++] = VGCREATE; + vgargv[n++] = pool->def->name; + + pvargv[0] = PVCREATE; + pvargv[2] = NULL; + for (i = 0 ; i < pool->def->source.ndevice ; i++) { + int fd; + char zeros[512]; + memset(zeros, 0, sizeof(zeros)); + + /* + * LVM requires that the first 512 bytes are blanked if using + * a whole disk as a PV. So we just blank them out regardless + * rather than trying to figure out if we're a disk or partition + */ + if ((fd = open(pool->def->source.devices[i].path, O_WRONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot open device %s (%d)", + strerror(errno), errno); + goto cleanup; + } + if (write(fd, zeros, sizeof(zeros)) != sizeof(zeros)) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot clear device header %s (%d)", + strerror(errno), errno); + close(fd); + goto cleanup; + } + if (close(fd) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot close device %s (%d)", + strerror(errno), errno); + goto cleanup; + } + + /* + * Initialize the physical volume because vgcreate is not + * clever enough todo this for us :-( + */ + vgargv[n++] = pool->def->source.devices[i].path; + pvargv[1] = pool->def->source.devices[i].path; + if (virRun(conn, (char**)pvargv, NULL) < 0) + goto cleanup; + } + + vgargv[n++] = NULL; + + /* Now create the volume group itself */ + if (virRun(conn, (char**)vgargv, NULL) < 0) + goto cleanup; + + free(vgargv); + + return 0; + + cleanup: + free(vgargv); + return -1; +} + + +static int virStorageBackendLogicalRefreshPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + /* + * # vgs --separator : --noheadings --units b --unbuffered --nosuffix --options "vg_size,vg_free" VGNAME + * 10603200512:4328521728 + * + * Pull out size & free + */ + const char *regexes[] = { + "^\\s*(\\S+):(\\S+)\\s*$" + }; + int vars[] = { + 2 + }; + const char *prog[] = { + VGS, "--separator", ":", "--noheadings", "--units", "b", "--unbuffered", + "--nosuffix", "--options", "vg_size,vg_free", + pool->def->name, NULL + }; + + /* Get list of all logical volumes */ + if (virStorageBackendLogicalFindLVs(conn, pool, NULL) < 0) { + virStoragePoolObjClearVols(pool); + return -1; + } + + /* Now get basic volgrp metadata */ + if (virStorageBackendRunProgRegex(conn, + pool, + prog, + 1, + regexes, + vars, + virStorageBackendLogicalRefreshPoolFunc, + NULL) < 0) { + virStoragePoolObjClearVols(pool); + return -1; + } + + return 0; +} + + +/* XXX should we set LVM to inactive ? Probably not - it would + * suck if this were your LVM root fs :-) + */ +#if 0 +static int virStorageBackendLogicalStopPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + if (virStorageBackendLogicalSetActive(conn, pool, 0) < 0) + return -1; + + return 0; +} +#endif + +static int virStorageBackendLogicalDeletePool(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + const char *cmdargv[] = { + VGREMOVE, "-f", pool->def->name, NULL + }; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + /* XXX clear the PVs too ? ie pvremove ? probably ought to */ + + return 0; +} + + +static int virStorageBackendLogicalDeleteVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + unsigned int flags); + + +static int virStorageBackendLogicalCreateVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + int fd = -1; + char size[100]; + const char *cmdargv[] = { + LVCREATE, "--name", vol->name, "-L", size, + pool->def->target.path, NULL + }; + + snprintf(size, sizeof(size)-1, "%lluK", vol->capacity/1024); + size[sizeof(size)-1] = '\0'; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read path '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + goto cleanup; + } + + /* We can only chown/grp if root */ + if (getuid() == 0) { + if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot set file owner '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + goto cleanup; + } + } + if (fchmod(fd, vol->target.perms.mode) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot set file mode '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + goto cleanup; + } + + if (close(fd) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot close file '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + goto cleanup; + } + fd = -1; + + /* Fill in data about this new vol */ + if (virStorageBackendLogicalFindLVs(conn, pool, vol) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot find newly created volume '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + goto cleanup; + } + + return 0; + + cleanup: + if (fd != -1) + close(fd); + virStorageBackendLogicalDeleteVol(conn, pool, vol, 0); + return -1; +} + +static int virStorageBackendLogicalDeleteVol(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + unsigned int flags ATTRIBUTE_UNUSED) +{ + const char *cmdargv[] = { + LVREMOVE, "-f", vol->target.path, NULL + }; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + return 0; +} + + +virStorageBackend virStorageBackendLogical = { + .type = VIR_STORAGE_POOL_LOGICAL, + + .startPool = virStorageBackendLogicalStartPool, + .buildPool = virStorageBackendLogicalBuildPool, + .refreshPool = virStorageBackendLogicalRefreshPool, +#if 0 + .stopPool = virStorageBackendLogicalStopPool, +#endif + .deletePool = virStorageBackendLogicalDeletePool, + .createVol = virStorageBackendLogicalCreateVol, + .deleteVol = virStorageBackendLogicalDeleteVol, + + .poolOptions = { + .formatFromString = virStorageBackendLogicalPoolFormatFromString, + .formatToString = virStorageBackendLogicalPoolFormatToString, + }, + + .volType = VIR_STORAGE_VOL_BLOCK, +}; + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 31abfd8687b3 src/storage_backend_logical.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_logical.h Thu Feb 07 14:17:16 2008 -0500 @@ -0,0 +1,45 @@ +/* + * storage_backend_logical.h: storage backend for logical volume handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_BACKEND_LOGICAL_H__ +#define __VIR_STORAGE_BACKEND_LOGICAL_H__ + +#include "storage_backend.h" + +extern virStorageBackend virStorageBackendLogical; + +#endif /* __VIR_STORAGE_BACKEND_LOGVOL_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:38:39AM +0000, Daniel P. Berrange wrote:
This patch implements a storage pool based on logical volume management. The underlying implementation calls out to LVM on Linux. A future implementation for Solaris would use the ZFS pool support to achieve the same functionality. The LVM impl uses the LVM command line tools, in particular lvs, and vgs for listing, and the *create, *remove tools for modifications. The 'build' operation will construct a new volume group, and initialize any physical volume members. The 'delete' operation will permanently remove the group. Volumes are allocated by carving out logical volumes. There are no placement constraints how the volume XML will show the actual storage per-volume on the underlying physical volumes. The LVM UUID is used for the unique volume keys.
Okay, it would be good to get it tested on other Linux distros
diff -r 31abfd8687b3 qemud/qemud.c --- a/qemud/qemud.c Thu Feb 07 13:44:25 2008 -0500 +++ b/qemud/qemud.c Thu Feb 07 14:17:16 2008 -0500 @@ -2089,7 +2089,9 @@ int main(int argc, char **argv) {
if (pipe(sigpipe) < 0 || qemudSetNonBlock(sigpipe[0]) < 0 || - qemudSetNonBlock(sigpipe[1]) < 0) { + qemudSetNonBlock(sigpipe[1]) < 0 || + qemudSetCloseExec(sigpipe[0]) < 0 || + qemudSetCloseExec(sigpipe[1]) < 0) { qemudLog(QEMUD_ERR, _("Failed to create pipe: %s"), strerror(errno)); goto error1;
That can be commited completely independantly, its a bug fix Seems some of the code tries to be generic, what other kind of logical volume do you have in mind ? [...]
+ + /* Finally fill in extents information */ + if ((tmp = realloc(vol->source.extents, sizeof(*tmp) * (vol->source.nextent + 1))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "extents"); + return -1; + } + vol->source.extents = tmp; + + if ((vol->source.extents[vol->source.nextent].path = + strdup(groups[2])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "extents"); + return -1; + } + + if (xstrtol_ull(groups[3], NULL, 10, &offset) < 0) + return -1; + if (xstrtol_ull(groups[4], NULL, 10, &length) < 0) + return -1; + if (xstrtol_ull(groups[5], NULL, 10, &size) < 0) + return -1;
Can we really just return -1 here for error handling at that point ? vol->source had had some of its fields filled, but incompletely initialized this looks dangerous. [...]
+ for (i = 0 ; i < pool->def->source.ndevice ; i++) { + int fd; + char zeros[512]; + memset(zeros, 0, sizeof(zeros));
those 2 can probably be moved out of the loop
+ + /* + * LVM requires that the first 512 bytes are blanked if using + * a whole disk as a PV. So we just blank them out regardless + * rather than trying to figure out if we're a disk or partition + */
is it really 512 or the block size on the device used ? But 512 is probably sufficient for LVM to consider it cleared, just wondering ... Looks fine to me, +1 Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Tue, Feb 19, 2008 at 03:43:11AM -0500, Daniel Veillard wrote:
diff -r 31abfd8687b3 qemud/qemud.c --- a/qemud/qemud.c Thu Feb 07 13:44:25 2008 -0500 +++ b/qemud/qemud.c Thu Feb 07 14:17:16 2008 -0500 @@ -2089,7 +2089,9 @@ int main(int argc, char **argv) {
if (pipe(sigpipe) < 0 || qemudSetNonBlock(sigpipe[0]) < 0 || - qemudSetNonBlock(sigpipe[1]) < 0) { + qemudSetNonBlock(sigpipe[1]) < 0 || + qemudSetCloseExec(sigpipe[0]) < 0 || + qemudSetCloseExec(sigpipe[1]) < 0) { qemudLog(QEMUD_ERR, _("Failed to create pipe: %s"), strerror(errno)); goto error1;
That can be commited completely independantly, its a bug fix
Yes, I found this because the LVM tools will print out a warning message if they're passed any file descriptor > 2 !
Seems some of the code tries to be generic, what other kind of logical volume do you have in mind ?
Solaris has ZFS which provides parity with LVM volume manegement so I intend that they'd have a ZFS impl of 'logical' pool type
[...]
+ + /* Finally fill in extents information */ + if ((tmp = realloc(vol->source.extents, sizeof(*tmp) * (vol->source.nextent + 1))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "extents"); + return -1; + } + vol->source.extents = tmp; + + if ((vol->source.extents[vol->source.nextent].path = + strdup(groups[2])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "extents"); + return -1; + } + + if (xstrtol_ull(groups[3], NULL, 10, &offset) < 0) + return -1; + if (xstrtol_ull(groups[4], NULL, 10, &length) < 0) + return -1; + if (xstrtol_ull(groups[5], NULL, 10, &size) < 0) + return -1;
Can we really just return -1 here for error handling at that point ? vol->source had had some of its fields filled, but incompletely initialized this looks dangerous.
The caller will free the entire object & shutdown the pool if this fails. I should however, report the error message before returning.
[...]
+ for (i = 0 ; i < pool->def->source.ndevice ; i++) { + int fd; + char zeros[512]; + memset(zeros, 0, sizeof(zeros));
those 2 can probably be moved out of the loop
Yep.
+ + /* + * LVM requires that the first 512 bytes are blanked if using + * a whole disk as a PV. So we just blank them out regardless + * rather than trying to figure out if we're a disk or partition + */
is it really 512 or the block size on the device used ? But 512 is probably sufficient for LVM to consider it cleared, just wondering ...
The 'pvcreate' man page explicitly says the first sector <quote> For whole disk devices only the partition table must be erased, which will effectively destroy all data on that disk. This can be done by zeroing the first sector with: dd if=/dev/zero of=PhysicalVolume bs=512 count=1 </quote> So 512 is fine for MSDOS partition tables at least. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 19, 2008 at 12:46:28PM +0000, Daniel P. Berrange wrote:
On Tue, Feb 19, 2008 at 03:43:11AM -0500, Daniel Veillard wrote:
+ + /* + * LVM requires that the first 512 bytes are blanked if using + * a whole disk as a PV. So we just blank them out regardless + * rather than trying to figure out if we're a disk or partition + */
is it really 512 or the block size on the device used ? But 512 is probably sufficient for LVM to consider it cleared, just wondering ...
The 'pvcreate' man page explicitly says the first sector
<quote> For whole disk devices only the partition table must be erased, which will effectively destroy all data on that disk. This can be done by zeroing the first sector with:
dd if=/dev/zero of=PhysicalVolume bs=512 count=1 </quote>
So 512 is fine for MSDOS partition tables at least.
Ahh, the legacy :-) thanks for the explanations ! Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
+ for (i = 0 ; i < pool->def->source.ndevice ; i++) { + int fd; + char zeros[512]; + memset(zeros, 0, sizeof(zeros)); ... is it really 512 or the block size on the device used ? But 512 is probably sufficient for LVM to consider it cleared, just wondering ...
The 'pvcreate' man page explicitly says the first sector
<quote> For whole disk devices only the partition table must be erased, which will effectively destroy all data on that disk. This can be done by zeroing the first sector with:
dd if=/dev/zero of=PhysicalVolume bs=512 count=1 </quote>
So 512 is fine for MSDOS partition tables at least.
How about giving the constant a name? That'd make it more readable. #define SECTOR_SIZE 512 -- Jim, who has spent far too much time dealing with hard-coded literal array sizes like that in parted, when I made parts of it work with larger-than-512-byte sectors.

On Tue, Feb 12, 2008 at 04:38:39AM +0000, Daniel P. Berrange wrote:
This patch implements a storage pool based on logical volume management. The underlying implementation calls out to LVM on Linux. A future implementation for Solaris would use the ZFS pool support to achieve the same functionality. The LVM impl uses the LVM command line tools, in particular lvs, and vgs for listing, and the *create, *remove tools for modifications. The 'build' operation will construct a new volume group, and initialize any physical volume members. The 'delete' operation will permanently remove the group. Volumes are allocated by carving out logical volumes. There are no placement constraints how the volume XML will show the actual storage per-volume on the underlying physical volumes. The LVM UUID is used for the unique volume keys.
b/docs/storage/pool-logical.xml | 9 b/src/storage_backend_logical.c | 531 ++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_logical.h | 45 +++ configure.in | 58 ++++ libvirt.spec.in | 4 po/POTFILES.in | 1 qemud/qemud.c | 4 src/Makefile.am | 8 src/storage_backend.c | 14 + 9 files changed, 673 insertions(+), 1 deletion(-) diff -r 555d6e919c35 configure.in --- a/configure.in Tue Feb 19 17:22:05 2008 -0500 +++ b/configure.in Tue Feb 19 17:31:03 2008 -0500 @@ -559,6 +559,8 @@ dnl AC_ARG_WITH(storage-fs, [ --with-storage-fs with FileSystem backend for the storage driver (on)],[],[with_storage_fs=check]) +AC_ARG_WITH(storage-lvm, +[ --with-storage-lvm with LVM backend for the storage driver (on)],[],[with_storage_lvm=check]) if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin]) @@ -596,6 +598,61 @@ if test -n "$QCOW_CREATE" ; then AC_DEFINE_UNQUOTED([QCOW_CREATE],["$QCOW_CREATE"], [Location or name of the qcow-create program]) fi + + +if test "$with_storage_lvm" = "yes" -o "$with_storage_lvm" = "check"; then + AC_PATH_PROG(PVCREATE, [pvcreate], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(VGCREATE, [vgcreate], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(LVCREATE, [lvcreate], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(PVREMOVE, [pvremove], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(VGREMOVE, [vgremove], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(LVREMOVE, [lvremove], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(VGCHANGE, [vgchange], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(PVS, [pvs], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(VGS, [vgs], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(LVS, [lvs], [], [$PATH:/sbin:/usr/sbin]) + + if test "$with_storage_lvm" = "yes" ; then + if test -z "$PVCREATE" ; then AC_MSG_ERROR(We need pvcreate for LVM storage driver) ; fi + if test -z "$VGCREATE" ; then AC_MSG_ERROR(We need vgcreate for LVM storage driver) ; fi + if test -z "$LVCREATE" ; then AC_MSG_ERROR(We need lvcreate for LVM storage driver) ; fi + if test -z "$PVREMOVE" ; then AC_MSG_ERROR(We need pvremove for LVM storage driver) ; fi + if test -z "$VGREMOVE" ; then AC_MSG_ERROR(We need vgremove for LVM storage driver) ; fi + if test -z "$LVREMOVE" ; then AC_MSG_ERROR(We need lvremove for LVM storage driver) ; fi + if test -z "$VGCHANGE" ; then AC_MSG_ERROR(We need vgchange for LVM storage driver) ; fi + if test -z "$PVS" ; then AC_MSG_ERROR(We need pvs for LVM storage driver) ; fi + if test -z "$VGS" ; then AC_MSG_ERROR(We need vgs for LVM storage driver) ; fi + if test -z "$LVS" ; then AC_MSG_ERROR(We need lvs for LVM storage driver) ; fi + else + if test -z "$PVCREATE" ; then with_storage_lvm=no ; fi + if test -z "$VGCREATE" ; then with_storage_lvm=no ; fi + if test -z "$LVCREATE" ; then with_storage_lvm=no ; fi + if test -z "$PVREMOVE" ; then with_storage_lvm=no ; fi + if test -z "$VGREMOVE" ; then with_storage_lvm=no ; fi + if test -z "$LVREMOVE" ; then with_storage_lvm=no ; fi + if test -z "VGCHANGE" ; then with_storage_lvm=no ; fi + if test -z "$PVS" ; then with_storage_lvm=no ; fi + if test -z "$VGS" ; then with_storage_lvm=no ; fi + if test -z "$LVS" ; then with_storage_lvm=no ; fi + + if test "$with_storage_lvm" = "check" ; then with_storage_lvm=yes ; fi + fi + + if test "$with_storage_lvm" = "yes" ; then + AC_DEFINE_UNQUOTED(WITH_STORAGE_LVM, 1, [whether LVM backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([PVCREATE],["$PVCREATE"],[Location of pvcreate program]) + AC_DEFINE_UNQUOTED([VGCREATE],["$VGCREATE"],[Location of vgcreate program]) + AC_DEFINE_UNQUOTED([LVCREATE],["$LVCREATE"],[Location of lvcreate program]) + AC_DEFINE_UNQUOTED([PVREMOVE],["$PVREMOVE"],[Location of pvcreate program]) + AC_DEFINE_UNQUOTED([VGREMOVE],["$VGREMOVE"],[Location of vgcreate program]) + AC_DEFINE_UNQUOTED([LVREMOVE],["$LVREMOVE"],[Location of lvcreate program]) + AC_DEFINE_UNQUOTED([VGCHANGE],["$VGCHANGE"],[Location of vgchange program]) + AC_DEFINE_UNQUOTED([PVS],["$PVS"],[Location of pvs program]) + AC_DEFINE_UNQUOTED([VGS],["$VGS"],[Location of vgs program]) + AC_DEFINE_UNQUOTED([LVS],["$LVS"],[Location of lvs program]) + fi +fi +AM_CONDITIONAL(WITH_STORAGE_LVM, [test "$with_storage_lvm" = "yes"]) dnl @@ -811,6 +868,7 @@ AC_MSG_NOTICE([ Dir: yes]) AC_MSG_NOTICE([ Dir: yes]) AC_MSG_NOTICE([ FS: $with_storage_fs]) AC_MSG_NOTICE([ NetFS: $with_storage_fs]) +AC_MSG_NOTICE([ LVM: $with_storage_lvm]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) diff -r 555d6e919c35 docs/storage/pool-logical.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-logical.xml Tue Feb 19 17:31:03 2008 -0500 @@ -0,0 +1,9 @@ +<pool type="logical"> + <name>HostVG</name> + <source> + <device path="/dev/sda1"/> + </source> + <target> + <path>/dev/HostVG</path> + </target> +</pool> diff -r 555d6e919c35 libvirt.spec.in --- a/libvirt.spec.in Tue Feb 19 17:22:05 2008 -0500 +++ b/libvirt.spec.in Tue Feb 19 17:31:03 2008 -0500 @@ -49,6 +49,8 @@ Requires: /usr/bin/qemu-img # From Xen RPMs Requires: /usr/sbin/qcow-create %endif +# For LVM drivers +Requires: lvm2 BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -73,6 +75,8 @@ BuildRequires: /usr/bin/qemu-img # From Xen RPMs BuildRequires: /usr/sbin/qcow-create %endif +# For LVM drivers +BuildRequires: lvm2 Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 diff -r 555d6e919c35 po/POTFILES.in --- a/po/POTFILES.in Tue Feb 19 17:22:05 2008 -0500 +++ b/po/POTFILES.in Tue Feb 19 17:31:03 2008 -0500 @@ -12,6 +12,7 @@ src/remote_internal.c src/remote_internal.c src/storage_backend.c src/storage_backend_fs.c +src/storage_backend_logical.c src/storage_conf.c src/storage_driver.c src/sexpr.c diff -r 555d6e919c35 qemud/qemud.c --- a/qemud/qemud.c Tue Feb 19 17:22:05 2008 -0500 +++ b/qemud/qemud.c Tue Feb 19 17:31:03 2008 -0500 @@ -2089,7 +2089,9 @@ int main(int argc, char **argv) { if (pipe(sigpipe) < 0 || qemudSetNonBlock(sigpipe[0]) < 0 || - qemudSetNonBlock(sigpipe[1]) < 0) { + qemudSetNonBlock(sigpipe[1]) < 0 || + qemudSetCloseExec(sigpipe[0]) < 0 || + qemudSetCloseExec(sigpipe[1]) < 0) { qemudLog(QEMUD_ERR, _("Failed to create pipe: %s"), strerror(errno)); goto error1; diff -r 555d6e919c35 src/Makefile.am --- a/src/Makefile.am Tue Feb 19 17:22:05 2008 -0500 +++ b/src/Makefile.am Tue Feb 19 17:31:03 2008 -0500 @@ -68,6 +68,14 @@ SERVER_SOURCES = \ SERVER_SOURCES = \ ../qemud/remote_protocol.c ../qemud/remote_protocol.h +if WITH_STORAGE_LVM +CLIENT_SOURCES += storage_backend_logical.h storage_backend_logical.c +else +EXTRA_DIST += storage_backend_logical.h storage_backend_logical.c +endif + + + libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) $(SELINUX_LIBS) \ @CYGWIN_EXTRA_LIBADD@ ../gnulib/lib/libgnu.la diff -r 555d6e919c35 src/storage_backend.c --- a/src/storage_backend.c Tue Feb 19 17:22:05 2008 -0500 +++ b/src/storage_backend.c Tue Feb 19 17:31:03 2008 -0500 @@ -36,6 +36,9 @@ #if HAVE_SELINUX #include <selinux/selinux.h> #endif +#if WITH_STORAGE_LVM +#include "storage_backend_logical.h" +#endif #include "util.h" @@ -47,6 +50,9 @@ static virStorageBackendPtr backends[] = #if WITH_STORAGE_FS &virStorageBackendFileSystem, &virStorageBackendNetFileSystem, +#endif +#if WITH_STORAGE_LVM + &virStorageBackendLogical, #endif }; @@ -89,6 +95,10 @@ virStorageBackendFromString(const char * return VIR_STORAGE_POOL_FS; if (STREQ(type, "netfs")) return VIR_STORAGE_POOL_NETFS; +#endif +#if WITH_STORAGE_LVM + if (STREQ(type, "logical")) + return VIR_STORAGE_POOL_LOGICAL; #endif virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, @@ -106,6 +116,10 @@ virStorageBackendToString(int type) { return "fs"; case VIR_STORAGE_POOL_NETFS: return "netfs"; +#endif +#if WITH_STORAGE_LVM + case VIR_STORAGE_POOL_LOGICAL: + return "logical"; #endif } diff -r 555d6e919c35 src/storage_backend_logical.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_logical.c Tue Feb 19 17:31:03 2008 -0500 @@ -0,0 +1,531 @@ +/* + * storage_backend_logvol.c: storage backend for logical volume handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <sys/wait.h> +#include <sys/stat.h> +#include <stdio.h> +#include <regex.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include "storage_backend_logical.h" +#include "storage_conf.h" +#include "util.h" + + +#define PV_BLANK_SECTOR_SIZE 512 + +enum { + VIR_STORAGE_POOL_LOGICAL_LVM2 = 0, +}; + + +static int +virStorageBackendLogicalPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_LOGICAL_LVM2; + + if (STREQ(format, "lvm2")) + return VIR_STORAGE_POOL_LOGICAL_LVM2; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported pool format %s"), format); + return -1; +} + +static const char * +virStorageBackendLogicalPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_LOGICAL_LVM2: + return "lvm2"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported pool format %d"), format); + return NULL; +} + +static int +virStorageBackendLogicalSetActive(virConnectPtr conn, + virStoragePoolObjPtr pool, + int on) +{ + const char *cmdargv[4]; + + cmdargv[0] = VGCHANGE; + cmdargv[1] = on ? "-ay" : "-an"; + cmdargv[2] = pool->def->name; + cmdargv[3] = NULL; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + return 0; +} + + +static int +virStorageBackendLogicalMakeVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + void *data) +{ + virStorageVolDefPtr vol = NULL; + virStorageVolSourceExtentPtr tmp; + unsigned long long offset, size, length; + + /* See if we're only looking for a specific volume */ + if (data != NULL) { + vol = data; + if (STRNEQ(vol->name, groups[0])) + return 0; + } + + /* Or filling in more data on an existing volume */ + if (vol == NULL) + vol = virStorageVolDefFindByName(pool, groups[0]); + + /* Or a completely new volume */ + if (vol == NULL) { + if ((vol = calloc(1, sizeof(*vol))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + + if ((vol->name = strdup(groups[0])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + } + + if (vol->target.path == NULL) { + if ((vol->target.path = malloc(strlen(pool->def->target.path) + + 1 + strlen(vol->name) + 1)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + strcpy(vol->target.path, pool->def->target.path); + strcat(vol->target.path, "/"); + strcat(vol->target.path, vol->name); + } + + if (vol->key == NULL && + (vol->key = strdup(groups[1])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + + if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0) + return -1; + + + /* Finally fill in extents information */ + if ((tmp = realloc(vol->source.extents, sizeof(*tmp) + * (vol->source.nextent + 1))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("extents")); + return -1; + } + vol->source.extents = tmp; + + if ((vol->source.extents[vol->source.nextent].path = + strdup(groups[2])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("extents")); + return -1; + } + + if (virStrToLong_ull(groups[3], NULL, 10, &offset) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("malformed volume extent offset value")); + return -1; + } + if (virStrToLong_ull(groups[4], NULL, 10, &length) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("malformed volume extent length value")); + return -1; + } + if (virStrToLong_ull(groups[5], NULL, 10, &size) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("malformed volume extent size value")); + return -1; + } + + vol->source.extents[vol->source.nextent].start = offset * size; + vol->source.extents[vol->source.nextent].end = (offset * size) + length; + vol->source.nextent++; + + return 0; +} + +static int +virStorageBackendLogicalFindLVs(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + /* + * # lvs --separator : --noheadings --units b --unbuffered --nosuffix --options "lv_name,uuid,devices,seg_size,vg_extent_size" VGNAME + * RootLV:06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky:/dev/hda2(0):5234491392:33554432 + * SwapLV:oHviCK-8Ik0-paqS-V20c-nkhY-Bm1e-zgzU0M:/dev/hda2(156):1040187392:33554432 + * Test2:3pg3he-mQsA-5Sui-h0i6-HNmc-Cz7W-QSndcR:/dev/hda2(219):1073741824:33554432 + * Test3:UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht:/dev/hda2(251):2181038080:33554432 + * Test3:UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht:/dev/hda2(187):1040187392:33554432 + * + * Pull out name & uuid, device, device extent start #, segment size, extent size. + * + * NB can be multiple rows per volume if they have many extents + */ + const char *regexes[] = { + "^\\s*(\\S+):(\\S+):(\\S+)\\((\\S+)\\):(\\S+):(\\S+)\\s*$" + }; + int vars[] = { + 6 + }; + const char *prog[] = { + LVS, "--separator", ":", "--noheadings", "--units", "b", + "--unbuffered", "--nosuffix", "--options", + "lv_name,uuid,devices,seg_size,vg_extent_size", + pool->def->name, NULL + }; + + return virStorageBackendRunProgRegex(conn, + pool, + prog, + 1, + regexes, + vars, + virStorageBackendLogicalMakeVol, + vol); +} + +static int +virStorageBackendLogicalRefreshPoolFunc(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + char **const groups, + void *data ATTRIBUTE_UNUSED) +{ + if (virStrToLong_ull(groups[0], NULL, 10, &pool->def->capacity) < 0) + return -1; + if (virStrToLong_ull(groups[1], NULL, 10, &pool->def->available) < 0) + return -1; + pool->def->allocation = pool->def->capacity - pool->def->available; + + return 0; +} + + +static int +virStorageBackendLogicalStartPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + if (virStorageBackendLogicalSetActive(conn, pool, 1) < 0) + return -1; + + return 0; +} + + +static int +virStorageBackendLogicalBuildPool(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + const char **vgargv; + const char *pvargv[3]; + int n = 0, i, fd; + char zeros[PV_BLANK_SECTOR_SIZE]; + + memset(zeros, 0, sizeof(zeros)); + + /* XXX multiple pvs */ + if ((vgargv = malloc(sizeof(char*) * (1))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("command line")); + return -1; + } + + vgargv[n++] = VGCREATE; + vgargv[n++] = pool->def->name; + + pvargv[0] = PVCREATE; + pvargv[2] = NULL; + for (i = 0 ; i < pool->def->source.ndevice ; i++) { + /* + * LVM requires that the first sector is blanked if using + * a whole disk as a PV. So we just blank them out regardless + * rather than trying to figure out if we're a disk or partition + */ + if ((fd = open(pool->def->source.devices[i].path, O_WRONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot open device %s"), + strerror(errno)); + goto cleanup; + } + if (write(fd, zeros, sizeof(zeros)) != sizeof(zeros)) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot clear device header %s"), + strerror(errno)); + close(fd); + goto cleanup; + } + if (close(fd) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot close device %s"), + strerror(errno)); + goto cleanup; + } + + /* + * Initialize the physical volume because vgcreate is not + * clever enough todo this for us :-( + */ + vgargv[n++] = pool->def->source.devices[i].path; + pvargv[1] = pool->def->source.devices[i].path; + if (virRun(conn, (char**)pvargv, NULL) < 0) + goto cleanup; + } + + vgargv[n++] = NULL; + + /* Now create the volume group itself */ + if (virRun(conn, (char**)vgargv, NULL) < 0) + goto cleanup; + + free(vgargv); + + return 0; + + cleanup: + free(vgargv); + return -1; +} + + +static int +virStorageBackendLogicalRefreshPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + /* + * # vgs --separator : --noheadings --units b --unbuffered --nosuffix --options "vg_size,vg_free" VGNAME + * 10603200512:4328521728 + * + * Pull out size & free + */ + const char *regexes[] = { + "^\\s*(\\S+):(\\S+)\\s*$" + }; + int vars[] = { + 2 + }; + const char *prog[] = { + VGS, "--separator", ":", "--noheadings", "--units", "b", "--unbuffered", + "--nosuffix", "--options", "vg_size,vg_free", + pool->def->name, NULL + }; + + /* Get list of all logical volumes */ + if (virStorageBackendLogicalFindLVs(conn, pool, NULL) < 0) { + virStoragePoolObjClearVols(pool); + return -1; + } + + /* Now get basic volgrp metadata */ + if (virStorageBackendRunProgRegex(conn, + pool, + prog, + 1, + regexes, + vars, + virStorageBackendLogicalRefreshPoolFunc, + NULL) < 0) { + virStoragePoolObjClearVols(pool); + return -1; + } + + return 0; +} + + +/* XXX should we set LVM to inactive ? Probably not - it would + * suck if this were your LVM root fs :-) + */ +#if 0 +static int +virStorageBackendLogicalStopPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + if (virStorageBackendLogicalSetActive(conn, pool, 0) < 0) + return -1; + + return 0; +} +#endif + +static int +virStorageBackendLogicalDeletePool(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + const char *cmdargv[] = { + VGREMOVE, "-f", pool->def->name, NULL + }; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + /* XXX clear the PVs too ? ie pvremove ? probably ought to */ + + return 0; +} + + +static int +virStorageBackendLogicalDeleteVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + unsigned int flags); + + +static int +virStorageBackendLogicalCreateVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + int fd = -1; + char size[100]; + const char *cmdargv[] = { + LVCREATE, "--name", vol->name, "-L", size, + pool->def->target.path, NULL + }; + + snprintf(size, sizeof(size)-1, "%lluK", vol->capacity/1024); + size[sizeof(size)-1] = '\0'; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot read path '%s': %s"), + vol->target.path, strerror(errno)); + goto cleanup; + } + + /* We can only chown/grp if root */ + if (getuid() == 0) { + if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot set file owner '%s': %s"), + vol->target.path, strerror(errno)); + goto cleanup; + } + } + if (fchmod(fd, vol->target.perms.mode) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot set file mode '%s': %s"), + vol->target.path, strerror(errno)); + goto cleanup; + } + + if (close(fd) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot close file '%s': %s"), + vol->target.path, strerror(errno)); + goto cleanup; + } + fd = -1; + + /* Fill in data about this new vol */ + if (virStorageBackendLogicalFindLVs(conn, pool, vol) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot find newly created volume '%s': %s"), + vol->target.path, strerror(errno)); + goto cleanup; + } + + return 0; + + cleanup: + if (fd != -1) + close(fd); + virStorageBackendLogicalDeleteVol(conn, pool, vol, 0); + return -1; +} + +static int +virStorageBackendLogicalDeleteVol(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + unsigned int flags ATTRIBUTE_UNUSED) +{ + const char *cmdargv[] = { + LVREMOVE, "-f", vol->target.path, NULL + }; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + return 0; +} + + +virStorageBackend virStorageBackendLogical = { + .type = VIR_STORAGE_POOL_LOGICAL, + + .startPool = virStorageBackendLogicalStartPool, + .buildPool = virStorageBackendLogicalBuildPool, + .refreshPool = virStorageBackendLogicalRefreshPool, +#if 0 + .stopPool = virStorageBackendLogicalStopPool, +#endif + .deletePool = virStorageBackendLogicalDeletePool, + .createVol = virStorageBackendLogicalCreateVol, + .deleteVol = virStorageBackendLogicalDeleteVol, + + .poolOptions = { + .formatFromString = virStorageBackendLogicalPoolFormatFromString, + .formatToString = virStorageBackendLogicalPoolFormatToString, + }, + + .volType = VIR_STORAGE_VOL_BLOCK, +}; + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 555d6e919c35 src/storage_backend_logical.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_logical.h Tue Feb 19 17:31:03 2008 -0500 @@ -0,0 +1,45 @@ +/* + * storage_backend_logical.h: storage backend for logical volume handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_BACKEND_LOGICAL_H__ +#define __VIR_STORAGE_BACKEND_LOGICAL_H__ + +#include "storage_backend.h" + +extern virStorageBackend virStorageBackendLogical; + +#endif /* __VIR_STORAGE_BACKEND_LOGVOL_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This provides a storage pool using the iSCSI protocol. Since there is no API for iSCSI it is implemented by simply shelling out to the iscsiadm command line tool. A pool corresponds to a single target on the iSCSI server. Starting a pool logs into the server and maps the target's LUNs into the local filesystem. The default nodes are under /dev, allocated-on-demand and thus not guarenteed to be stable across reboots. For this reason it is recommended by the pool target path be configured to point to /dev/disk/by-path or /dev/disk/by-id whose entries are guarenteed stable for lifetime of the target+LUN. The 'refresh' operation will rescan the target for new LUNs and purge old LUNs allowing dynamic updates without needing a pool restart. b/src/storage_backend_iscsi.c | 424 ++++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_iscsi.h | 45 ++++ configure.in | 22 ++ libvirt.spec.in | 4 src/Makefile.am | 6 src/storage_backend.c | 15 + 6 files changed, 516 insertions(+) diff -r 9a3200af0a3d configure.in --- a/configure.in Thu Feb 07 11:14:56 2008 -0500 +++ b/configure.in Thu Feb 07 11:34:46 2008 -0500 @@ -561,6 +561,8 @@ AC_ARG_WITH(storage-fs, [ --with-storage-fs with FileSystem backend for the storage driver (on)],[],[with_storage_fs=check]) AC_ARG_WITH(storage-lvm, [ --with-storage-lvm with LVM backend for the storage driver (on)],[],[with_storage_lvm=check]) +AC_ARG_WITH(storage-iscsi, +[ --with-storage-iscsi with iSCSI backend for the storage driver (on)],[],[with_storage_iscsi=check]) if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin]) @@ -653,6 +655,25 @@ if test "$with_storage_lvm" = "yes" -o " fi fi AM_CONDITIONAL(WITH_STORAGE_LVM, [test "$with_storage_lvm" = "yes"]) + + + +if test "$with_storage_iscsi" = "yes" -o "$with_storage_iscsi" = "check"; then + AC_PATH_PROG(ISCSIADM, [iscsiadm], [], [$PATH:/sbin:/usr/sbin]) + if test "$with_storage_iscsi" = "yes" ; then + if test -z "$ISCSIADM" ; then AC_MSG_ERROR(We need iscsiadm for iSCSI storage driver) ; fi + else + if test -z "$ISCSIADM" ; then with_storage_iscsi=no ; fi + + if test "$with_storage_iscsi" = "check" ; then with_storage_iscsi=yes ; fi + fi + + if test "$with_storage_iscsi" = "yes" ; then + AC_DEFINE_UNQUOTED(WITH_STORAGE_ISCSI, 1, [whether iSCSI backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([ISCSIADM],["$ISCSIADM"],[Location of iscsiadm program]) + fi +fi +AM_CONDITIONAL(WITH_STORAGE_ISCSI, [test "$with_storage_iscsi" = "yes"]) dnl @@ -869,6 +890,7 @@ AC_MSG_NOTICE([ FS: $with_storage_f AC_MSG_NOTICE([ FS: $with_storage_fs]) AC_MSG_NOTICE([ NetFS: $with_storage_fs]) AC_MSG_NOTICE([ LVM: $with_storage_lvm]) +AC_MSG_NOTICE([ iSCSI: $with_storage_iscsi]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) diff -r 9a3200af0a3d libvirt.spec.in --- a/libvirt.spec.in Thu Feb 07 11:14:56 2008 -0500 +++ b/libvirt.spec.in Thu Feb 07 11:34:46 2008 -0500 @@ -51,6 +51,8 @@ Requires: /usr/sbin/qcow-create %endif # For LVM drivers Requires: lvm2 +# For ISCSI driver +Requires: iscsi-initiator-utils BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -77,6 +79,8 @@ BuildRequires: /usr/sbin/qcow-create %endif # For LVM drivers BuildRequires: lvm2 +# For ISCSI driver +BuildRequires: iscsi-initiator-utils Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 diff -r 9a3200af0a3d src/Makefile.am --- a/src/Makefile.am Thu Feb 07 11:14:56 2008 -0500 +++ b/src/Makefile.am Thu Feb 07 11:34:46 2008 -0500 @@ -75,6 +75,12 @@ EXTRA_DIST += storage_backend_logical.h EXTRA_DIST += storage_backend_logical.h storage_backend_logical.c endif +if WITH_STORAGE_ISCSI +CLIENT_SOURCES += storage_backend_iscsi.h storage_backend_iscsi.c +else +EXTRA_DIST += storage_backend_iscsi.h storage_backend_iscsi.c +endif + libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) diff -r 9a3200af0a3d src/storage_backend.c --- a/src/storage_backend.c Thu Feb 07 11:14:56 2008 -0500 +++ b/src/storage_backend.c Thu Feb 07 11:34:46 2008 -0500 @@ -39,6 +39,10 @@ #if WITH_STORAGE_LVM #include "storage_backend_logical.h" #endif +#if WITH_STORAGE_ISCSI +#include "storage_backend_iscsi.h" +#endif + #include "util.h" @@ -53,6 +57,9 @@ static virStorageBackendPtr backends[] = #endif #if WITH_STORAGE_LVM &virStorageBackendLogical, +#endif +#if WITH_STORAGE_ISCSI + &virStorageBackendISCSI, #endif }; @@ -94,6 +101,10 @@ int virStorageBackendFromString(const ch #if WITH_STORAGE_LVM if (STREQ(type, "logical")) return VIR_STORAGE_POOL_LOGICAL; +#endif +#if WITH_STORAGE_ISCSI + if (STREQ(type, "iscsi")) + return VIR_STORAGE_POOL_ISCSI; #endif virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %s", type); return -1; @@ -112,6 +123,10 @@ const char *virStorageBackendToString(in #if WITH_STORAGE_LVM case VIR_STORAGE_POOL_LOGICAL: return "logical"; +#endif +#if WITH_STORAGE_ISCSI + case VIR_STORAGE_POOL_ISCSI: + return "iscsi"; #endif } diff -r 9a3200af0a3d src/storage_backend_iscsi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_iscsi.c Thu Feb 07 11:34:46 2008 -0500 @@ -0,0 +1,424 @@ +/* + * storage_backend_iscsi.c: storage backend for iSCSI handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <sys/socket.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <string.h> +#include <stdio.h> +#include <regex.h> +#include <fcntl.h> +#include <unistd.h> +#include <dirent.h> + +#include "storage_backend_iscsi.h" +#include "util.h" + +static int virStorageBackendISCSITargetIP(virConnectPtr conn, + const char *hostname, + char *ipaddr, + size_t ipaddrlen) +{ + struct addrinfo hints; + struct addrinfo *result = NULL; + int ret; + + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + ret = getaddrinfo(hostname, NULL, &hints, &result); + if (ret != 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "getaddrinfo %s", + gai_strerror(ret)); + return -1; + } + + if (result == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "no IP address for target %s", hostname); + return -1; + } + + if (getnameinfo(result->ai_addr, result->ai_addrlen, + ipaddr, ipaddrlen, NULL, 0, + NI_NUMERICHOST) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot format ip addr for %s", hostname); + freeaddrinfo(result); + return -1; + } + + freeaddrinfo(result); + return 0; +} + +static int virStorageBackendISCSIExtractSession(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + void *data) +{ + char **session = data; + + if (STREQ(groups[1], pool->def->source.devices[0].path)) { + if ((*session = strdup(groups[0])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "session"); + return -1; + } + } + + return 0; +} + +static char *virStorageBackendISCSISession(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + /* + * # iscsiadm --mode session -P 0 + * tcp: [1] 192.168.122.170:3260,1 demo-tgt-b + * tcp: [2] 192.168.122.170:3260,1 demo-tgt-a + * + * Pull out 2nd and 4th fields + */ + const char *regexes[] = { + "^tcp:\\s+\\[(\\S+)\\]\\s+\\S+\\s+(\\S+)\\s*$" + }; + int vars[] = { + 2, + }; + const char *prog[] = { + ISCSIADM, "--mode", "session", "-P", "0", NULL + }; + char *session = NULL; + + if (virStorageBackendRunProgRegex(conn, pool, + prog, + 1, + regexes, + vars, + virStorageBackendISCSIExtractSession, + &session) < 0) + return NULL; + + if (session == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot find session"); + return NULL; + } + + return session; +} + +static int virStorageBackendISCSIConnection(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *portal, + const char *action) +{ + const char *cmdargv[] = { + ISCSIADM, "--mode", "node", "--portal", portal, + "--targetname", pool->def->source.devices[0].path, action, NULL + }; + + if (virRun(conn, (char **)cmdargv, NULL) < 0) + return -1; + + return 0; +} + + +static int virStorageBackendISCSIMakeLUN(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + void *data ATTRIBUTE_UNUSED) +{ + virStorageVolDefPtr vol; + int fd = -1; + char scsiid[100]; + char *dev = groups[4]; + int opentries = 0; + char *devpath = NULL; + + snprintf(scsiid, sizeof(scsiid)-1, "%s:%s:%s:%s", + groups[0], groups[1], groups[2], groups[3]); + + if ((vol = calloc(1, sizeof(virStorageVolDef))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + return -1; + } + + if ((vol->name = strdup(scsiid)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "name"); + goto cleanup; + } + + if ((devpath = malloc(5 + strlen(dev) + 1)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "devpath"); + goto cleanup; + } + strcpy(devpath, "/dev/"); + strcat(devpath, dev); + /* It can take a little while between logging into the ISCSI + * server and udev creating the /dev nodes, so if we get ENOENT + * we must retry a few times - they should eventually appear. + * We currently wait for upto 5 seconds. Is this good enough ? + * Perhaps not on a very heavily loaded system Any other + * options... ? + */ + reopen: + if ((fd = open(devpath, O_RDONLY)) < 0) { + opentries++; + if (errno == ENOENT && opentries < 50) { + usleep(100 * 1000); + goto reopen; + } + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot open %s: %s (%d)", + devpath, strerror(errno), errno); + goto cleanup; + } + + /* Now figure out the stable path + * + * XXX this method is O(N) because it scans the pool target + * dir every time its run. Should figure out a more efficient + * way of doing this... + */ + if ((vol->target.path = virStorageBackendStablePath(conn, pool, devpath)) == NULL) + goto cleanup; + + if (devpath != vol->target.path) + free(devpath); + devpath = NULL; + + if (virStorageBackendUpdateVolInfoFD(conn, vol, fd, 1) < 0) + goto cleanup; + + /* XXX use unique iSCSI id instead */ + vol->key = strdup(vol->target.path); + if (vol->key == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "key"); + goto cleanup; + } + + + pool->def->capacity += vol->capacity; + pool->def->allocation += vol->allocation; + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + + close(fd); + + return 0; + + cleanup: + if (fd != -1) close(fd); + free(devpath); + virStorageVolDefFree(vol); + return -1; +} + +static int virStorageBackendISCSIFindLUNs(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *session) +{ + /* + * # iscsiadm --mode session -r $session -P 3 + * + * scsi1 Channel 00 Id 0 Lun: 0 + * scsi1 Channel 00 Id 0 Lun: 1 + * Attached scsi disk sdc State: running + * scsi1 Channel 00 Id 0 Lun: 2 + * Attached scsi disk sdd State: running + * scsi1 Channel 00 Id 0 Lun: 3 + * Attached scsi disk sde State: running + * scsi1 Channel 00 Id 0 Lun: 4 + * Attached scsi disk sdf State: running + * scsi1 Channel 00 Id 0 Lun: 5 + * Attached scsi disk sdg State: running + * + * Need 2 regex to match alternating lines + */ + const char *regexes[] = { + "^\\s*scsi(\\S+)\\s+Channel\\s+(\\S+)\\s+Id\\s+(\\S+)\\s+Lun:\\s+(\\S+)\\s*$", + "^\\s*Attached\\s+scsi\\s+disk\\s+(\\S+)\\s+State:\\s+running\\s*$" + }; + int vars[] = { + 4, 1 + }; + const char *prog[] = { + ISCSIADM, "--mode", "session", "-r", session, "-P", "3", NULL, + }; + + return virStorageBackendRunProgRegex(conn, pool, + prog, + 2, + regexes, + vars, + virStorageBackendISCSIMakeLUN, + NULL); +} + + +static int virStorageBackendISCSILogin(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *portal) +{ + return virStorageBackendISCSIConnection(conn, pool, portal, "--login"); +} + +static int virStorageBackendISCSILogout(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *portal) +{ + return virStorageBackendISCSIConnection(conn, pool, portal, "--logout"); +} + +static char *virStorageBackendISCSIPortal(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char ipaddr[NI_MAXHOST]; + char *portal; + + if (virStorageBackendISCSITargetIP(conn, + pool->def->source.host.name, + ipaddr, sizeof(ipaddr)) < 0) + return NULL; + + portal = malloc(strlen(ipaddr) + 1 + 4 + 2 + 1); + if (portal == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "portal"); + return NULL; + } + + strcpy(portal, ipaddr); + strcat(portal, ":3260,1"); + + return portal; +} + + +static int virStorageBackendISCSIStartPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char *portal = NULL; + + if (pool->def->source.host.name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source host"); + return -1; + } + + if (pool->def->source.ndevice != 1 || + pool->def->source.devices[0].path == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source device"); + return -1; + } + + if ((portal = virStorageBackendISCSIPortal(conn, pool)) == NULL) + return -1; + if (virStorageBackendISCSILogin(conn, pool, portal) < 0) { + free(portal); + return -1; + } + free(portal); + return 0; +} + +static int virStorageBackendISCSIRefreshPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char *portal = NULL, *session = NULL; + + pool->def->allocation = pool->def->capacity = pool->def->available = 0; + + if ((portal = virStorageBackendISCSIPortal(conn, pool)) == NULL) + return -1; + + if ((session = virStorageBackendISCSISession(conn, pool)) == NULL) { + goto cleanup; + } + if (virStorageBackendISCSIFindLUNs(conn, pool, session) < 0) + goto cleanup; + free(portal); + free(session); + + return 0; + + cleanup: + free(portal); + free(session); + return -1; +} + + +static int virStorageBackendISCSIStopPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char *portal; + + if ((portal = virStorageBackendISCSIPortal(conn, pool)) == NULL) + return -1; + + if (virStorageBackendISCSILogout(conn, pool, portal) < 0) { + free(portal); + return -1; + } + free(portal); + + return 0; +} + + +virStorageBackend virStorageBackendISCSI = { + .type = VIR_STORAGE_POOL_ISCSI, + + .startPool = virStorageBackendISCSIStartPool, + .refreshPool = virStorageBackendISCSIRefreshPool, + .stopPool = virStorageBackendISCSIStopPool, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_HOST | + VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) + }, + + .volType = VIR_STORAGE_VOL_BLOCK, +}; + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 9a3200af0a3d src/storage_backend_iscsi.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_iscsi.h Thu Feb 07 11:34:46 2008 -0500 @@ -0,0 +1,45 @@ +/* + * storage_backend_iscsi.h: storage backend for iSCSI handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_BACKEND_ISCSI_H__ +#define __VIR_STORAGE_BACKEND_ISCSI_H__ + +#include "storage_backend.h" + +extern virStorageBackend virStorageBackendISCSI; + +#endif /* __VIR_STORAGE_BACKEND_ISCSI_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

A couple of comments about this, inline. <snip>
+static int virStorageBackendISCSIConnection(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *portal, + const char *action) +{ + const char *cmdargv[] = { + ISCSIADM, "--mode", "node", "--portal", portal, + "--targetname", pool->def->source.devices[0].path, action, NULL + }; + + if (virRun(conn, (char **)cmdargv, NULL) < 0) + return -1; + + return 0; +}
Dan and I discussed this on IRC already, but the above code won't work as-is. The way iscsiadm works is that you first scan the target with an "iscsiadm --mode discovery --type sendtargets --portal <ip>". This command actually creates a directory structure which later iscsiadm commands (including --login) use. So you basically have to do the sendtargets, even if you already know the name of the target you want to connect to. It's just a bugfix, however, and shouldn't prevent this from going in. <snip>
+static char *virStorageBackendISCSIPortal(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char ipaddr[NI_MAXHOST]; + char *portal; + + if (virStorageBackendISCSITargetIP(conn, + pool->def->source.host.name, + ipaddr, sizeof(ipaddr)) < 0) + return NULL; + + portal = malloc(strlen(ipaddr) + 1 + 4 + 2 + 1); + if (portal == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "portal"); + return NULL; + } + + strcpy(portal, ipaddr); + strcat(portal, ":3260,1"); + + return portal; +}
We probably shouldn't hardcode the 3260 (port number). I have no idea how common it is to run iSCSI servers on other ports, but I have to imagine it is possible, so we should probably allow it. Again, though, this seems like it would just be an extension in the XML that we would use (adding a port="" attribute), so not something that would block this going in.
+ + +static int virStorageBackendISCSIStartPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char *portal = NULL; + + if (pool->def->source.host.name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source host"); + return -1; + } + + if (pool->def->source.ndevice != 1 || + pool->def->source.devices[0].path == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source device"); + return -1; + } + + if ((portal = virStorageBackendISCSIPortal(conn, pool)) == NULL) + return -1; + if (virStorageBackendISCSILogin(conn, pool, portal) < 0) { + free(portal); + return -1; + } + free(portal); + return 0; +}
One general comment I have here; it doesn't seem it is possible to specify an iSCSI server without a target name, which is a little unfortunate. What would be really nice would be able to give an IP address + port number to this driver, and then have it automatically scan all targets (via the sendtargets discussed above) and either return that list to the user, or store it internally for later use. I have no idea how this would fit in with the current API, however. <snip>
+static int virStorageBackendISCSIStopPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char *portal; + + if ((portal = virStorageBackendISCSIPortal(conn, pool)) == NULL) + return -1; + + if (virStorageBackendISCSILogout(conn, pool, portal) < 0) { + free(portal); + return -1; + } + free(portal); + + return 0; +}
One note for testers; current 2.6.23 and 2.6.24 kernels have a bug where iscsiadm will hang on --logout. This should be fixed in 2.6.25, but it will cause problems until then when stopping a pool. Chris Lalancette

On Tue, Feb 12, 2008 at 08:37:02AM -0500, Chris Lalancette wrote:
+static char *virStorageBackendISCSIPortal(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char ipaddr[NI_MAXHOST]; + char *portal; + + if (virStorageBackendISCSITargetIP(conn, + pool->def->source.host.name, + ipaddr, sizeof(ipaddr)) < 0) + return NULL; + + portal = malloc(strlen(ipaddr) + 1 + 4 + 2 + 1); + if (portal == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "portal"); + return NULL; + } + + strcpy(portal, ipaddr); + strcat(portal, ":3260,1"); + + return portal; +}
We probably shouldn't hardcode the 3260 (port number). I have no idea how common it is to run iSCSI servers on other ports, but I have to imagine it is possible, so we should probably allow it. Again, though, this seems like it would just be an extension in the XML that we would use (adding a port="" attribute), so not something that would block this going in.
The internal storage_conf.h file does actually track the port number, but I need to add the XML parser code to extract it & then hook it up here. It should not be hard - just on my TODO list.
+static int virStorageBackendISCSIStartPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char *portal = NULL; + + if (pool->def->source.host.name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source host"); + return -1; + } + + if (pool->def->source.ndevice != 1 || + pool->def->source.devices[0].path == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source device"); + return -1; + } + + if ((portal = virStorageBackendISCSIPortal(conn, pool)) == NULL) + return -1; + if (virStorageBackendISCSILogin(conn, pool, portal) < 0) { + free(portal); + return -1; + } + free(portal); + return 0; +}
One general comment I have here; it doesn't seem it is possible to specify an iSCSI server without a target name, which is a little unfortunate. What would be really nice would be able to give an IP address + port number to this driver, and then have it automatically scan all targets (via the sendtargets discussed above) and either return that list to the user, or store it internally for later use. I have no idea how this would fit in with the current API, however.
That's also a feature I intend to add. The current system lets you define storage pools from a config you have prepared. There is also a method virConnectDiscoverStoragePools whose purpose is for autodiscovery. For iSCSI it would query for all exported targets, and give you back a list of XML docs representing a suitable defautl pool config for each target. For NFS it would query for all exported shares. For LVM it would lookup all defined volume groups. For disks, it would return configs for all local disks, etc etc.
<snip>
+static int virStorageBackendISCSIStopPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char *portal; + + if ((portal = virStorageBackendISCSIPortal(conn, pool)) == NULL) + return -1; + + if (virStorageBackendISCSILogout(conn, pool, portal) < 0) { + free(portal); + return -1; + } + free(portal); + + return 0; +}
One note for testers; current 2.6.23 and 2.6.24 kernels have a bug where iscsiadm will hang on --logout. This should be fixed in 2.6.25, but it will cause problems until then when stopping a pool.
It'll probably fail on 2.6.21 and 2.6.22 too. I've been testing iSCSI on RHEL-5 with 2.6.18 primarily. The fixes will be in 2.6.25 Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:39:25AM +0000, Daniel P. Berrange wrote:
This provides a storage pool using the iSCSI protocol. Since there is no API for iSCSI it is implemented by simply shelling out to the iscsiadm command line tool. A pool corresponds to a single target on the iSCSI server. Starting a pool logs into the server and maps the target's LUNs into the local filesystem. The default nodes are under /dev, allocated-on-demand and thus not guarenteed to be stable across reboots. For this reason it is recommended by the pool target path be configured to point to /dev/disk/by-path or /dev/disk/by-id whose entries are guarenteed stable for lifetime of the target+LUN. The 'refresh' operation will rescan the target for new LUNs and purge old LUNs allowing dynamic updates without needing a pool restart.
Okay, the portability of this code will be fun, but that makes sense as is,
diff -r 9a3200af0a3d libvirt.spec.in --- a/libvirt.spec.in Thu Feb 07 11:14:56 2008 -0500 +++ b/libvirt.spec.in Thu Feb 07 11:34:46 2008 -0500 @@ -51,6 +51,8 @@ Requires: /usr/sbin/qcow-create %endif # For LVM drivers Requires: lvm2 +# For ISCSI driver +Requires: iscsi-initiator-utils
Apparently iscsi-initiator-utils exists in RHEL 4 (and 3 but we are not tergetting that old) it would be great if that worked there too. [...]
+static int virStorageBackendISCSIMakeLUN(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + void *data ATTRIBUTE_UNUSED) [...] + if ((devpath = malloc(5 + strlen(dev) + 1)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "devpath"); + goto cleanup; + } + strcpy(devpath, "/dev/"); + strcat(devpath, dev);
each time i see strcat() I get afraid, a good old snprintf for those 2 is probably easier to understand. Looks fine to me, i never played with iSCSI, so no real insight on the questions left in the code. Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Tue, 2008-02-19 at 03:24 -0500, Daniel Veillard wrote:
+# For ISCSI driver +Requires: iscsi-initiator-utils
Apparently iscsi-initiator-utils exists in RHEL 4 (and 3 but we are not tergetting that old) it would be great if that worked there too.
I can't remember when exactly, but the command line syntax of these tools changed quite significantly at some point. Cheeers, Mark.

On Tue, Feb 19, 2008 at 08:45:03AM +0000, Mark McLoughlin wrote:
On Tue, 2008-02-19 at 03:24 -0500, Daniel Veillard wrote:
+# For ISCSI driver +Requires: iscsi-initiator-utils
Apparently iscsi-initiator-utils exists in RHEL 4 (and 3 but we are not tergetting that old) it would be great if that worked there too.
I can't remember when exactly, but the command line syntax of these tools changed quite significantly at some point.
Yes, I've had a few people mention that. My testing has been on RHEL5 and Fedora 8, so it at least works there. We can incrementally fix to work on other places if people report problems. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:39:25AM +0000, Daniel P. Berrange wrote:
This provides a storage pool using the iSCSI protocol. Since there is no API for iSCSI it is implemented by simply shelling out to the iscsiadm command line tool. A pool corresponds to a single target on the iSCSI server. Starting a pool logs into the server and maps the target's LUNs into the local filesystem. The default nodes are under /dev, allocated-on-demand and thus not guarenteed to be stable across reboots. For this reason it is recommended by the pool target path be configured to point to /dev/disk/by-path or /dev/disk/by-id whose entries are guarenteed stable for lifetime of the target+LUN. The 'refresh' operation will rescan the target for new LUNs and purge old LUNs allowing dynamic updates without needing a pool restart.
b/src/storage_backend_iscsi.c | 455 ++++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_iscsi.h | 45 ++++ configure.in | 22 ++ libvirt.spec.in | 4 po/POTFILES.in | 1 src/Makefile.am | 6 src/storage_backend.c | 15 + 7 files changed, 548 insertions(+) diff -r ee74be65111c configure.in --- a/configure.in Tue Feb 19 17:31:04 2008 -0500 +++ b/configure.in Tue Feb 19 17:38:39 2008 -0500 @@ -561,6 +561,8 @@ AC_ARG_WITH(storage-fs, [ --with-storage-fs with FileSystem backend for the storage driver (on)],[],[with_storage_fs=check]) AC_ARG_WITH(storage-lvm, [ --with-storage-lvm with LVM backend for the storage driver (on)],[],[with_storage_lvm=check]) +AC_ARG_WITH(storage-iscsi, +[ --with-storage-iscsi with iSCSI backend for the storage driver (on)],[],[with_storage_iscsi=check]) if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin]) @@ -653,6 +655,25 @@ if test "$with_storage_lvm" = "yes" -o " fi fi AM_CONDITIONAL(WITH_STORAGE_LVM, [test "$with_storage_lvm" = "yes"]) + + + +if test "$with_storage_iscsi" = "yes" -o "$with_storage_iscsi" = "check"; then + AC_PATH_PROG(ISCSIADM, [iscsiadm], [], [$PATH:/sbin:/usr/sbin]) + if test "$with_storage_iscsi" = "yes" ; then + if test -z "$ISCSIADM" ; then AC_MSG_ERROR(We need iscsiadm for iSCSI storage driver) ; fi + else + if test -z "$ISCSIADM" ; then with_storage_iscsi=no ; fi + + if test "$with_storage_iscsi" = "check" ; then with_storage_iscsi=yes ; fi + fi + + if test "$with_storage_iscsi" = "yes" ; then + AC_DEFINE_UNQUOTED(WITH_STORAGE_ISCSI, 1, [whether iSCSI backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([ISCSIADM],["$ISCSIADM"],[Location of iscsiadm program]) + fi +fi +AM_CONDITIONAL(WITH_STORAGE_ISCSI, [test "$with_storage_iscsi" = "yes"]) dnl @@ -869,6 +890,7 @@ AC_MSG_NOTICE([ FS: $with_storage_f AC_MSG_NOTICE([ FS: $with_storage_fs]) AC_MSG_NOTICE([ NetFS: $with_storage_fs]) AC_MSG_NOTICE([ LVM: $with_storage_lvm]) +AC_MSG_NOTICE([ iSCSI: $with_storage_iscsi]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) diff -r ee74be65111c libvirt.spec.in --- a/libvirt.spec.in Tue Feb 19 17:31:04 2008 -0500 +++ b/libvirt.spec.in Tue Feb 19 17:38:39 2008 -0500 @@ -51,6 +51,8 @@ Requires: /usr/sbin/qcow-create %endif # For LVM drivers Requires: lvm2 +# For ISCSI driver +Requires: iscsi-initiator-utils BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -77,6 +79,8 @@ BuildRequires: /usr/sbin/qcow-create %endif # For LVM drivers BuildRequires: lvm2 +# For ISCSI driver +BuildRequires: iscsi-initiator-utils Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 diff -r ee74be65111c po/POTFILES.in --- a/po/POTFILES.in Tue Feb 19 17:31:04 2008 -0500 +++ b/po/POTFILES.in Tue Feb 19 17:38:39 2008 -0500 @@ -13,6 +13,7 @@ src/storage_backend.c src/storage_backend.c src/storage_backend_fs.c src/storage_backend_logical.c +src/storage_backend_iscsi.c src/storage_conf.c src/storage_driver.c src/sexpr.c diff -r ee74be65111c src/Makefile.am --- a/src/Makefile.am Tue Feb 19 17:31:04 2008 -0500 +++ b/src/Makefile.am Tue Feb 19 17:38:39 2008 -0500 @@ -74,6 +74,12 @@ EXTRA_DIST += storage_backend_logical.h EXTRA_DIST += storage_backend_logical.h storage_backend_logical.c endif +if WITH_STORAGE_ISCSI +CLIENT_SOURCES += storage_backend_iscsi.h storage_backend_iscsi.c +else +EXTRA_DIST += storage_backend_iscsi.h storage_backend_iscsi.c +endif + libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) diff -r ee74be65111c src/storage_backend.c --- a/src/storage_backend.c Tue Feb 19 17:31:04 2008 -0500 +++ b/src/storage_backend.c Tue Feb 19 17:38:39 2008 -0500 @@ -39,6 +39,10 @@ #if WITH_STORAGE_LVM #include "storage_backend_logical.h" #endif +#if WITH_STORAGE_ISCSI +#include "storage_backend_iscsi.h" +#endif + #include "util.h" @@ -53,6 +57,9 @@ static virStorageBackendPtr backends[] = #endif #if WITH_STORAGE_LVM &virStorageBackendLogical, +#endif +#if WITH_STORAGE_ISCSI + &virStorageBackendISCSI, #endif }; @@ -99,6 +106,10 @@ virStorageBackendFromString(const char * #if WITH_STORAGE_LVM if (STREQ(type, "logical")) return VIR_STORAGE_POOL_LOGICAL; +#endif +#if WITH_STORAGE_ISCSI + if (STREQ(type, "iscsi")) + return VIR_STORAGE_POOL_ISCSI; #endif virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, @@ -120,6 +131,10 @@ virStorageBackendToString(int type) { #if WITH_STORAGE_LVM case VIR_STORAGE_POOL_LOGICAL: return "logical"; +#endif +#if WITH_STORAGE_ISCSI + case VIR_STORAGE_POOL_ISCSI: + return "iscsi"; #endif } diff -r ee74be65111c src/storage_backend_iscsi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_iscsi.c Tue Feb 19 17:38:39 2008 -0500 @@ -0,0 +1,455 @@ +/* + * storage_backend_iscsi.c: storage backend for iSCSI handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <sys/socket.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <string.h> +#include <stdio.h> +#include <regex.h> +#include <fcntl.h> +#include <unistd.h> +#include <dirent.h> + +#include "storage_backend_iscsi.h" +#include "util.h" + +static int +virStorageBackendISCSITargetIP(virConnectPtr conn, + const char *hostname, + char *ipaddr, + size_t ipaddrlen) +{ + struct addrinfo hints; + struct addrinfo *result = NULL; + int ret; + + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + ret = getaddrinfo(hostname, NULL, &hints, &result); + if (ret != 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("host lookup failed %s"), + gai_strerror(ret)); + return -1; + } + + if (result == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("no IP address for target %s"), + hostname); + return -1; + } + + if (getnameinfo(result->ai_addr, result->ai_addrlen, + ipaddr, ipaddrlen, NULL, 0, + NI_NUMERICHOST) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot format ip addr for %s"), + hostname); + freeaddrinfo(result); + return -1; + } + + freeaddrinfo(result); + return 0; +} + +static int +virStorageBackendISCSIExtractSession(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + void *data) +{ + char **session = data; + + if (STREQ(groups[1], pool->def->source.devices[0].path)) { + if ((*session = strdup(groups[0])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("session")); + return -1; + } + } + + return 0; +} + +static char * +virStorageBackendISCSISession(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + /* + * # iscsiadm --mode session -P 0 + * tcp: [1] 192.168.122.170:3260,1 demo-tgt-b + * tcp: [2] 192.168.122.170:3260,1 demo-tgt-a + * + * Pull out 2nd and 4th fields + */ + const char *regexes[] = { + "^tcp:\\s+\\[(\\S+)\\]\\s+\\S+\\s+(\\S+)\\s*$" + }; + int vars[] = { + 2, + }; + const char *prog[] = { + ISCSIADM, "--mode", "session", "-P", "0", NULL + }; + char *session = NULL; + + if (virStorageBackendRunProgRegex(conn, pool, + prog, + 1, + regexes, + vars, + virStorageBackendISCSIExtractSession, + &session) < 0) + return NULL; + + if (session == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot find session")); + return NULL; + } + + return session; +} + +static int +virStorageBackendISCSIConnection(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *portal, + const char *action) +{ + const char *cmdargv[] = { + ISCSIADM, "--mode", "node", "--portal", portal, + "--targetname", pool->def->source.devices[0].path, action, NULL + }; + + if (virRun(conn, (char **)cmdargv, NULL) < 0) + return -1; + + return 0; +} + + +static int +virStorageBackendISCSIMakeLUN(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + void *data ATTRIBUTE_UNUSED) +{ + virStorageVolDefPtr vol; + int fd = -1; + char lunid[100]; + char *dev = groups[4]; + int opentries = 0; + char *devpath = NULL; + + snprintf(lunid, sizeof(lunid)-1, "lun-%s", groups[3]); + + if ((vol = calloc(1, sizeof(virStorageVolDef))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + + if ((vol->name = strdup(lunid)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("name")); + goto cleanup; + } + + if ((devpath = malloc(5 + strlen(dev) + 1)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("devpath")); + goto cleanup; + } + strcpy(devpath, "/dev/"); + strcat(devpath, dev); + /* It can take a little while between logging into the ISCSI + * server and udev creating the /dev nodes, so if we get ENOENT + * we must retry a few times - they should eventually appear. + * We currently wait for upto 5 seconds. Is this good enough ? + * Perhaps not on a very heavily loaded system Any other + * options... ? + */ + reopen: + if ((fd = open(devpath, O_RDONLY)) < 0) { + opentries++; + if (errno == ENOENT && opentries < 50) { + usleep(100 * 1000); + goto reopen; + } + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot open %s: %s"), + devpath, strerror(errno)); + goto cleanup; + } + + /* Now figure out the stable path + * + * XXX this method is O(N) because it scans the pool target + * dir every time its run. Should figure out a more efficient + * way of doing this... + */ + if ((vol->target.path = virStorageBackendStablePath(conn, + pool, + devpath)) == NULL) + goto cleanup; + + if (devpath != vol->target.path) + free(devpath); + devpath = NULL; + + if (virStorageBackendUpdateVolInfoFD(conn, vol, fd, 1) < 0) + goto cleanup; + + /* XXX use unique iSCSI id instead */ + vol->key = strdup(vol->target.path); + if (vol->key == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("key")); + goto cleanup; + } + + + pool->def->capacity += vol->capacity; + pool->def->allocation += vol->allocation; + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + + close(fd); + + return 0; + + cleanup: + if (fd != -1) close(fd); + free(devpath); + virStorageVolDefFree(vol); + return -1; +} + +static int +virStorageBackendISCSIFindLUNs(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *session) +{ + /* + * # iscsiadm --mode session -r $session -P 3 + * + * scsi1 Channel 00 Id 0 Lun: 0 + * scsi1 Channel 00 Id 0 Lun: 1 + * Attached scsi disk sdc State: running + * scsi1 Channel 00 Id 0 Lun: 2 + * Attached scsi disk sdd State: running + * scsi1 Channel 00 Id 0 Lun: 3 + * Attached scsi disk sde State: running + * scsi1 Channel 00 Id 0 Lun: 4 + * Attached scsi disk sdf State: running + * scsi1 Channel 00 Id 0 Lun: 5 + * Attached scsi disk sdg State: running + * + * Need 2 regex to match alternating lines + */ + const char *regexes[] = { + "^\\s*scsi(\\S+)\\s+Channel\\s+(\\S+)\\s+Id\\s+(\\S+)\\s+Lun:\\s+(\\S+)\\s*$", + "^\\s*Attached\\s+scsi\\s+disk\\s+(\\S+)\\s+State:\\s+running\\s*$" + }; + int vars[] = { + 4, 1 + }; + const char *prog[] = { + ISCSIADM, "--mode", "session", "-r", session, "-P", "3", NULL, + }; + + return virStorageBackendRunProgRegex(conn, pool, + prog, + 2, + regexes, + vars, + virStorageBackendISCSIMakeLUN, + NULL); +} + + +static int +virStorageBackendISCSIRescanLUNs(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + const char *session) +{ + const char *cmdargv[] = { + ISCSIADM, "--mode", "session", "-r", session, "-R", NULL, + }; + + if (virRun(conn, (char **)cmdargv, NULL) < 0) + return -1; + + return 0; +} + + +static int +virStorageBackendISCSILogin(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *portal) +{ + return virStorageBackendISCSIConnection(conn, pool, portal, "--login"); +} + +static int +virStorageBackendISCSILogout(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *portal) +{ + return virStorageBackendISCSIConnection(conn, pool, portal, "--logout"); +} + +static char * +virStorageBackendISCSIPortal(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char ipaddr[NI_MAXHOST]; + char *portal; + + if (virStorageBackendISCSITargetIP(conn, + pool->def->source.host.name, + ipaddr, sizeof(ipaddr)) < 0) + return NULL; + + portal = malloc(strlen(ipaddr) + 1 + 4 + 2 + 1); + if (portal == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("portal")); + return NULL; + } + + strcpy(portal, ipaddr); + strcat(portal, ":3260,1"); + + return portal; +} + + +static int +virStorageBackendISCSIStartPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char *portal = NULL; + + if (pool->def->source.host.name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("missing source host")); + return -1; + } + + if (pool->def->source.ndevice != 1 || + pool->def->source.devices[0].path == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("missing source device")); + return -1; + } + + if ((portal = virStorageBackendISCSIPortal(conn, pool)) == NULL) + return -1; + if (virStorageBackendISCSILogin(conn, pool, portal) < 0) { + free(portal); + return -1; + } + free(portal); + return 0; +} + +static int +virStorageBackendISCSIRefreshPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char *session = NULL; + + pool->def->allocation = pool->def->capacity = pool->def->available = 0; + + if ((session = virStorageBackendISCSISession(conn, pool)) == NULL) + goto cleanup; + if (virStorageBackendISCSIRescanLUNs(conn, pool, session) < 0) + goto cleanup; + if (virStorageBackendISCSIFindLUNs(conn, pool, session) < 0) + goto cleanup; + free(session); + + return 0; + + cleanup: + free(session); + return -1; +} + + +static int +virStorageBackendISCSIStopPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char *portal; + + if ((portal = virStorageBackendISCSIPortal(conn, pool)) == NULL) + return -1; + + if (virStorageBackendISCSILogout(conn, pool, portal) < 0) { + free(portal); + return -1; + } + free(portal); + + return 0; +} + + +virStorageBackend virStorageBackendISCSI = { + .type = VIR_STORAGE_POOL_ISCSI, + + .startPool = virStorageBackendISCSIStartPool, + .refreshPool = virStorageBackendISCSIRefreshPool, + .stopPool = virStorageBackendISCSIStopPool, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_HOST | + VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) + }, + + .volType = VIR_STORAGE_VOL_BLOCK, +}; + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r ee74be65111c src/storage_backend_iscsi.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_iscsi.h Tue Feb 19 17:38:39 2008 -0500 @@ -0,0 +1,45 @@ +/* + * storage_backend_iscsi.h: storage backend for iSCSI handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_BACKEND_ISCSI_H__ +#define __VIR_STORAGE_BACKEND_ISCSI_H__ + +#include "storage_backend.h" + +extern virStorageBackend virStorageBackendISCSI; + +#endif /* __VIR_STORAGE_BACKEND_ISCSI_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This implements a storage pool for partitioning local disks. It uses parted as the tool for reading & writing disk partitions. Since parted is GPLv3, we cannot link against the code directly. So, this driver calls out to the regular 'parted' command line tool for creationg/deletion. For listing of partiitons we have a trivial helper program '/usr/libexec/libvirt_parthelper'. This outputs partition listing in a easily parseable format - each partition, or free space extent is a record, containing 5 fields. Records and fields are separated by NULLs. The internal libvirt helper API virStorageBackendRunProgNul can reliably parse this data format (thanks to Jim for the suggestion of using NULLs instead of whitespace, since the latter is not so easy to protect against bogus files) Creating volumes from disk partitions is more complex than most of the drivers. This is because partition tables have quite strict placement constraints. There are free extent regions and a partition must fall within one, and further more be aligned to a suitable byte boundary. For this reason the pool XML format will contain information about the free extents on a disk. The volume XML will also detail the actual extents used by a partition on disk. All of the parted partition table types are supported. A new disk can be initialized with the 'build' operation which will write a new partition table. When creating volumes a partition entry type can be specified if required. For simply assigning volumes to guests though, it is not neccessary to use anything other than the default of 'none'. .hgignore | 1 b/src/parthelper.c | 127 ++++++++++ b/src/storage_backend_disk.c | 508 +++++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_disk.h | 45 +++ configure.in | 48 ++++ libvirt.spec.in | 5 src/Makefile.am | 18 + src/storage_backend.c | 14 + 8 files changed, 766 insertions(+) diff -r 4c283aac5fd6 .hgignore --- a/.hgignore Thu Feb 07 14:17:57 2008 -0500 +++ b/.hgignore Thu Feb 07 16:51:32 2008 -0500 @@ -18,6 +18,7 @@ Makefile\.in$ ^src/virsh ^src/libvirt.la ^src/\.libs +^src/parthelper ^aclocal.m4$ ^docs/devhelp/libvirt.devhelp$ ^libtool$ diff -r 4c283aac5fd6 configure.in --- a/configure.in Thu Feb 07 14:17:57 2008 -0500 +++ b/configure.in Thu Feb 07 16:51:32 2008 -0500 @@ -27,6 +27,7 @@ GNUTLS_REQUIRED="1.0.25" GNUTLS_REQUIRED="1.0.25" AVAHI_REQUIRED="0.6.0" POLKIT_REQUIRED="0.6" +PARTED_REQUIRED="1.8.0" dnl Checks for C compiler. AC_PROG_CC @@ -563,6 +564,8 @@ AC_ARG_WITH(storage-lvm, [ --with-storage-lvm with LVM backend for the storage driver (on)],[],[with_storage_lvm=check]) AC_ARG_WITH(storage-iscsi, [ --with-storage-iscsi with iSCSI backend for the storage driver (on)],[],[with_storage_iscsi=check]) +AC_ARG_WITH(storage-disk, +[ --with-storage-disk with GPartd Disk backend for the storage driver (on)],[],[with_storage_disk=check]) if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin]) @@ -674,6 +677,50 @@ if test "$with_storage_iscsi" = "yes" -o fi fi AM_CONDITIONAL(WITH_STORAGE_ISCSI, [test "$with_storage_iscsi" = "yes"]) + + + +LIBPARTED_CFLAGS= +LIBPARTED_LIBS= +if test "$with_storage_disk" = "yes" -o "$with_storage_disk" = "check"; then + AC_PATH_PROG(PARTED, [parted], [], [$PATH:/sbin:/usr/sbin]) + if test -z "$PARTED" ; then with_storage_disk=no ; fi + + PARTED_FOUND=yes + if test "$with_storage_disk" != "no" -a "x$PKG_CONFIG" != "x" ; then + PKG_CHECK_MODULES(LIBPARTED, libparted >= $PARTED_REQUIRED, [], [PARTED_FOUND=no]) + fi + if test "$PARTED_FOUND" = "no"; then + # RHEL-5 vintage parted is missing pkg-config files + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + PARTED_FOUND=yes + AC_CHECK_HEADER(parted/parted.h,,[PARTED_FOUND=no]) + AC_CHECK_LIB(uuid, uuid_generate,,[PARTED_FOUND=no]) + AC_CHECK_LIB(parted, ped_device_read,,[PARTED_FOUND=no]) + LIBPARTED_LIBS="-luuid -lparted" + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + fi + + if test "$PARTED_FOUND" = "no" ; then + if test "$with_storage_disk" = "yes" ; then + AC_MSG_ERROR(We need parted for disk storage driver) + else + with_storage_disk=no + fi + else + with_storage_disk=yes + fi + + if test "$with_storage_disk" = "yes"; then + AC_DEFINE_UNQUOTED(WITH_STORAGE_DISK, 1, [whether Disk backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([PARTED],["$PARTED"], [Location or name of the parted program]) + fi +fi +AM_CONDITIONAL(WITH_STORAGE_DISK, [test "$with_storage_disk" = "yes"]) +AC_SUBST(LIBPARTED_CFLAGS) +AC_SUBST(LIBPARTED_LIBS) dnl @@ -891,6 +938,7 @@ AC_MSG_NOTICE([ NetFS: $with_storage_f AC_MSG_NOTICE([ NetFS: $with_storage_fs]) AC_MSG_NOTICE([ LVM: $with_storage_lvm]) AC_MSG_NOTICE([ iSCSI: $with_storage_iscsi]) +AC_MSG_NOTICE([ Disk: $with_storage_disk]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) diff -r 4c283aac5fd6 libvirt.spec.in --- a/libvirt.spec.in Thu Feb 07 14:17:57 2008 -0500 +++ b/libvirt.spec.in Thu Feb 07 16:51:32 2008 -0500 @@ -53,6 +53,8 @@ Requires: lvm2 Requires: lvm2 # For ISCSI driver Requires: iscsi-initiator-utils +# For disk driver +Requires: parted BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -81,6 +83,8 @@ BuildRequires: lvm2 BuildRequires: lvm2 # For ISCSI driver BuildRequires: iscsi-initiator-utils +# For disk driver +BuildRequires: parted-devel Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 @@ -203,6 +207,7 @@ fi %if %{with_proxy} == "yes" %attr(4755, root, root) %{_libexecdir}/libvirt_proxy %endif +%attr(0755, root, root) %{_libexecdir}/libvirt_parthelper %attr(0755, root, root) %{_sbindir}/libvirtd %doc docs/*.rng %doc docs/*.xml diff -r 4c283aac5fd6 src/Makefile.am --- a/src/Makefile.am Thu Feb 07 14:17:57 2008 -0500 +++ b/src/Makefile.am Thu Feb 07 16:51:32 2008 -0500 @@ -81,6 +81,13 @@ EXTRA_DIST += storage_backend_iscsi.h s EXTRA_DIST += storage_backend_iscsi.h storage_backend_iscsi.c endif +if WITH_STORAGE_DISK +CLIENT_SOURCES += storage_backend_disk.h storage_backend_disk.c +else +EXTRA_DIST += storage_backend_disk.h storage_backend_disk.c +endif + + libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) @@ -100,6 +107,17 @@ virsh_LDADD = $(LDADDS) $(VIRSH_LIBS) virsh_LDADD = $(LDADDS) $(VIRSH_LIBS) virsh_CFLAGS = $(COVERAGE_CFLAGS) $(READLINE_CFLAGS) +if WITH_STORAGE_DISK +libexec_PROGRAMS = libvirt_parthelper + +libvirt_parthelper_SOURCES = parthelper.c +libvirt_parthelper_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) +libvirt_parthelper_LDADD = $(LIBPARTED_LIBS) +libvirt_parthelper_CFLAGS = $(LIBPARTED_CFLAGS) +else +EXTRA_DIST += parthelper.c +endif + # # target to ease building test programs # diff -r 4c283aac5fd6 src/parthelper.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parthelper.c Thu Feb 07 16:51:32 2008 -0500 @@ -0,0 +1,127 @@ +/* + * parthelper.c: Helper program to talk to parted with. + * + * This helper exists because parted is GPLv3+, while libvirt is LGPLv2+. + * Thus we can't link to parted in libvirt.so without the combined work + * being GPLv3+. Thus we separate via an external command. NB, this source + * code is still LGPLv2+, but the binary helper is effectively GPLv3+ + * + * The existing 'parted' command line tool is also incredibly hard to parse + * in a reliable fashion if merely after a list of partitions & sizes, + * though it is fine for creating partitions. + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <parted/parted.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + PedDevice *dev; + PedDisk *disk; + PedPartition *part; + + if (argc != 2) { + fprintf(stderr, "syntax: %s DEVICE\n", argv[0]); + return 1; + } + + if ((dev = ped_device_get(argv[1])) == NULL) { + fprintf(stderr, "unable to access device %s\n", argv[1]); + return 2; + } + + if ((disk = ped_disk_new(dev)) == NULL) { + fprintf(stderr, "unable to access disk %s\n", argv[1]); + return 2; + } + + /* Get the first partition, and then iterate over all */ + part = ped_disk_get_partition(disk, 1); + while (part) { + const char *type; + const char *content; + if (part->type & PED_PARTITION_LOGICAL) { + type = "logical"; + if (part->type & PED_PARTITION_FREESPACE) + content = "free"; + else if (part->type & PED_PARTITION_METADATA) + content = "metadata"; + else if (part->type & PED_PARTITION_PROTECTED) + content = "protected"; + else + content = "data"; + } else if (part->type == PED_PARTITION_EXTENDED) { + type = "extended"; + content = "metadata"; + } else { + type = "normal"; + if (part->type & PED_PARTITION_FREESPACE) + content = "free"; + else if (part->type & PED_PARTITION_METADATA) + content = "metadata"; + else if (part->type & PED_PARTITION_PROTECTED) + content = "protected"; + else + content = "data"; + } + + /* We do +1 on geom.end, because we want end of the last sector + * in bytes, not the last sector number + */ + if (part->num != -1) { + printf("%s%d%c%s%c%s%c%llu%c%llu%c%llu%c", + part->geom.dev->path, + part->num, '\0', + type, '\0', + content, '\0', + part->geom.start * 512llu, '\0', + (part->geom.end + 1 ) * 512llu, '\0', + part->geom.length * 512llu, '\0'); + } else { + printf("%s%c%s%c%s%c%llu%c%llu%c%llu%c", + "-", '\0', + type, '\0', + content, '\0', + part->geom.start * 512llu, '\0', + (part->geom.end + 1 ) * 512llu, '\0', + part->geom.length * 512llu, '\0'); + } + part = ped_disk_next_partition(disk, part); + } + + return 0; +} +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 4c283aac5fd6 src/storage_backend.c --- a/src/storage_backend.c Thu Feb 07 14:17:57 2008 -0500 +++ b/src/storage_backend.c Thu Feb 07 16:51:32 2008 -0500 @@ -42,6 +42,9 @@ #if WITH_STORAGE_ISCSI #include "storage_backend_iscsi.h" #endif +#if WITH_STORAGE_DISK +#include "storage_backend_disk.h" +#endif #include "util.h" @@ -60,6 +63,9 @@ static virStorageBackendPtr backends[] = #endif #if WITH_STORAGE_ISCSI &virStorageBackendISCSI, +#endif +#if WITH_STORAGE_DISK + &virStorageBackendDisk, #endif }; @@ -105,6 +111,10 @@ int virStorageBackendFromString(const ch #if WITH_STORAGE_ISCSI if (STREQ(type, "iscsi")) return VIR_STORAGE_POOL_ISCSI; +#endif +#if WITH_STORAGE_DISK + if (STREQ(type, "disk")) + return VIR_STORAGE_POOL_DISK; #endif virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %s", type); return -1; @@ -127,6 +137,10 @@ const char *virStorageBackendToString(in #if WITH_STORAGE_ISCSI case VIR_STORAGE_POOL_ISCSI: return "iscsi"; +#endif +#if WITH_STORAGE_DISK + case VIR_STORAGE_POOL_DISK: + return "disk"; #endif } diff -r 4c283aac5fd6 src/storage_backend_disk.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_disk.c Thu Feb 07 16:51:32 2008 -0500 @@ -0,0 +1,508 @@ +/* + * storage_backend_disk.c: storage backend for disk handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "storage_backend_disk.h" +#include "util.h" + +enum { + VIR_STORAGE_POOL_DISK_DOS = 0, + VIR_STORAGE_POOL_DISK_DVH, + VIR_STORAGE_POOL_DISK_GPT, + VIR_STORAGE_POOL_DISK_MAC, + VIR_STORAGE_POOL_DISK_BSD, + VIR_STORAGE_POOL_DISK_PC98, + VIR_STORAGE_POOL_DISK_SUN, +}; + +/* + * XXX these are basically partition types. + * + * fdisk has a bazillion partition ID types + * parted has practically none, and splits the + * info across 3 different attributes. + * + * So this is a semi-generic set + */ +enum { + VIR_STORAGE_VOL_DISK_NONE = 0, + VIR_STORAGE_VOL_DISK_LINUX, + VIR_STORAGE_VOL_DISK_FAT16, + VIR_STORAGE_VOL_DISK_FAT32, + VIR_STORAGE_VOL_DISK_LINUX_SWAP, + VIR_STORAGE_VOL_DISK_LINUX_LVM, + VIR_STORAGE_VOL_DISK_LINUX_RAID, + VIR_STORAGE_VOL_DISK_EXTENDED, +}; + +#define PARTHELPER BINDIR "/libvirt_parthelper" + +static int virStorageBackendDiskPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_DISK_DOS; + + if (STREQ(format, "dos")) + return VIR_STORAGE_POOL_DISK_DOS; + if (STREQ(format, "dvh")) + return VIR_STORAGE_POOL_DISK_DVH; + if (STREQ(format, "gpt")) + return VIR_STORAGE_POOL_DISK_GPT; + if (STREQ(format, "mac")) + return VIR_STORAGE_POOL_DISK_MAC; + if (STREQ(format, "bsd")) + return VIR_STORAGE_POOL_DISK_BSD; + if (STREQ(format, "pc98")) + return VIR_STORAGE_POOL_DISK_PC98; + if (STREQ(format, "sun")) + return VIR_STORAGE_POOL_DISK_SUN; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported pool format %s", format); + return -1; +} + +static const char *virStorageBackendDiskPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_DISK_DOS: + return "dos"; + case VIR_STORAGE_POOL_DISK_DVH: + return "dvh"; + case VIR_STORAGE_POOL_DISK_GPT: + return "gpt"; + case VIR_STORAGE_POOL_DISK_MAC: + return "mac"; + case VIR_STORAGE_POOL_DISK_BSD: + return "bsd"; + case VIR_STORAGE_POOL_DISK_PC98: + return "pc98"; + case VIR_STORAGE_POOL_DISK_SUN: + return "sun"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported pool format %d", format); + return NULL; +} + +static int virStorageBackendDiskVolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_VOL_DISK_NONE; + + if (STREQ(format, "none")) + return VIR_STORAGE_VOL_DISK_NONE; + if (STREQ(format, "linux")) + return VIR_STORAGE_VOL_DISK_LINUX; + if (STREQ(format, "fat16")) + return VIR_STORAGE_VOL_DISK_FAT16; + if (STREQ(format, "fat32")) + return VIR_STORAGE_VOL_DISK_FAT32; + if (STREQ(format, "linux-swap")) + return VIR_STORAGE_VOL_DISK_LINUX_SWAP; + if (STREQ(format, "linux-lvm")) + return VIR_STORAGE_VOL_DISK_LINUX_LVM; + if (STREQ(format, "linux-raid")) + return VIR_STORAGE_VOL_DISK_LINUX_RAID; + if (STREQ(format, "extended")) + return VIR_STORAGE_VOL_DISK_EXTENDED; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %s", format); + return -1; +} + +static const char *virStorageBackendDiskVolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_VOL_DISK_NONE: + return "none"; + case VIR_STORAGE_VOL_DISK_LINUX: + return "linux"; + case VIR_STORAGE_VOL_DISK_FAT16: + return "fat16"; + case VIR_STORAGE_VOL_DISK_FAT32: + return "fat32"; + case VIR_STORAGE_VOL_DISK_LINUX_SWAP: + return "swap"; + case VIR_STORAGE_VOL_DISK_LINUX_LVM: + return "lvm"; + case VIR_STORAGE_VOL_DISK_LINUX_RAID: + return "raid"; + case VIR_STORAGE_VOL_DISK_EXTENDED: + return "extended"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %d", format); + return NULL; +} + +static int virStorageBackendDiskMakeDataVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + virStorageVolDefPtr vol) +{ + char *tmp, *devpath; + + if (vol == NULL) { + if ((vol = calloc(1, sizeof(virStorageVolDef))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + return -1; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + + /* Prepended path will be same for all partitions, so we can + * strip the path to form a reasonable pool-unique name + */ + tmp = strrchr(groups[0], '/'); + if ((vol->name = strdup(tmp ? tmp + 1 : groups[0])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + return -1; + } + } + + if (vol->target.path == NULL) { + if ((devpath = strdup(groups[0])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + return -1; + } + + /* Now figure out the stable path + * + * XXX this method is O(N) because it scans the pool target + * dir every time its run. Should figure out a more efficient + * way of doing this... + */ + if ((vol->target.path = virStorageBackendStablePath(conn, pool, devpath)) == NULL) + return -1; + + if (devpath != vol->target.path) + free(devpath); + devpath = NULL; + } + + if (vol->key == NULL) { + /* XXX base off a unique key of the underlying disk */ + if ((vol->key = strdup(vol->target.path)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + return -1; + } + } + + if (vol->source.extents == NULL) { + if ((vol->source.extents = calloc(1, sizeof(*(vol->source.extents)))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume extents"); + return -1; + } + vol->source.nextent = 1; + + if (xstrtol_ull(groups[3], NULL, 10, + &vol->source.extents[0].start) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot parse device start location"); + return -1; + } + + if (xstrtol_ull(groups[4], NULL, 10, + &vol->source.extents[0].end) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot parse device end location"); + return -1; + } + + if ((vol->source.extents[0].path = strdup(pool->def->source.devices[0].path)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "extents"); + return -1; + } + } + + /* Refresh allocation/capacity/perms */ + if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0) + return -1; + + /* The above gets allocation wrong for extended partitions, so overwrite it */ + vol->allocation = vol->capacity = (vol->source.extents[0].end - vol->source.extents[0].start); + + if (STRNEQ(groups[2], "metadata")) + pool->def->allocation += vol->allocation; + if (vol->source.extents[0].end > pool->def->capacity) + pool->def->capacity = vol->source.extents[0].end; + + return 0; +} + +static int virStorageBackendDiskMakeFreeExtent(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool, + char **const groups) +{ + virStoragePoolSourceDeviceExtentPtr tmp; + virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0]; + + if ((tmp = realloc(dev->freeExtents, + sizeof(*tmp) * (dev->nfreeExtent+1))) == NULL) + return -1; + dev->freeExtents = tmp; + + memset(dev->freeExtents + + dev->nfreeExtent, 0, sizeof(*tmp)); + + if (xstrtol_ull(groups[3], NULL, 10, + &dev->freeExtents[dev->nfreeExtent].start) < 0) + return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */ + + if (xstrtol_ull(groups[4], NULL, 10, + &dev->freeExtents[dev->nfreeExtent].end) < 0) + return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */ + + pool->def->available += + (dev->freeExtents[dev->nfreeExtent].end - + dev->freeExtents[dev->nfreeExtent].start); + if (dev->freeExtents[dev->nfreeExtent].end > pool->def->capacity) + pool->def->capacity = dev->freeExtents[dev->nfreeExtent].end; + + dev->nfreeExtent++; + + return 0; +} + + +static int virStorageBackendDiskMakeVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + size_t ntok ATTRIBUTE_UNUSED, + char **const groups, + void *data) +{ + /* + * Ignore normal+metadata, and logical+metadata partitions + * since they're basically internal book-keeping regions + * we have no control over. Do keep extended+metadata though + * because that's the MS-DOS extended partition region we + * need to be able to view/create/delete + */ + if ((STREQ(groups[1], "normal") || + STREQ(groups[1], "logical")) && + STREQ(groups[2], "metadata")) + return 0; + + /* Remaining data / metdata parts get turn into volumes... */ + if (STREQ(groups[2], "metadata") || + STREQ(groups[2], "data")) { + virStorageVolDefPtr vol = data; + /* We're searching for a specific vol only, so ignore others */ + if (vol && + STRNEQ(vol->name, groups[0])) + return 0; + + return virStorageBackendDiskMakeDataVol(conn, pool, groups, vol); + } else if (STREQ(groups[2], "free")) { + /* ....or free space extents */ + return virStorageBackendDiskMakeFreeExtent(conn, pool, groups); + } else { + /* This codepath should never happen unless someone changed + * libvirt_parthelper forgot to change this code */ + return -1; + } +} + + +/* To get a list of partitions we run an external helper + * tool which then uses parted APIs. This is because + * parted's API is not compatible with libvirt's license + * but we really really want to use parted because the + * other options all suck :-) + * + * All the other storage backends run an external tool for + * listing volumes so this really isn't too much of a pain, + * and we can even ensure the output is friendly. + */ +static int virStorageBackendDiskReadPartitions(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + + /* + * # libvirt_parthelper DEVICE + * /dev/sda1 normal data 32256 106928128 106896384 + * /dev/sda2 normal data 106928640 100027629568 99920701440 + * - normal metadata 100027630080 100030242304 2612736 + * + */ + const char *prog[] = { + PARTHELPER, pool->def->source.devices[0].path, NULL, + }; + + pool->def->allocation = pool->def->capacity = pool->def->available = 0; + + return virStorageBackendRunProgNul(conn, + pool, + prog, + 6, + virStorageBackendDiskMakeVol, + vol); +} + + +static int virStorageBackendDiskRefreshPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + free(pool->def->source.devices[0].freeExtents); + pool->def->source.devices[0].nfreeExtent = 0; + pool->def->source.devices[0].freeExtents = NULL; + + return virStorageBackendDiskReadPartitions(conn, pool, NULL); +} + + +/** + * Write a new partition table header + */ +static int virStorageBackendDiskBuildPool(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + /* eg parted /dev/sda mklabel msdos */ + const char *prog[] = { + PARTED, + pool->def->source.devices[0].path, + "mklabel", + virStorageBackendDiskPoolFormatToString(conn, pool->def->source.format), + NULL, + }; + + if (virRun(conn, (char**)prog, NULL) < 0) + return -1; + + return 0; +} + + +static int virStorageBackendDiskDeleteVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + unsigned int flags); + +static int virStorageBackendDiskCreateVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + int i; + char start[100], end[100]; + unsigned long long startOffset, endOffset, smallestSize = 0, smallestExtent = -1; + virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0]; + /* XXX customizable partition types */ + const char *cmdargv[] = { + PARTED, + pool->def->source.devices[0].path, + "mkpart", + "--script", + "ext2", + start, + end, + NULL + }; + + for (i = 0 ; i < dev->nfreeExtent ; i++) { + unsigned long long size = + dev->freeExtents[i].end - + dev->freeExtents[i].start; + if (size > vol->allocation && + (smallestSize == 0 || + size < smallestSize)) { + smallestSize = size; + smallestExtent = i; + } + } + if (smallestExtent == -1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "no large enough free extent"); + return -1; + } + startOffset = dev->freeExtents[smallestExtent].start; + endOffset = startOffset + vol->allocation; + + snprintf(start, sizeof(start)-1, "%lluB", startOffset); + start[sizeof(start)-1] = '\0'; + snprintf(end, sizeof(end)-1, "%lluB", endOffset); + end[sizeof(end)-1] = '\0'; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + /* Blow away free extent info, as we're about to re-populate it */ + free(pool->def->source.devices[0].freeExtents); + pool->def->source.devices[0].nfreeExtent = 0; + pool->def->source.devices[0].freeExtents = NULL; + + /* Fetch actual extent info */ + if (virStorageBackendDiskReadPartitions(conn, pool, vol) < 0) + return -1; + + return 0; +} + + +static int virStorageBackendDiskDeleteVol(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED) +{ + /* delete a partition */ + virStorageReportError(conn, VIR_ERR_NO_SUPPORT, "Disk pools are not yet supported"); + return -1; +} + + +virStorageBackend virStorageBackendDisk = { + .type = VIR_STORAGE_POOL_DISK, + + .buildPool = virStorageBackendDiskBuildPool, + .refreshPool = virStorageBackendDiskRefreshPool, + + .createVol = virStorageBackendDiskCreateVol, + .deleteVol = virStorageBackendDiskDeleteVol, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE), + .formatFromString = virStorageBackendDiskPoolFormatFromString, + .formatToString = virStorageBackendDiskPoolFormatToString, + }, + .volOptions = { + .formatFromString = virStorageBackendDiskVolFormatFromString, + .formatToString = virStorageBackendDiskVolFormatToString, + }, + + .volType = VIR_STORAGE_VOL_BLOCK, +}; + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 4c283aac5fd6 src/storage_backend_disk.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_disk.h Thu Feb 07 16:51:32 2008 -0500 @@ -0,0 +1,45 @@ +/* + * storage_backend_disk.h: storage backend for disk handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_BACKEND_DISK_H__ +#define __VIR_STORAGE_BACKEND_DISK_H__ + +#include "storage_backend.h" + +extern virStorageBackend virStorageBackendDisk; + +#endif /* __VIR_STORAGE_BACKEND_DISK_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
diff -r 4c283aac5fd6 src/storage_backend_disk.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_disk.c Thu Feb 07 16:51:32 2008 -0500 ... +static int virStorageBackendDiskVolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_VOL_DISK_NONE; + + if (STREQ(format, "none")) + return VIR_STORAGE_VOL_DISK_NONE; + if (STREQ(format, "linux")) + return VIR_STORAGE_VOL_DISK_LINUX; + if (STREQ(format, "fat16")) + return VIR_STORAGE_VOL_DISK_FAT16; + if (STREQ(format, "fat32")) + return VIR_STORAGE_VOL_DISK_FAT32; + if (STREQ(format, "linux-swap")) + return VIR_STORAGE_VOL_DISK_LINUX_SWAP; + if (STREQ(format, "linux-lvm")) + return VIR_STORAGE_VOL_DISK_LINUX_LVM; + if (STREQ(format, "linux-raid")) + return VIR_STORAGE_VOL_DISK_LINUX_RAID; + if (STREQ(format, "extended")) + return VIR_STORAGE_VOL_DISK_EXTENDED; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %s", format); + return -1; +} + +static const char *virStorageBackendDiskVolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_VOL_DISK_NONE: + return "none"; + case VIR_STORAGE_VOL_DISK_LINUX: + return "linux"; + case VIR_STORAGE_VOL_DISK_FAT16: + return "fat16"; + case VIR_STORAGE_VOL_DISK_FAT32: + return "fat32"; + case VIR_STORAGE_VOL_DISK_LINUX_SWAP: + return "swap"; + case VIR_STORAGE_VOL_DISK_LINUX_LVM: + return "lvm"; + case VIR_STORAGE_VOL_DISK_LINUX_RAID: + return "raid"; + case VIR_STORAGE_VOL_DISK_EXTENDED: + return "extended"; + }
Hi Dan, I suppose the above functions should have the same strings and enum values? If so, the strings for the three VIR_STORAGE_VOL_DISK_LINUX_* enums in the latter need to match the "linux-..." prefixed ones above.

On Fri, Feb 15, 2008 at 05:11:03PM +0100, Jim Meyering wrote:
+static const char *virStorageBackendDiskVolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_VOL_DISK_NONE: + return "none"; + case VIR_STORAGE_VOL_DISK_LINUX: + return "linux"; + case VIR_STORAGE_VOL_DISK_FAT16: + return "fat16"; + case VIR_STORAGE_VOL_DISK_FAT32: + return "fat32"; + case VIR_STORAGE_VOL_DISK_LINUX_SWAP: + return "swap"; + case VIR_STORAGE_VOL_DISK_LINUX_LVM: + return "lvm"; + case VIR_STORAGE_VOL_DISK_LINUX_RAID: + return "raid"; + case VIR_STORAGE_VOL_DISK_EXTENDED: + return "extended"; + }
Hi Dan,
I suppose the above functions should have the same strings and enum values? If so, the strings for the three VIR_STORAGE_VOL_DISK_LINUX_* enums in the latter need to match the "linux-..." prefixed ones above.
Yes, good catch - they definitely need the linux- prefix Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
+static int virStorageBackendDiskCreateVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + int i; + char start[100], end[100]; + unsigned long long startOffset, endOffset, smallestSize = 0, smallestExtent = -1;
[Past column 80 -> harder to spot] unsigned... = -1; ? How about this instead, since smallestExtent only ever gets set to values stored in "i" (an int)? int smallestExtent = -1;
+ virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0]; + /* XXX customizable partition types */ + const char *cmdargv[] = { + PARTED, + pool->def->source.devices[0].path, + "mkpart", + "--script", + "ext2", + start, + end, + NULL + }; + + for (i = 0 ; i < dev->nfreeExtent ; i++) { + unsigned long long size = + dev->freeExtents[i].end - + dev->freeExtents[i].start;
Shouldn't size be 1 larger? i.e., unsigned long long size = dev->freeExtents[i].end - dev->freeExtents[i].start + 1;

On Fri, Feb 15, 2008 at 07:52:37PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
+static int virStorageBackendDiskCreateVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + int i; + char start[100], end[100]; + unsigned long long startOffset, endOffset, smallestSize = 0, smallestExtent = -1;
[Past column 80 -> harder to spot] unsigned... = -1; ?
How about this instead, since smallestExtent only ever gets set to values stored in "i" (an int)?
int smallestExtent = -1;
Yes, it should just be an int.
+ virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0]; + /* XXX customizable partition types */ + const char *cmdargv[] = { + PARTED, + pool->def->source.devices[0].path, + "mkpart", + "--script", + "ext2", + start, + end, + NULL + }; + + for (i = 0 ; i < dev->nfreeExtent ; i++) { + unsigned long long size = + dev->freeExtents[i].end - + dev->freeExtents[i].start;
Shouldn't size be 1 larger? i.e.,
unsigned long long size = dev->freeExtents[i].end - dev->freeExtents[i].start + 1;
I don't think so - the +1 adjustment is already done in the libvirt_parthelper program, but I'll double-check Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Fri, Feb 15, 2008 at 07:52:37PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote: ... Shouldn't size be 1 larger? i.e.,
unsigned long long size = dev->freeExtents[i].end - dev->freeExtents[i].start + 1;
I don't think so - the +1 adjustment is already done in the libvirt_parthelper program, but I'll double-check
Yes, you're right: (from that same patch): + /* We do +1 on geom.end, because we want end of the last sector + * in bytes, not the last sector number

On Tue, Feb 12, 2008 at 04:40:26AM +0000, Daniel P. Berrange wrote:
This implements a storage pool for partitioning local disks. It uses parted as the tool for reading & writing disk partitions. Since parted is GPLv3, we cannot link against the code directly. So, this driver calls out to the regular 'parted' command line tool for creationg/deletion. For listing of partiitons we have a trivial helper program '/usr/libexec/libvirt_parthelper'. This outputs partition listing in a easily parseable format - each partition, or free space extent is a record, containing 5 fields. Records and fields are separated by NULLs. The internal libvirt helper API virStorageBackendRunProgNul can reliably parse this data format (thanks to Jim for the suggestion of using NULLs instead of whitespace, since the latter is not so easy to protect against bogus files)
Creating volumes from disk partitions is more complex than most of the drivers. This is because partition tables have quite strict placement constraints. There are free extent regions and a partition must fall within one, and further more be aligned to a suitable byte boundary. For this reason the pool XML format will contain information about the free extents on a disk. The volume XML will also detail the actual extents used by a partition on disk.
All of the parted partition table types are supported. A new disk can be initialized with the 'build' operation which will write a new partition table. When creating volumes a partition entry type can be specified if required. For simply assigning volumes to guests though, it is not neccessary to use anything other than the default of 'none'.
.hgignore | 1 b/src/parthelper.c | 127 ++++++++++ b/src/storage_backend_disk.c | 537 +++++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_disk.h | 45 +++ configure.in | 48 +++ libvirt.spec.in | 5 po/POTFILES.in | 1 src/Makefile.am | 18 + src/storage_backend.c | 14 + 9 files changed, 796 insertions(+) diff -r f54c0eb07193 .hgignore --- a/.hgignore Tue Feb 19 17:38:39 2008 -0500 +++ b/.hgignore Tue Feb 19 17:43:53 2008 -0500 @@ -18,6 +18,7 @@ Makefile\.in$ ^src/virsh ^src/libvirt.la ^src/\.libs +^src/parthelper ^aclocal.m4$ ^docs/devhelp/libvirt.devhelp$ ^libtool$ diff -r f54c0eb07193 configure.in --- a/configure.in Tue Feb 19 17:38:39 2008 -0500 +++ b/configure.in Tue Feb 19 17:43:53 2008 -0500 @@ -27,6 +27,7 @@ GNUTLS_REQUIRED="1.0.25" GNUTLS_REQUIRED="1.0.25" AVAHI_REQUIRED="0.6.0" POLKIT_REQUIRED="0.6" +PARTED_REQUIRED="1.8.0" dnl Checks for C compiler. AC_PROG_CC @@ -563,6 +564,8 @@ AC_ARG_WITH(storage-lvm, [ --with-storage-lvm with LVM backend for the storage driver (on)],[],[with_storage_lvm=check]) AC_ARG_WITH(storage-iscsi, [ --with-storage-iscsi with iSCSI backend for the storage driver (on)],[],[with_storage_iscsi=check]) +AC_ARG_WITH(storage-disk, +[ --with-storage-disk with GPartd Disk backend for the storage driver (on)],[],[with_storage_disk=check]) if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin]) @@ -674,6 +677,50 @@ if test "$with_storage_iscsi" = "yes" -o fi fi AM_CONDITIONAL(WITH_STORAGE_ISCSI, [test "$with_storage_iscsi" = "yes"]) + + + +LIBPARTED_CFLAGS= +LIBPARTED_LIBS= +if test "$with_storage_disk" = "yes" -o "$with_storage_disk" = "check"; then + AC_PATH_PROG(PARTED, [parted], [], [$PATH:/sbin:/usr/sbin]) + if test -z "$PARTED" ; then with_storage_disk=no ; fi + + PARTED_FOUND=yes + if test "$with_storage_disk" != "no" -a "x$PKG_CONFIG" != "x" ; then + PKG_CHECK_MODULES(LIBPARTED, libparted >= $PARTED_REQUIRED, [], [PARTED_FOUND=no]) + fi + if test "$PARTED_FOUND" = "no"; then + # RHEL-5 vintage parted is missing pkg-config files + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + PARTED_FOUND=yes + AC_CHECK_HEADER(parted/parted.h,,[PARTED_FOUND=no]) + AC_CHECK_LIB(uuid, uuid_generate,,[PARTED_FOUND=no]) + AC_CHECK_LIB(parted, ped_device_read,,[PARTED_FOUND=no]) + LIBPARTED_LIBS="-luuid -lparted" + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + fi + + if test "$PARTED_FOUND" = "no" ; then + if test "$with_storage_disk" = "yes" ; then + AC_MSG_ERROR(We need parted for disk storage driver) + else + with_storage_disk=no + fi + else + with_storage_disk=yes + fi + + if test "$with_storage_disk" = "yes"; then + AC_DEFINE_UNQUOTED(WITH_STORAGE_DISK, 1, [whether Disk backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([PARTED],["$PARTED"], [Location or name of the parted program]) + fi +fi +AM_CONDITIONAL(WITH_STORAGE_DISK, [test "$with_storage_disk" = "yes"]) +AC_SUBST(LIBPARTED_CFLAGS) +AC_SUBST(LIBPARTED_LIBS) dnl @@ -891,6 +938,7 @@ AC_MSG_NOTICE([ NetFS: $with_storage_f AC_MSG_NOTICE([ NetFS: $with_storage_fs]) AC_MSG_NOTICE([ LVM: $with_storage_lvm]) AC_MSG_NOTICE([ iSCSI: $with_storage_iscsi]) +AC_MSG_NOTICE([ Disk: $with_storage_disk]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) diff -r f54c0eb07193 libvirt.spec.in --- a/libvirt.spec.in Tue Feb 19 17:38:39 2008 -0500 +++ b/libvirt.spec.in Tue Feb 19 17:43:53 2008 -0500 @@ -53,6 +53,8 @@ Requires: lvm2 Requires: lvm2 # For ISCSI driver Requires: iscsi-initiator-utils +# For disk driver +Requires: parted BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -81,6 +83,8 @@ BuildRequires: lvm2 BuildRequires: lvm2 # For ISCSI driver BuildRequires: iscsi-initiator-utils +# For disk driver +BuildRequires: parted-devel Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 @@ -203,6 +207,7 @@ fi %if %{with_proxy} == "yes" %attr(4755, root, root) %{_libexecdir}/libvirt_proxy %endif +%attr(0755, root, root) %{_libexecdir}/libvirt_parthelper %attr(0755, root, root) %{_sbindir}/libvirtd %doc docs/*.rng %doc docs/*.xml diff -r f54c0eb07193 po/POTFILES.in --- a/po/POTFILES.in Tue Feb 19 17:38:39 2008 -0500 +++ b/po/POTFILES.in Tue Feb 19 17:43:53 2008 -0500 @@ -14,6 +14,7 @@ src/storage_backend_fs.c src/storage_backend_fs.c src/storage_backend_logical.c src/storage_backend_iscsi.c +src/storage_backend_disk.c src/storage_conf.c src/storage_driver.c src/sexpr.c diff -r f54c0eb07193 src/Makefile.am --- a/src/Makefile.am Tue Feb 19 17:38:39 2008 -0500 +++ b/src/Makefile.am Tue Feb 19 17:43:53 2008 -0500 @@ -80,6 +80,13 @@ EXTRA_DIST += storage_backend_iscsi.h s EXTRA_DIST += storage_backend_iscsi.h storage_backend_iscsi.c endif +if WITH_STORAGE_DISK +CLIENT_SOURCES += storage_backend_disk.h storage_backend_disk.c +else +EXTRA_DIST += storage_backend_disk.h storage_backend_disk.c +endif + + libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) @@ -99,6 +106,17 @@ virsh_LDADD = $(LDADDS) $(VIRSH_LIBS) virsh_LDADD = $(LDADDS) $(VIRSH_LIBS) virsh_CFLAGS = $(COVERAGE_CFLAGS) $(READLINE_CFLAGS) +if WITH_STORAGE_DISK +libexec_PROGRAMS = libvirt_parthelper + +libvirt_parthelper_SOURCES = parthelper.c +libvirt_parthelper_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) +libvirt_parthelper_LDADD = $(LIBPARTED_LIBS) +libvirt_parthelper_CFLAGS = $(LIBPARTED_CFLAGS) +else +EXTRA_DIST += parthelper.c +endif + # # target to ease building test programs # diff -r f54c0eb07193 src/parthelper.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parthelper.c Tue Feb 19 17:43:53 2008 -0500 @@ -0,0 +1,127 @@ +/* + * parthelper.c: Helper program to talk to parted with. + * + * This helper exists because parted is GPLv3+, while libvirt is LGPLv2+. + * Thus we can't link to parted in libvirt.so without the combined work + * being GPLv3+. Thus we separate via an external command. NB, this source + * code is still LGPLv2+, but the binary helper is effectively GPLv3+ + * + * The existing 'parted' command line tool is also incredibly hard to parse + * in a reliable fashion if merely after a list of partitions & sizes, + * though it is fine for creating partitions. + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <parted/parted.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + PedDevice *dev; + PedDisk *disk; + PedPartition *part; + + if (argc != 2) { + fprintf(stderr, "syntax: %s DEVICE\n", argv[0]); + return 1; + } + + if ((dev = ped_device_get(argv[1])) == NULL) { + fprintf(stderr, "unable to access device %s\n", argv[1]); + return 2; + } + + if ((disk = ped_disk_new(dev)) == NULL) { + fprintf(stderr, "unable to access disk %s\n", argv[1]); + return 2; + } + + /* Get the first partition, and then iterate over all */ + part = ped_disk_get_partition(disk, 1); + while (part) { + const char *type; + const char *content; + if (part->type & PED_PARTITION_LOGICAL) { + type = "logical"; + if (part->type & PED_PARTITION_FREESPACE) + content = "free"; + else if (part->type & PED_PARTITION_METADATA) + content = "metadata"; + else if (part->type & PED_PARTITION_PROTECTED) + content = "protected"; + else + content = "data"; + } else if (part->type == PED_PARTITION_EXTENDED) { + type = "extended"; + content = "metadata"; + } else { + type = "normal"; + if (part->type & PED_PARTITION_FREESPACE) + content = "free"; + else if (part->type & PED_PARTITION_METADATA) + content = "metadata"; + else if (part->type & PED_PARTITION_PROTECTED) + content = "protected"; + else + content = "data"; + } + + /* We do +1 on geom.end, because we want end of the last sector + * in bytes, not the last sector number + */ + if (part->num != -1) { + printf("%s%d%c%s%c%s%c%llu%c%llu%c%llu%c", + part->geom.dev->path, + part->num, '\0', + type, '\0', + content, '\0', + part->geom.start * 512llu, '\0', + (part->geom.end + 1 ) * 512llu, '\0', + part->geom.length * 512llu, '\0'); + } else { + printf("%s%c%s%c%s%c%llu%c%llu%c%llu%c", + "-", '\0', + type, '\0', + content, '\0', + part->geom.start * 512llu, '\0', + (part->geom.end + 1 ) * 512llu, '\0', + part->geom.length * 512llu, '\0'); + } + part = ped_disk_next_partition(disk, part); + } + + return 0; +} +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r f54c0eb07193 src/storage_backend.c --- a/src/storage_backend.c Tue Feb 19 17:38:39 2008 -0500 +++ b/src/storage_backend.c Tue Feb 19 17:43:53 2008 -0500 @@ -42,6 +42,9 @@ #if WITH_STORAGE_ISCSI #include "storage_backend_iscsi.h" #endif +#if WITH_STORAGE_DISK +#include "storage_backend_disk.h" +#endif #include "util.h" @@ -60,6 +63,9 @@ static virStorageBackendPtr backends[] = #endif #if WITH_STORAGE_ISCSI &virStorageBackendISCSI, +#endif +#if WITH_STORAGE_DISK + &virStorageBackendDisk, #endif }; @@ -110,6 +116,10 @@ virStorageBackendFromString(const char * #if WITH_STORAGE_ISCSI if (STREQ(type, "iscsi")) return VIR_STORAGE_POOL_ISCSI; +#endif +#if WITH_STORAGE_DISK + if (STREQ(type, "disk")) + return VIR_STORAGE_POOL_DISK; #endif virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, @@ -135,6 +145,10 @@ virStorageBackendToString(int type) { #if WITH_STORAGE_ISCSI case VIR_STORAGE_POOL_ISCSI: return "iscsi"; +#endif +#if WITH_STORAGE_DISK + case VIR_STORAGE_POOL_DISK: + return "disk"; #endif } diff -r f54c0eb07193 src/storage_backend_disk.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_disk.c Tue Feb 19 17:43:53 2008 -0500 @@ -0,0 +1,537 @@ +/* + * storage_backend_disk.c: storage backend for disk handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "storage_backend_disk.h" +#include "util.h" + +enum { + VIR_STORAGE_POOL_DISK_DOS = 0, + VIR_STORAGE_POOL_DISK_DVH, + VIR_STORAGE_POOL_DISK_GPT, + VIR_STORAGE_POOL_DISK_MAC, + VIR_STORAGE_POOL_DISK_BSD, + VIR_STORAGE_POOL_DISK_PC98, + VIR_STORAGE_POOL_DISK_SUN, +}; + +/* + * XXX these are basically partition types. + * + * fdisk has a bazillion partition ID types + * parted has practically none, and splits the + * info across 3 different attributes. + * + * So this is a semi-generic set + */ +enum { + VIR_STORAGE_VOL_DISK_NONE = 0, + VIR_STORAGE_VOL_DISK_LINUX, + VIR_STORAGE_VOL_DISK_FAT16, + VIR_STORAGE_VOL_DISK_FAT32, + VIR_STORAGE_VOL_DISK_LINUX_SWAP, + VIR_STORAGE_VOL_DISK_LINUX_LVM, + VIR_STORAGE_VOL_DISK_LINUX_RAID, + VIR_STORAGE_VOL_DISK_EXTENDED, +}; + +#define PARTHELPER BINDIR "/libvirt_parthelper" + +static int +virStorageBackendDiskPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_DISK_DOS; + + if (STREQ(format, "dos")) + return VIR_STORAGE_POOL_DISK_DOS; + if (STREQ(format, "dvh")) + return VIR_STORAGE_POOL_DISK_DVH; + if (STREQ(format, "gpt")) + return VIR_STORAGE_POOL_DISK_GPT; + if (STREQ(format, "mac")) + return VIR_STORAGE_POOL_DISK_MAC; + if (STREQ(format, "bsd")) + return VIR_STORAGE_POOL_DISK_BSD; + if (STREQ(format, "pc98")) + return VIR_STORAGE_POOL_DISK_PC98; + if (STREQ(format, "sun")) + return VIR_STORAGE_POOL_DISK_SUN; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported pool format %s"), format); + return -1; +} + +static const char * +virStorageBackendDiskPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_DISK_DOS: + return "dos"; + case VIR_STORAGE_POOL_DISK_DVH: + return "dvh"; + case VIR_STORAGE_POOL_DISK_GPT: + return "gpt"; + case VIR_STORAGE_POOL_DISK_MAC: + return "mac"; + case VIR_STORAGE_POOL_DISK_BSD: + return "bsd"; + case VIR_STORAGE_POOL_DISK_PC98: + return "pc98"; + case VIR_STORAGE_POOL_DISK_SUN: + return "sun"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported pool format %d"), format); + return NULL; +} + +static int +virStorageBackendDiskVolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_VOL_DISK_NONE; + + if (STREQ(format, "none")) + return VIR_STORAGE_VOL_DISK_NONE; + if (STREQ(format, "linux")) + return VIR_STORAGE_VOL_DISK_LINUX; + if (STREQ(format, "fat16")) + return VIR_STORAGE_VOL_DISK_FAT16; + if (STREQ(format, "fat32")) + return VIR_STORAGE_VOL_DISK_FAT32; + if (STREQ(format, "linux-swap")) + return VIR_STORAGE_VOL_DISK_LINUX_SWAP; + if (STREQ(format, "linux-lvm")) + return VIR_STORAGE_VOL_DISK_LINUX_LVM; + if (STREQ(format, "linux-raid")) + return VIR_STORAGE_VOL_DISK_LINUX_RAID; + if (STREQ(format, "extended")) + return VIR_STORAGE_VOL_DISK_EXTENDED; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %s"), format); + return -1; +} + +static const char * +virStorageBackendDiskVolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_VOL_DISK_NONE: + return "none"; + case VIR_STORAGE_VOL_DISK_LINUX: + return "linux"; + case VIR_STORAGE_VOL_DISK_FAT16: + return "fat16"; + case VIR_STORAGE_VOL_DISK_FAT32: + return "fat32"; + case VIR_STORAGE_VOL_DISK_LINUX_SWAP: + return "linux-swap"; + case VIR_STORAGE_VOL_DISK_LINUX_LVM: + return "linux-lvm"; + case VIR_STORAGE_VOL_DISK_LINUX_RAID: + return "linux-raid"; + case VIR_STORAGE_VOL_DISK_EXTENDED: + return "extended"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %d"), format); + return NULL; +} + +static int +virStorageBackendDiskMakeDataVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + virStorageVolDefPtr vol) +{ + char *tmp, *devpath; + + if (vol == NULL) { + if ((vol = calloc(1, sizeof(virStorageVolDef))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + + /* Prepended path will be same for all partitions, so we can + * strip the path to form a reasonable pool-unique name + */ + tmp = strrchr(groups[0], '/'); + if ((vol->name = strdup(tmp ? tmp + 1 : groups[0])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + } + + if (vol->target.path == NULL) { + if ((devpath = strdup(groups[0])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + + /* Now figure out the stable path + * + * XXX this method is O(N) because it scans the pool target + * dir every time its run. Should figure out a more efficient + * way of doing this... + */ + if ((vol->target.path = virStorageBackendStablePath(conn, + pool, + devpath)) == NULL) + return -1; + + if (devpath != vol->target.path) + free(devpath); + devpath = NULL; + } + + if (vol->key == NULL) { + /* XXX base off a unique key of the underlying disk */ + if ((vol->key = strdup(vol->target.path)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + } + + if (vol->source.extents == NULL) { + if ((vol->source.extents = + calloc(1, sizeof(*(vol->source.extents)))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("volume extents")); + return -1; + } + vol->source.nextent = 1; + + if (virStrToLong_ull(groups[3], NULL, 10, + &vol->source.extents[0].start) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse device start location")); + return -1; + } + + if (virStrToLong_ull(groups[4], NULL, 10, + &vol->source.extents[0].end) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse device end location")); + return -1; + } + + if ((vol->source.extents[0].path = + strdup(pool->def->source.devices[0].path)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("extents")); + return -1; + } + } + + /* Refresh allocation/capacity/perms */ + if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0) + return -1; + + /* The above gets allocation wrong for + * extended partitions, so overwrite it */ + vol->allocation = vol->capacity = + (vol->source.extents[0].end - vol->source.extents[0].start); + + if (STRNEQ(groups[2], "metadata")) + pool->def->allocation += vol->allocation; + if (vol->source.extents[0].end > pool->def->capacity) + pool->def->capacity = vol->source.extents[0].end; + + return 0; +} + +static int +virStorageBackendDiskMakeFreeExtent(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool, + char **const groups) +{ + virStoragePoolSourceDeviceExtentPtr tmp; + virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0]; + + if ((tmp = realloc(dev->freeExtents, + sizeof(*tmp) * (dev->nfreeExtent+1))) == NULL) + return -1; + dev->freeExtents = tmp; + + memset(dev->freeExtents + + dev->nfreeExtent, 0, sizeof(*tmp)); + + if (virStrToLong_ull(groups[3], NULL, 10, + &dev->freeExtents[dev->nfreeExtent].start) < 0) + return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */ + + if (virStrToLong_ull(groups[4], NULL, 10, + &dev->freeExtents[dev->nfreeExtent].end) < 0) + return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */ + + pool->def->available += + (dev->freeExtents[dev->nfreeExtent].end - + dev->freeExtents[dev->nfreeExtent].start); + if (dev->freeExtents[dev->nfreeExtent].end > pool->def->capacity) + pool->def->capacity = dev->freeExtents[dev->nfreeExtent].end; + + dev->nfreeExtent++; + + return 0; +} + + +static int +virStorageBackendDiskMakeVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + size_t ntok ATTRIBUTE_UNUSED, + char **const groups, + void *data) +{ + /* + * Ignore normal+metadata, and logical+metadata partitions + * since they're basically internal book-keeping regions + * we have no control over. Do keep extended+metadata though + * because that's the MS-DOS extended partition region we + * need to be able to view/create/delete + */ + if ((STREQ(groups[1], "normal") || + STREQ(groups[1], "logical")) && + STREQ(groups[2], "metadata")) + return 0; + + /* Remaining data / metdata parts get turn into volumes... */ + if (STREQ(groups[2], "metadata") || + STREQ(groups[2], "data")) { + virStorageVolDefPtr vol = data; + /* We're searching for a specific vol only, so ignore others */ + if (vol && + STRNEQ(vol->name, groups[0])) + return 0; + + return virStorageBackendDiskMakeDataVol(conn, pool, groups, vol); + } else if (STREQ(groups[2], "free")) { + /* ....or free space extents */ + return virStorageBackendDiskMakeFreeExtent(conn, pool, groups); + } else { + /* This codepath should never happen unless someone changed + * libvirt_parthelper forgot to change this code */ + return -1; + } +} + + +/* To get a list of partitions we run an external helper + * tool which then uses parted APIs. This is because + * parted's API is not compatible with libvirt's license + * but we really really want to use parted because the + * other options all suck :-) + * + * All the other storage backends run an external tool for + * listing volumes so this really isn't too much of a pain, + * and we can even ensure the output is friendly. + */ +static int +virStorageBackendDiskReadPartitions(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + + /* + * # libvirt_parthelper DEVICE + * /dev/sda1 normal data 32256 106928128 106896384 + * /dev/sda2 normal data 106928640 100027629568 99920701440 + * - normal metadata 100027630080 100030242304 2612736 + * + */ + const char *prog[] = { + PARTHELPER, pool->def->source.devices[0].path, NULL, + }; + + pool->def->allocation = pool->def->capacity = pool->def->available = 0; + + return virStorageBackendRunProgNul(conn, + pool, + prog, + 6, + virStorageBackendDiskMakeVol, + vol); +} + + +static int +virStorageBackendDiskRefreshPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + free(pool->def->source.devices[0].freeExtents); + pool->def->source.devices[0].nfreeExtent = 0; + pool->def->source.devices[0].freeExtents = NULL; + + return virStorageBackendDiskReadPartitions(conn, pool, NULL); +} + + +/** + * Write a new partition table header + */ +static int +virStorageBackendDiskBuildPool(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + /* eg parted /dev/sda mklabel msdos */ + const char *prog[] = { + PARTED, + pool->def->source.devices[0].path, + "mklabel", + virStorageBackendDiskPoolFormatToString(conn, pool->def->source.format), + NULL, + }; + + if (virRun(conn, (char**)prog, NULL) < 0) + return -1; + + return 0; +} + + +static int +virStorageBackendDiskDeleteVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + unsigned int flags); + +static int +virStorageBackendDiskCreateVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + int i; + char start[100], end[100]; + unsigned long long startOffset, endOffset, smallestSize = 0; + int smallestExtent = -1; + virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0]; + /* XXX customizable partition types */ + const char *cmdargv[] = { + PARTED, + pool->def->source.devices[0].path, + "mkpart", + "--script", + "ext2", + start, + end, + NULL + }; + + for (i = 0 ; i < dev->nfreeExtent ; i++) { + unsigned long long size = + dev->freeExtents[i].end - + dev->freeExtents[i].start; + if (size > vol->allocation && + (smallestSize == 0 || + size < smallestSize)) { + smallestSize = size; + smallestExtent = i; + } + } + if (smallestExtent == -1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("no large enough free extent")); + return -1; + } + startOffset = dev->freeExtents[smallestExtent].start; + endOffset = startOffset + vol->allocation; + + snprintf(start, sizeof(start)-1, "%lluB", startOffset); + start[sizeof(start)-1] = '\0'; + snprintf(end, sizeof(end)-1, "%lluB", endOffset); + end[sizeof(end)-1] = '\0'; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + /* Blow away free extent info, as we're about to re-populate it */ + free(pool->def->source.devices[0].freeExtents); + pool->def->source.devices[0].nfreeExtent = 0; + pool->def->source.devices[0].freeExtents = NULL; + + /* Fetch actual extent info */ + if (virStorageBackendDiskReadPartitions(conn, pool, vol) < 0) + return -1; + + return 0; +} + + +static int +virStorageBackendDiskDeleteVol(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED) +{ + /* delete a partition */ + virStorageReportError(conn, VIR_ERR_NO_SUPPORT, + _("Disk pools are not yet supported")); + return -1; +} + + +virStorageBackend virStorageBackendDisk = { + .type = VIR_STORAGE_POOL_DISK, + + .buildPool = virStorageBackendDiskBuildPool, + .refreshPool = virStorageBackendDiskRefreshPool, + + .createVol = virStorageBackendDiskCreateVol, + .deleteVol = virStorageBackendDiskDeleteVol, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE), + .formatFromString = virStorageBackendDiskPoolFormatFromString, + .formatToString = virStorageBackendDiskPoolFormatToString, + }, + .volOptions = { + .formatFromString = virStorageBackendDiskVolFormatFromString, + .formatToString = virStorageBackendDiskVolFormatToString, + }, + + .volType = VIR_STORAGE_VOL_BLOCK, +}; + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r f54c0eb07193 src/storage_backend_disk.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_disk.h Tue Feb 19 17:43:53 2008 -0500 @@ -0,0 +1,45 @@ +/* + * storage_backend_disk.h: storage backend for disk handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_STORAGE_BACKEND_DISK_H__ +#define __VIR_STORAGE_BACKEND_DISK_H__ + +#include "storage_backend.h" + +extern virStorageBackend virStorageBackendDisk; + +#endif /* __VIR_STORAGE_BACKEND_DISK_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This just stubs out an empty no-op storage driver impl for the test driver. This is basically to prevent the ensure the test suite does not try to contact the daemon for storage APIs, which would cause tests to fail. This needs to be extended to provide a fully-fledged test impl of the storage APIs in the future. It is intended that the test driver be able to leverage the XML parsing & formatting code from the storage_conf.{c,h} files, allowing greater code re-use across the dummy test and real storage drivers. test.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff -r e2a1678e316e src/test.c --- a/src/test.c Thu Feb 07 12:34:16 2008 -0500 +++ b/src/test.c Thu Feb 07 12:34:21 2008 -0500 @@ -2208,6 +2208,22 @@ static int testNetworkSetAutostart(virNe privnet->autostart = autostart ? 1 : 0; RELEASE_NETWORK(); return (0); +} + +static virDrvOpenStatus testStorageOpen(virConnectPtr conn, + xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) { + if (STRNEQ(conn->driver->name, "Test")) + return VIR_DRV_OPEN_DECLINED; + + conn->storagePrivateData = conn->privateData; + return VIR_DRV_OPEN_SUCCESS; +} + +static int testStorageClose(virConnectPtr conn) { + conn->storagePrivateData = NULL; + return 0; } @@ -2301,6 +2317,12 @@ static virNetworkDriver testNetworkDrive }; +static virStorageDriver testStorageDriver = { + .name = "Test", + .open = testStorageOpen, + .close = testStorageClose, +}; + /** * testRegister: * @@ -2312,6 +2334,8 @@ testRegister(void) if (virRegisterDriver(&testDriver) < 0) return -1; if (virRegisterNetworkDriver(&testNetworkDriver) < 0) + return -1; + if (virRegisterStorageDriver(&testStorageDriver) < 0) return -1; return 0; } -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:41:09AM +0000, Daniel P. Berrange wrote:
This just stubs out an empty no-op storage driver impl for the test driver. This is basically to prevent the ensure the test suite does not try to contact the daemon for storage APIs, which would cause tests to fail.
This needs to be extended to provide a fully-fledged test impl of the storage APIs in the future. It is intended that the test driver be able to leverage the XML parsing & formatting code from the storage_conf.{c,h} files, allowing greater code re-use across the dummy test and real storage drivers.
uncontroversial, +1, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

This patch adds a new 'storage.html' page to the website giving examples of the XML format for each pool type, the XML format for volumes, a description of all XML elements, and other storage pool specific information. libvir.html | 576 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ site.xsl | 3 2 files changed, 579 insertions(+) diff -r 70e46e1711e1 docs/libvir.html --- a/docs/libvir.html Fri Feb 08 10:04:07 2008 -0500 +++ b/docs/libvir.html Mon Feb 11 16:28:51 2008 -0500 @@ -3914,5 +3914,581 @@ first appeared in libvirt 0.2.0. </tr> </table> +<h2><a name="Storage" id="Storage">Storage Management</a></h2> + +<p> +This page describes the storage management capabilities in +libvirt. +</p> + +<ul> +<li><a href="#StorageCore">Core concepts</a></li> +<li><a href="#StoragePool">Storage pool XML</a> + <ul> + <li><a href="#StoragePoolFirst">First level elements</a></li> + <li><a href="#StoragePoolSource">Source elements</a></li> + <li><a href="#StoragePoolTarget">Target elements</a></li> + <li><a href="#StoragePoolExtents">Device extents</a></li> + </ul> +</li> +<li><a href="#StorageVol">Storage volume XML</a> + <ul> + <li><a href="#StorageVolFirst">First level elements</a></li> + <li><a href="#StorageVolSource">Source elements</a></li> + <li><a href="#StorageVolTarget">Target elements</a></li> + </ul> +</li> +<li><a href="#StorageBackend">Storage backend drivers</a> + <ul> + <li><a href="#StorageBackendDir">Directory backend</a></li> + <li><a href="#StorageBackendFS">Local filesystem backend</a></li> + <li><a href="#StorageBackendNetFS">Network filesystem backend</a></li> + <li><a href="#StorageBackendLogical">Logical backend</a></li> + <li><a href="#StorageBackendDisk">Disk backend</a></li> + <li><a href="#StorageBackendISCSI">iSCSI backend</a></li> +</ul> + +<h3><a name="StorageCore">Core concepts</a></h3> + +<p> +The storage management APIs are based around 2 core concepts +</p> + +<ol> +<li><strong>Volume</strong> - a single storage volume which can +be assigned to a guest, or used for creating further pools. A +volume is either a block device, a raw file, or a special format +file.</li> +<li><strong>Pool</strong> - provides a means for taking a chunk +of storage and carving it up into volumes. A pool can be used to +manage things such as a physical disk, a NFS server, a iSCSI target, +a host adapter, an LVM group.</li> +</ol> + +<p> +These two concepts are mapped through to two libvirt objects, a +<code>virStorageVolPtr</code> and a <code>virStoragePoolPtr</code>, +each with a collection of APIs for their management. +</p> + + +<h3><a name="StoragePool">Storage pool XML</a></h3> + +<p> +Although all storage pool backends share the same public APIs and +XML format, they have varying levels of capabilities. Some may +allow creation of volumes, others may only allow use of pre-existing +volumes. Some may have constraints on volume size, or placement. +</p> + +<p>The is the top level tag for a storage pool document is 'pool'. It has +a single attribute <code>type</code>, which is one of <code>dir</code>, +<code>fs</code>,<code>netfs</code>,<code>disk</code>,<code>iscsi</code>, +<code>logical</code>. This corresponds to the storage backend drivers +listed further along in this document. +</p> + + +<h4><a name="StoragePoolFirst">First level elements</a></h4> + +<dl> +<dt>name</dt> +<dd>Providing a name for the pool which is unique to the host. +This is mandatory when defining a pool</dd> + +<dt>uuid</dt> +<dd>Providing an identifier for the pool which is globally unique. +This is optional when defining a pool, a UUID will be generated if +omitted</dd> + +<dt>allocation</dt> +<dd>Providing the total storage allocation for the pool. This may +be larger than the sum of the allocation of all volumes due to +metadata overhead. This value is in bytes. This is not applicable +when creating a pool.</dd> + +<dt>capacity</dt> +<dd>Providing the total storage capacity for the pool. Due to +underlying device constraints it may not be possible to use the +full capacity for storage volumes. This value is in bytes. This +is not applicable when creating a pool.</dd> + +<dt>available</dt> +<dd>Providing the free space available for allocating new volums +in the pool. Due to underlying device constraints it may not be +possible to allocate the entire free space to a single volume. +This value is in bytes. This is not applicable when creating a +pool.</dd> + +<dt>source</dt> +<dd>Provides information about the source of the pool, such as +the underlying host devices, or remote server</dd> + +<dt>target</dt> +<dd>Provides information about the representation of the pool +on the local host.</dd> +</dl> + +<h4><a name="StoragePoolSource">Source elements</a></h4> + +<dl> +<dt>device</dt> +<dd>Provides the source for pools backed by physical devices. +May be repeated multiple times depending on backend driver. Contains +a single attribute <code>path</code> which is the fully qualified +path to the block device node.</dd> +<dt>directory</dt> +<dd>Provides the source for pools backed by directories. May +only occur once. Contains a single attribute <code>path</code> +which is the fully qualified path to the block device node.</dd> +<dt>host</dt> +<dd>Provides the source for pools backed by storage from a +remote server. Will be used in combination with a <code>directory</code> +or <code>device</code> element. Contains an attribute <code>name<code> +which is the hostname or IP address of the server. May optionally +contain a <code>port</code> attribute for the protocol specific +port number.</dd> +<dt>format</dt> +<dd>Provides information about the format of the pool. This +contains a single attribute <code>type</code> whose value is +backend specific. This is typically used to indicate filesystem +type, or network filesystem type, or partition table type, or +LVM metadata type. All drivers are required to have a default +value for this, so it is optional.</dd> +</dl> + +<h4><a name="StoragePoolTarget">Target elements</a></h4> + +<dl> +<dt>path</dt> +<dd>Provides the location at which the pool will be mapped into +the local filesystem namespace. For a filesystem/directory based +pool it will be the name of the directory in which volumes will +be created. For device based pools it will tbe directory in which +devices nodes exist. For the latter <code>/dev/</code> may seem +like the logical choice, however, devices nodes there are not +guarenteed stable across reboots, since they are allocated on +demand. It is preferrable to use a stable location such as one +of the <code>/dev/disk/by-{path,id,uuid,label</code> locations. +</dd> +<dt>permissions<dt> +<dd>Provides information about the default permissions to use +when creating volumes. This is currently only useful for directory +or filesystem based pools, where the volumes allocated are simple +files. For pools where the volumes are device nodes, the hotplug +scripts determine permissions. It contains 4 child elements. The +<code>mode</code> element contains the octal permission set. The +<code>owner</code> element contains the numeric user ID. The <code>group</code> +element contains the numeric group ID. The <code>label</code> element +contains the MAC (eg SELinux) label string. +</dd> +</dl> + +<h4><a name="StoragePoolExtents">Device extents</a></h4> + +<p> +If a storage pool exposes information about its underlying +placement / allocation scheme, the <code>device</code> element +within the <code>source</code> element may contain information +about its avilable extents. Some pools have a constraint that +a volume must be allocated entirely within a single constraint +(eg disk partition pools). Thus the extent information allows an +application to determine the maximum possible size for a new +volume +</p> + +<p> +For storage pools supporting extent information, within each +<code>device</code> element there will be zero or more <code>freeExtent</code> +elements. Each of these elements contains two attributes, <code>start</code> +and <code>end</code> which provide the boundaries of the extent on the +device, measured in bytes. +</p> + +<h3><a name="StorageVol">Storage volume XML</a></h3> + +<p> +A storage volume will be either a file or a device node. +</p> + +<h4><a name="StorageVolFirst">First level elements</a></h4> + +<dl> +<dt>name</dt> +<dd>Providing a name for the pool which is unique to the host. +This is mandatory when defining a pool</dd> + +<dt>uuid</dt> +<dd>Providing an identifier for the pool which is globally unique. +This is optional when defining a pool, a UUID will be generated if +omitted</dd> + +<dt>allocation</dt> +<dd>Providing the total storage allocation for the volume. This +may be smaller than the logical capacity if the volume is sparsely +allocated. It may also be larger than the logical capacity if the +volume has substantial metadata overhead. This value is in bytes. +If omitted when creating a volume, the volume will be fully +allocated at time of creation. If set to a value smaller than the +capacity, the pool has the <strong>option</strong> of deciding +to sparsely allocate a volume. It does not have to honour requests +for sparse allocation though.</dd> + +<dt>capacity</dt> +<dd>Providing the logical capacity for the volume. This value is +in bytes. This is compulsory when creating a volume</dd> + +<dt>source</dt> +<dd>Provides information about the underlying storage allocation +of the volume. This may not be available for some pool types.</dd> + +<dt>target</dt> +<dd>Provides information about the representation of the volume +on the local host.</dd> +</dl> + +<h4><a name="StorageVolTarget">Target elements</a></h4> + +<dl> +<dt>path</dt> +<dd>Provides the location at which the pool will be mapped into +the local filesystem namespace. For a filesystem/directory based +pool it will be the name of the directory in which volumes will +be created. For device based pools it will tbe directory in which +devices nodes exist. For the latter <code>/dev/</code> may seem +like the logical choice, however, devices nodes there are not +guarenteed stable across reboots, since they are allocated on +demand. It is preferrable to use a stable location such as one +of the <code>/dev/disk/by-{path,id,uuid,label</code> locations. +</dd> +<dt>format</dt> +<dd>Provides information about the pool specific volume format. +For disk pools it will provide the partition type. For filesystem +or directory pools it will provide the file format type, eg cow, +qcow, vmdk, raw. If omitted when creating a volume, the pool's +default format will be used. The actual format is specified via +the <code>type</code>. Consult the pool-specific docs for the +list of valid values.</dd> +<dt>permissions<dt> +<dd>Provides information about the default permissions to use +when creating volumes. This is currently only useful for directory +or filesystem based pools, where the volumes allocated are simple +files. For pools where the volumes are device nodes, the hotplug +scripts determine permissions. It contains 4 child elements. The +<code>mode</code> element contains the octal permission set. The +<code>owner</code> element contains the numeric user ID. The <code>group</code> +element contains the numeric group ID. The <code>label</code> element +contains the MAC (eg SELinux) label string. +</dd> +</dl> + + + +<h3><a name="StorageBackend">Storage backend drivers</a></h3> + +<p> +This section illustrates the capabilities / format for each of +the different backend storage pool drivers +</p> + +<h4><a name="StorageBackendDir">Directory pool</a></h4> + +<p> +A pool with a type of <code>dir</code> provides the means to manage +files within a directory. The files can be fully allocated raw files, +sparsely allocated raw files, or one of the special disk formats +such as <code>qcow</code>,<code>qcow2</code>,<code>vmdk</code>, +<code>cow</code>, etc as supported by the <code>qemu-img</code> +program. If the directory does not exist at the time the pool is +defined, the <code>build</code> operation can be used to create it. +</p> + +<h5>Example pool input definition</h5> + +<pre> +<pool type="dir"> + <name>virtimages</name> + <target> + <path>/var/lib/virt/images</path> + </target> +</pool> +</pre> + +<h5>Valid pool format types</h5> + +<p> +The directory pool does not use the pool format type element. +</p> + +<h5>Valid volume format types</h5> + +<p> +One of the following options: +</p> + +<ul> +<li><code>raw</code>: a plain file</li> +<li><code>bochs</code>: Bochs disk image format</li> +<li><code>cloop</code>: compressed loopback disk image format</li> +<li><code>cow</code>: User Mode Linux disk image format</li> +<li><code>dmg</code>: Mac disk image format</li> +<li><code>iso</code>: CDROM disk image format</li> +<li><code>qcow</code>: QEMU v1 disk image format</li> +<li><code>qcow2</code>: QEMU v2 disk image format</li> +<li><code>vmdk</code>: VMWare disk image format</li> +<li><code>vpc</code>: VirtualPC disk image format</li> +</ul> + +<p> +When listing existing volumes all these formats are supported +natively. When creating new volumes, only a subset may be +available. The <code>raw</code> type is guarenteed always +available. The <code>qcow2</code> type can be created if +either <code>qemu-img</code> or <code>qcow-create</code> tools +are present. The others are dependant on support of the +<code>qemu-img</code> tool. + +<h4><a name="StorageBackendFS">Filesystem pool</a></h4> + +<p> +This is a variant of the directory pool. Instead of creating a +directory on an existing mounted filesystem though, it expects +a source block device to be named. This block device will be +mounted and files managed in the directory of its mount point. +It will default to allowing the kernel to automatically discover +the filesystem type, though it can be specified manually if +required. +</p> + +<h5>Example pool input</h5> + +<pre> +<pool type="fs"> + <name>virtimages</name> + <source> + <device path="/dev/VolGroup00/VirtImages"/> + </source> + <target> + <path>/var/lib/virt/images</path> + </target> +</pool> +</pre> + +<h5>Valid pool format types</h5> + +<p> +The fileystem pool supports the following formats: +</p> + +<ul> +<li><code>auto</code> - automatically determine format</li> +<li><code>ext2</code></li> +<li><code>ext3</code></li> +<li><code>ext4</code></li> +<li><code>ufs</code></li> +<li><code>iso9660</code></li> +<li><code>udf</code></li> +<li><code>gfs</code></li> +<li><code>gfs2</code></li> +<li><code>vfat</code></li> +<li><code>hfs+</code></li> +<li><code>xfs</code></li> +</ul> + +<h5>Valid volume format types</h5> + +<p> +The valid volume types are the same as for the <code>directory</code> +pool type. +</p> + +<h4><a name="StorageBackendNetFS">Network filesystem pool</a></h4> + +<p> +This is a variant of the filesystem pool. Instead of requiring +a local block device as the source, it requires the name of a +host and path of an exported directory. It will mount this network +filesystem and manage files within the directory of its mount +point. It will default to using NFS as the protocol. +</p> + +<h5>Example pool input</h5> + +<pre> +<pool type="netfs"> + <name>virtimages</name> + <source> + <host name="nfs.example.com"/> + <dir path="/var/lib/virt/images"/> + </source> + <target> + <path>/var/lib/virt/images</path> + </target> +</pool> +</pre> + +<h5>Valid pool format types</h5> + +<p> +The network fileystem pool supports the following formats: +</p> + +<ul> +<li><code>auto</code> - automatically determine format</li> +<li><code>nfs</code></li> +</ul> + +<h5>Valid volume format types</h5> + +<p> +The valid volume types are the same as for the <code>directory</code> +pool type. +</p> + +<h4><a name="StorageBackendLogical">Logical volume pools</a></h4> + +<p> +This provides a pool based on an LVM volume group. For a +pre-defined LVM volume group, simply providing the group +name is sufficient, while to build a new group requires +providing a list of source devices to serve as physical +volumes. Volumes will be allocated by carving out chunks +of storage from the volume group. +</p> + +<h5>Example pool input</h5> + +<pre> +<pool type="logical"> + <name>HostVG</name> + <source> + <device path="/dev/sda1"/> + <device path="/dev/sdb1"/> + <device path="/dev/sdc1"/> + </source> + <target> + <path>/dev/HostVG</path> + </target> +</pool> +</pre> + +<h5>Valid pool format types</h5> + +<p> +The logical volume pool does not use the pool format type element. +</p> + +<h5>Valid volume format types</h5> + +<p> +The logical volume pool does not use the volume format type element. +</p> + + +<h4><a name="StorageBackendDisk">Disk volume pools</a></h4> + +<p> +This provides a pool based on a physical disk. Volumes are created +by adding partitions to the disk. Disk pools are have constraints +on the size and placement of volumes. The 'free extents' +information will detail the regions which are available for creating +new volumes. A volume cannot span across 2 different free extents. +</p> + +<h5>Example pool input</h5> + +<pre> +<pool type="disk"> + <name>sda</name> + <source> + <device path='/dev/sda'/> + </source> + <target> + <path>/dev</path> + </target> +</pool> +</pre> + +<h5>Valid pool format types</h5> + +<p> +The disk volume pool accepts the following pool format types, representing +the common partition table types: +</p> + +<ul> +<li><code>dos</code></li> +<li><code>dvh</code></li> +<li><code>gpt</code></li> +<li><code>mac</code></li> +<li><code>bsd</code></li> +<li><code>pc98</code></li> +<li><code>sun</code></li> +</ul> + +<p> +The <code>dos</code> or <code>gpt</code> formats are recommended for +best portability - the latter is needed for disks larger than 2TB. +</p> + +<h5>Valid volume format types</h5> + +<p> +The disk volume pool accepts the following volume format types, representing +the common partition entry types: +</p> + +<ul> +<li><code>none</code></li> +<li><code>linux</code></li> +<li><code>fat16</code></li> +<li><code>fat32</code></li> +<li><code>linux-swap</code></li> +<li><code>linux-lvm</code></li> +<li><code>linux-raid</code></li> +<li><code>extended</code></li> +</ul> + + +<h4><a name="StorageBackendISCSI">iSCSI volume pools</a></h4> + +<p> +This provides a pool based on an iSCSI target. Volumes must be +pre-allocated on the iSCSI server, and cannot be created via +the libvirt APIs. Since /dev/XXX names may change each time libvirt +logs into the iSCSI target, it is recommended to configure the pool +to use <code>/dev/disk/by-path</code> or <code>/dev/disk/by-id</code> +for the target path. These provide persistent stable naming for LUNs +</p> + +<h5>Example pool input</h5> + +<pre> +<pool type="iscsi"> + <name>virtimages</name> + <source> + <host name="iscsi.example.com"/> + <device path="demo-target"/> + </source> + <target> + <path>/dev/disk/by-path</path> + </target> +</pool> +</pre> + +<h5>Valid pool format types</h5> + +<p> +The logical volume pool does not use the pool format type element. +</p> + +<h5>Valid volume format types</h5> + +<p> +The logical volume pool does not use the volume format type element. +</p> + + + </body> </html> diff -r 70e46e1711e1 docs/site.xsl --- a/docs/site.xsl Fri Feb 08 10:04:07 2008 -0500 +++ b/docs/site.xsl Mon Feb 11 16:28:51 2008 -0500 @@ -65,6 +65,9 @@ </xsl:when> <xsl:when test="$name = '#Windows'"> <xsl:text>windows.html</xsl:text> + </xsl:when> + <xsl:when test="$name = '#Storage'"> + <xsl:text>storage.html</xsl:text> </xsl:when> <xsl:when test="$name = ''"> <xsl:text>unknown.html</xsl:text> -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:42:10AM +0000, Daniel P. Berrange wrote:
This patch adds a new 'storage.html' page to the website giving examples of the XML format for each pool type, the XML format for volumes, a description of all XML elements, and other storage pool specific information.
Good, we can probably enahnce it a bit, but let's commit what we have +1 Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

DB> The patch series that follows provides storage management APIs for dealing DB> with local directories, local filesystems, remote filesystems, logical DB> volumes (LVM), disk partitioning, and iSCSI. It will also soon support DB> SCSI host adapters (including NPIV). What about a directory of directories? For something like containers, you want to bind-mount a directory on the host system into the container's space. It seems reasonable to have a directory of, for example, copies of var for each of your container. For example: /var/lib/containers/var/{mycontA,mycontB}/{lib,spool,cache,...} ^^^ ^^^^^^^ ^^^^^^^ | | | Pool ----------------+ | | Volume ---------------------+ | Volume -----------------------------+ I haven't read anything in my skimming that leads me to believe that anything in the infrastructure you're adding would prevent such an implementation down the road, but I just thought I'd bring it up. -- Dan Smith IBM Linux Technology Center Open Hypervisor Team email: danms@us.ibm.com

On Tue, Feb 12, 2008 at 10:50:35AM -0800, Dan Smith wrote:
DB> The patch series that follows provides storage management APIs for dealing DB> with local directories, local filesystems, remote filesystems, logical DB> volumes (LVM), disk partitioning, and iSCSI. It will also soon support DB> SCSI host adapters (including NPIV).
What about a directory of directories? For something like containers, you want to bind-mount a directory on the host system into the container's space. It seems reasonable to have a directory of, for example, copies of var for each of your container. For example:
/var/lib/containers/var/{mycontA,mycontB}/{lib,spool,cache,...} ^^^ ^^^^^^^ ^^^^^^^ | | | Pool ----------------+ | | Volume ---------------------+ | Volume -----------------------------+
I haven't read anything in my skimming that leads me to believe that anything in the infrastructure you're adding would prevent such an implementation down the road, but I just thought I'd bring it up.
Well there's a couple of ways to approach this. * Have a general purpose file/directory manipulation API * Treat a directory as just another type of file 'format' The 2nd would be easy to do - in the volume XML format we could have support like this: <volume> <name>foo</name> <target> <format type="dir"/> </target> </volume> Which would result in $pool-target-dir/$vol-name. This works fine if you only need to be able to create the top level directory for the container filesystem. The question is how you end up populating the filesystem for the container. I don't have any answer for that, but I really rather want to avoid turning libvirt into a general file management API. If we want that it would be separate from the storage management APIs anyway. Perhaps simply the ability to 'clone' an existing directory (populated from source external soure, or off NFS) would be sufficient for populating containers ? Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

DB> Which would result in $pool-target-dir/$vol-name. This works fine DB> if you only need to be able to create the top level directory for DB> the container filesystem. Seems reasonable to me. DB> I really rather want to avoid turning libvirt into a general file DB> management API. Agreed. DB> Perhaps simply the ability to 'clone' an existing directory DB> (populated from source external soure, or off NFS) would be DB> sufficient for populating containers ? Sounds reasonable to me. :) Maybe Dave L. can chime in here with any thoughts he may have? -- Dan Smith IBM Linux Technology Center Open Hypervisor Team email: danms@us.ibm.com

Dan Smith wrote:
DB> Which would result in $pool-target-dir/$vol-name. This works fine DB> if you only need to be able to create the top level directory for DB> the container filesystem.
Seems reasonable to me.
DB> I really rather want to avoid turning libvirt into a general file DB> management API.
Agreed.
DB> Perhaps simply the ability to 'clone' an existing directory DB> (populated from source external soure, or off NFS) would be DB> sufficient for populating containers ?
Sounds reasonable to me. :)
So, if I understand correctly, the user would specify a directory containing an image of the entire file system they want for the container. This would be clone'd and setup so that it appears as / in the container filesystem?
Maybe Dave L. can chime in here with any thoughts he may have?
------------------------------------------------------------------------
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- Best Regards, Dave Leskovec IBM Linux Technology Center Open Virtualization

DL> So, if I understand correctly, the user would specify a directory DL> containing an image of the entire file system they want for the DL> container. This would be clone'd and setup so that it appears as DL> / in the container filesystem? No, they would just be directories that could be used for overlays (like in the case of sharing / with the parent, but overlaying a different /var). I think what Dan is suggesting is a way to automatically populate a new var overlay when the user creates an "image" of the type "directory" through libvirt. They would say "create me a new var, based on this other existing var over here". -- Dan Smith IBM Linux Technology Center Open Hypervisor Team email: danms@us.ibm.com

On Tue, Feb 12, 2008 at 01:29:55PM -0800, Dan Smith wrote:
DL> So, if I understand correctly, the user would specify a directory DL> containing an image of the entire file system they want for the DL> container. This would be clone'd and setup so that it appears as DL> / in the container filesystem?
No, they would just be directories that could be used for overlays (like in the case of sharing / with the parent, but overlaying a different /var).
I think what Dan is suggesting is a way to automatically populate a new var overlay when the user creates an "image" of the type "directory" through libvirt. They would say "create me a new var, based on this other existing var over here".
BTW, I'd like to not have to implement this :-) It is just the conceptual model that would fit it with the storage API design... Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Daniel P. Berrange wrote:
On Tue, Feb 12, 2008 at 01:29:55PM -0800, Dan Smith wrote:
DL> So, if I understand correctly, the user would specify a directory DL> containing an image of the entire file system they want for the DL> container. This would be clone'd and setup so that it appears as DL> / in the container filesystem?
No, they would just be directories that could be used for overlays (like in the case of sharing / with the parent, but overlaying a different /var).
I think what Dan is suggesting is a way to automatically populate a new var overlay when the user creates an "image" of the type "directory" through libvirt. They would say "create me a new var, based on this other existing var over here".
BTW, I'd like to not have to implement this :-) It is just the conceptual model that would fit it with the storage API design...
Dan.
:-) It would eliminate the need for mounts. Does it make more sense to integrate into the storage API design or leave the separate container specific mounts? -- Best Regards, Dave Leskovec IBM Linux Technology Center Open Virtualization

DL> It would eliminate the need for mounts. Why? DL> Does it make more sense to integrate into the storage API design DL> or leave the separate container specific mounts? From the perspective of a CIM provider, being able to correlate the storage used by any set of domains (be them virtual machines or a containers) to each other is important. Even if you can't provision* an overlay directory with libvirt (in the way that this API lets you provision an LV), being able to model the existence of one is important. I don't think this will change the XML of a container, but it will give us a way to associate the path provided for a mount to a storage pool. [*] provisioning in the containers case would be a recursive directory copy of a template overlay directory, which is what Dan was saying he didn't want to do -- Dan Smith IBM Linux Technology Center Open Hypervisor Team email: danms@us.ibm.com

On Tue, Feb 12, 2008 at 02:13:59PM -0800, Dan Smith wrote:
DL> It would eliminate the need for mounts.
Why?
DL> Does it make more sense to integrate into the storage API design DL> or leave the separate container specific mounts?
From the perspective of a CIM provider, being able to correlate the storage used by any set of domains (be them virtual machines or a containers) to each other is important.
Even if you can't provision* an overlay directory with libvirt (in the way that this API lets you provision an LV), being able to model the existence of one is important.
I don't think this will change the XML of a container, but it will give us a way to associate the path provided for a mount to a storage pool.
[*] provisioning in the containers case would be a recursive directory copy of a template overlay directory, which is what Dan was saying he didn't want to do
Yep, sorry I didn't make that clearer. There's 3 levels of increasing functionality we can do 1. Ability to create / view / associate the top level directory 2. Ability to populate the directory from some source 3. General purpose file management APIs Less is more. We can trivially do step 1, but I'll punt on the others unless we have clear compelling need from applications. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Ok, I understand now. Thanks! Daniel P. Berrange wrote:
On Tue, Feb 12, 2008 at 02:13:59PM -0800, Dan Smith wrote:
DL> It would eliminate the need for mounts.
Why?
DL> Does it make more sense to integrate into the storage API design DL> or leave the separate container specific mounts?
From the perspective of a CIM provider, being able to correlate the storage used by any set of domains (be them virtual machines or a containers) to each other is important.
Even if you can't provision* an overlay directory with libvirt (in the way that this API lets you provision an LV), being able to model the existence of one is important.
I don't think this will change the XML of a container, but it will give us a way to associate the path provided for a mount to a storage pool.
[*] provisioning in the containers case would be a recursive directory copy of a template overlay directory, which is what Dan was saying he didn't want to do
Yep, sorry I didn't make that clearer. There's 3 levels of increasing functionality we can do
1. Ability to create / view / associate the top level directory 2. Ability to populate the directory from some source 3. General purpose file management APIs
Less is more. We can trivially do step 1, but I'll punt on the others unless we have clear compelling need from applications.
Dan.
-- Best Regards, Dave Leskovec IBM Linux Technology Center Open Virtualization

On Tue, Feb 12, 2008 at 04:28:29AM +0000, Daniel P. Berrange wrote:
The patch series that follows provides storage management APIs for dealing with local directories, local filesystems, remote filesystems, logical volumes (LVM), disk partitioning, and iSCSI. It will also soon support SCSI host adapters (including NPIV).
I'll include comments inline with each patch. The final patch in the series also provides a 'storage.html' page for the website giving a fairly friendly overview of the storage pool types and their associated XML examples.
Although included here, the one API i really don't like is the one for discovery virConnectDiscoverStoragePools, since its API is not flexible enough in terms of metadata it accepts. I will probably just leave this out next time around, since its not critical for the core functionality and can thus be added later.
Also TBD is a way to format a filesystem on a volume, and how to clone an existing volume, and take a snapshot. These things can all be added at a later date.
Finally it does not include the async job support. This is also best added once the core code is merged.
In general I like the approach 'let's put what we consider fine first and discuss other parts later' . I guess it's the third round of review and I really find easier to comment based on existing stuff than big patches that gets iterated over time. So let's isolate what we consider okay and not needing further discussion, push them now, than in an upcoming fourth iteration, thanks, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

Daniel Veillard <veillard@redhat.com> wrote: ...
In general I like the approach 'let's put what we consider fine first and discuss other parts later' . I guess it's the third round of review and I really find easier to comment based on existing stuff than big patches that gets iterated over time. So let's isolate what we consider okay and not needing further discussion, push them now, than in an upcoming fourth iteration,
Yes, monolithic patches are hard to manage, and it's not effective to re-review from scratch a 10,000-line diff when the incremental diff is say just 1/10 the size. For that reason, I've been pulling from Dan's hg patch queue on an irregular basis, and importing the result into a git repository, putting each "pulled batch" on a new branch. Then, I can compare one batch to another with a simple git diff t7..t8 If only I had pulled a little more frequently over the last week or two. My final incremental is summarized like this: $ git diff --shortstat t8..t9-public 74 files changed, 2156 insertions(+), 9861 deletions(-)

On Thu, Feb 14, 2008 at 01:27:18PM +0100, Jim Meyering wrote:
Daniel Veillard <veillard@redhat.com> wrote: ...
In general I like the approach 'let's put what we consider fine first and discuss other parts later' . I guess it's the third round of review and I really find easier to comment based on existing stuff than big patches that gets iterated over time. So let's isolate what we consider okay and not needing further discussion, push them now, than in an upcoming fourth iteration,
Yes, monolithic patches are hard to manage, and it's not effective to re-review from scratch a 10,000-line diff when the incremental diff is say just 1/10 the size.
For that reason, I've been pulling from Dan's hg patch queue on an irregular basis, and importing the result into a git repository, putting each "pulled batch" on a new branch. Then, I can compare one batch to another with a simple
git diff t7..t8
If only I had pulled a little more frequently over the last week or two. My final incremental is summarized like this:
$ git diff --shortstat t8..t9-public 74 files changed, 2156 insertions(+), 9861 deletions(-)
FYI, the main changes that you'll have in that diff are - Adding of the virStoragePoolBuild & virStoragePoolDelete methods - Removal of the singleton pool (eg, 'loop' and 'volgrp' drivers) - LVM pool renamed from 'logvol' to 'logical' - More work on the 'disk' pool creation code. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Feb 14, 2008 at 05:51:13AM -0500, Daniel Veillard wrote:
On Tue, Feb 12, 2008 at 04:28:29AM +0000, Daniel P. Berrange wrote:
The patch series that follows provides storage management APIs for dealing with local directories, local filesystems, remote filesystems, logical volumes (LVM), disk partitioning, and iSCSI. It will also soon support SCSI host adapters (including NPIV).
I'll include comments inline with each patch. The final patch in the series also provides a 'storage.html' page for the website giving a fairly friendly overview of the storage pool types and their associated XML examples.
Although included here, the one API i really don't like is the one for discovery virConnectDiscoverStoragePools, since its API is not flexible enough in terms of metadata it accepts. I will probably just leave this out next time around, since its not critical for the core functionality and can thus be added later.
Also TBD is a way to format a filesystem on a volume, and how to clone an existing volume, and take a snapshot. These things can all be added at a later date.
Finally it does not include the async job support. This is also best added once the core code is merged.
In general I like the approach 'let's put what we consider fine first and discuss other parts later' . I guess it's the third round of review and I really find easier to comment based on existing stuff than big patches that gets iterated over time. So let's isolate what we consider okay and not needing further discussion, push them now, than in an upcoming fourth iteration,
With the exception of the virConnectDiscoverSToragePools api I'm happy for any of this to be committed - so people just ACK what you want to see in CVS & I'll make it so... Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Feb 12, 2008 at 04:28:29AM +0000, Daniel P. Berrange wrote:
The patch series that follows provides storage management APIs for dealing with local directories, local filesystems, remote filesystems, logical volumes (LVM), disk partitioning, and iSCSI. It will also soon support SCSI host adapters (including NPIV).
I'll include comments inline with each patch. The final patch in the series also provides a 'storage.html' page for the website giving a fairly friendly overview of the storage pool types and their associated XML examples.
Although included here, the one API i really don't like is the one for discovery virConnectDiscoverStoragePools, since its API is not flexible enough in terms of metadata it accepts. I will probably just leave this out next time around, since its not critical for the core functionality and can thus be added later.
Also TBD is a way to format a filesystem on a volume, and how to clone an existing volume, and take a snapshot. These things can all be added at a later date.
Finally it does not include the async job support. This is also best added once the core code is merged.
I've just sent out updates of the patch series, which I believe includs all the bug fixes & suggestions raised in this thread - there were alot of replies so there's a possibly i've missed one or two comments, but most should be there. The storage_{driver,conf,backend} and all backends have been re-formated to keep under 80 columns, with only a handful of exceptions where I didn't want to wrap static constant strings. All the new strings are marked for translations so make syntax-check passes. Format strings on virReportError are now correct - gcc annotation picked up a few bugs. The inclusion of bare 'errno' is mostly removed. And all the other coments... The patches should apply cleanly to today's CVS. For convenience you can grab them all directly from http://hg.berrange.com/libraries/libvirt--storage NB, only the patches prefixed with 'storage-XXXX' are submitted here. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
participants (8)
-
Chris Lalancette
-
Dan Smith
-
Daniel P. Berrange
-
Daniel Veillard
-
Dave Leskovec
-
Jim Meyering
-
Mark McLoughlin
-
Richard W.M. Jones