mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-29 00:55:22 +00:00
13284a6b83
In near future the storage pool object lock will be released during startPool and buildPool callback (in some backends). But this means that another thread may acquire the pool object lock and change its definition rendering the former thread access not only stale definition but also access freed memory (virStoragePoolObjAssignDef() will free old def when setting a new one). One way out of this would be to have the pool appear as active because our code deals with obj->def and obj->newdef just fine. But we can't declare a pool as active if it's not started or still building up. Therefore, have a boolean flag that is very similar and forces virStoragePoolObjAssignDef() to store new definition in obj->newdef even for an inactive pool. In turn, we have to move the definition to correct place when unsetting the flag. But that's as easy as calling virStoragePoolUpdateInactive(). Technically speaking, change made to storageDriverAutostartCallback() is not needed because until storage driver is initialized no storage API can run therefore there can't be anyone wanting to change the pool's definition. But I'm doing the change there for consistency anyways. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
2118 lines
56 KiB
C
2118 lines
56 KiB
C
/*
|
|
* virstorageobj.c: internal storage pool and volume objects handling
|
|
* (derived from storage_conf.c)
|
|
*
|
|
* 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, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <dirent.h>
|
|
|
|
#include "datatypes.h"
|
|
#include "node_device_conf.h"
|
|
#include "node_device_util.h"
|
|
#include "virstorageobj.h"
|
|
|
|
#include "viralloc.h"
|
|
#include "virerror.h"
|
|
#include "virfile.h"
|
|
#include "virhash.h"
|
|
#include "virlog.h"
|
|
#include "virscsihost.h"
|
|
#include "virstring.h"
|
|
#include "virvhba.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
|
|
|
VIR_LOG_INIT("conf.virstorageobj");
|
|
|
|
static virClassPtr virStoragePoolObjClass;
|
|
static virClassPtr virStoragePoolObjListClass;
|
|
static virClassPtr virStorageVolObjClass;
|
|
static virClassPtr virStorageVolObjListClass;
|
|
|
|
static void
|
|
virStoragePoolObjDispose(void *opaque);
|
|
static void
|
|
virStoragePoolObjListDispose(void *opaque);
|
|
static void
|
|
virStorageVolObjDispose(void *opaque);
|
|
static void
|
|
virStorageVolObjListDispose(void *opaque);
|
|
|
|
|
|
|
|
typedef struct _virStorageVolObj virStorageVolObj;
|
|
typedef virStorageVolObj *virStorageVolObjPtr;
|
|
struct _virStorageVolObj {
|
|
virObjectLockable parent;
|
|
|
|
virStorageVolDefPtr voldef;
|
|
};
|
|
|
|
typedef struct _virStorageVolObjList virStorageVolObjList;
|
|
typedef virStorageVolObjList *virStorageVolObjListPtr;
|
|
struct _virStorageVolObjList {
|
|
virObjectRWLockable parent;
|
|
|
|
/* key string -> virStorageVolObj mapping
|
|
* for (1), lockless lookup-by-key */
|
|
virHashTable *objsKey;
|
|
|
|
/* name string -> virStorageVolObj mapping
|
|
* for (1), lockless lookup-by-name */
|
|
virHashTable *objsName;
|
|
|
|
/* path string -> virStorageVolObj mapping
|
|
* for (1), lockless lookup-by-path */
|
|
virHashTable *objsPath;
|
|
};
|
|
|
|
struct _virStoragePoolObj {
|
|
virObjectLockable parent;
|
|
|
|
char *configFile;
|
|
char *autostartLink;
|
|
bool active;
|
|
bool starting;
|
|
bool autostart;
|
|
unsigned int asyncjobs;
|
|
|
|
virStoragePoolDefPtr def;
|
|
virStoragePoolDefPtr newDef;
|
|
|
|
virStorageVolObjListPtr volumes;
|
|
};
|
|
|
|
struct _virStoragePoolObjList {
|
|
virObjectRWLockable parent;
|
|
|
|
/* uuid string -> virStoragePoolObj mapping
|
|
* for (1), lockless lookup-by-uuid */
|
|
virHashTable *objs;
|
|
|
|
/* name string -> virStoragePoolObj mapping
|
|
* for (1), lockless lookup-by-name */
|
|
virHashTable *objsName;
|
|
};
|
|
|
|
|
|
static int
|
|
virStorageVolObjOnceInit(void)
|
|
{
|
|
if (!VIR_CLASS_NEW(virStorageVolObj, virClassForObjectLockable()))
|
|
return -1;
|
|
|
|
if (!VIR_CLASS_NEW(virStorageVolObjList, virClassForObjectRWLockable()))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virStorageVolObj);
|
|
|
|
|
|
static virStorageVolObjPtr
|
|
virStorageVolObjNew(void)
|
|
{
|
|
virStorageVolObjPtr obj;
|
|
|
|
if (virStorageVolObjInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(obj = virObjectLockableNew(virStorageVolObjClass)))
|
|
return NULL;
|
|
|
|
virObjectLock(obj);
|
|
return obj;
|
|
}
|
|
|
|
|
|
static void
|
|
virStorageVolObjEndAPI(virStorageVolObjPtr *obj)
|
|
{
|
|
if (!*obj)
|
|
return;
|
|
|
|
virObjectUnlock(*obj);
|
|
virObjectUnref(*obj);
|
|
*obj = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
virStorageVolObjDispose(void *opaque)
|
|
{
|
|
virStorageVolObjPtr obj = opaque;
|
|
|
|
virStorageVolDefFree(obj->voldef);
|
|
}
|
|
|
|
|
|
static virStorageVolObjListPtr
|
|
virStorageVolObjListNew(void)
|
|
{
|
|
virStorageVolObjListPtr vols;
|
|
|
|
if (virStorageVolObjInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(vols = virObjectRWLockableNew(virStorageVolObjListClass)))
|
|
return NULL;
|
|
|
|
if (!(vols->objsKey = virHashCreate(10, virObjectFreeHashData)) ||
|
|
!(vols->objsName = virHashCreate(10, virObjectFreeHashData)) ||
|
|
!(vols->objsPath = virHashCreate(10, virObjectFreeHashData))) {
|
|
virObjectUnref(vols);
|
|
return NULL;
|
|
}
|
|
|
|
return vols;
|
|
}
|
|
|
|
|
|
static void
|
|
virStorageVolObjListDispose(void *opaque)
|
|
{
|
|
virStorageVolObjListPtr vols = opaque;
|
|
|
|
virHashFree(vols->objsKey);
|
|
virHashFree(vols->objsName);
|
|
virHashFree(vols->objsPath);
|
|
}
|
|
|
|
|
|
static int
|
|
virStoragePoolObjOnceInit(void)
|
|
{
|
|
if (!VIR_CLASS_NEW(virStoragePoolObj, virClassForObjectLockable()))
|
|
return -1;
|
|
|
|
if (!VIR_CLASS_NEW(virStoragePoolObjList, virClassForObjectRWLockable()))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virStoragePoolObj);
|
|
|
|
|
|
virStoragePoolObjPtr
|
|
virStoragePoolObjNew(void)
|
|
{
|
|
virStoragePoolObjPtr obj;
|
|
|
|
if (virStoragePoolObjInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(obj = virObjectLockableNew(virStoragePoolObjClass)))
|
|
return NULL;
|
|
|
|
if (!(obj->volumes = virStorageVolObjListNew())) {
|
|
virObjectUnref(obj);
|
|
return NULL;
|
|
}
|
|
|
|
virObjectLock(obj);
|
|
obj->active = false;
|
|
return obj;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjEndAPI(virStoragePoolObjPtr *obj)
|
|
{
|
|
if (!*obj)
|
|
return;
|
|
|
|
virObjectUnlock(*obj);
|
|
virObjectUnref(*obj);
|
|
*obj = NULL;
|
|
}
|
|
|
|
|
|
virStoragePoolDefPtr
|
|
virStoragePoolObjGetDef(virStoragePoolObjPtr obj)
|
|
{
|
|
return obj->def;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjSetDef(virStoragePoolObjPtr obj,
|
|
virStoragePoolDefPtr def)
|
|
{
|
|
virStoragePoolDefFree(obj->def);
|
|
obj->def = def;
|
|
}
|
|
|
|
|
|
virStoragePoolDefPtr
|
|
virStoragePoolObjGetNewDef(virStoragePoolObjPtr obj)
|
|
{
|
|
return obj->newDef;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjDefUseNewDef(virStoragePoolObjPtr obj)
|
|
{
|
|
virStoragePoolDefFree(obj->def);
|
|
obj->def = obj->newDef;
|
|
obj->newDef = NULL;
|
|
}
|
|
|
|
|
|
const char *
|
|
virStoragePoolObjGetConfigFile(virStoragePoolObjPtr obj)
|
|
{
|
|
return obj->configFile;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjSetConfigFile(virStoragePoolObjPtr obj,
|
|
char *configFile)
|
|
{
|
|
VIR_FREE(obj->configFile);
|
|
obj->configFile = configFile;
|
|
}
|
|
|
|
|
|
const char *
|
|
virStoragePoolObjGetAutostartLink(virStoragePoolObjPtr obj)
|
|
{
|
|
return obj->autostartLink;
|
|
}
|
|
|
|
|
|
bool
|
|
virStoragePoolObjIsActive(virStoragePoolObjPtr obj)
|
|
{
|
|
return obj->active;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjSetActive(virStoragePoolObjPtr obj,
|
|
bool active)
|
|
{
|
|
obj->active = active;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjSetStarting(virStoragePoolObjPtr obj,
|
|
bool starting)
|
|
{
|
|
obj->starting = starting;
|
|
}
|
|
|
|
|
|
bool
|
|
virStoragePoolObjIsStarting(virStoragePoolObjPtr obj)
|
|
{
|
|
return obj->starting;
|
|
}
|
|
|
|
|
|
bool
|
|
virStoragePoolObjIsAutostart(virStoragePoolObjPtr obj)
|
|
{
|
|
if (!obj->configFile)
|
|
return 0;
|
|
|
|
return obj->autostart;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjSetAutostart(virStoragePoolObjPtr obj,
|
|
bool autostart)
|
|
{
|
|
obj->autostart = autostart;
|
|
}
|
|
|
|
|
|
unsigned int
|
|
virStoragePoolObjGetAsyncjobs(virStoragePoolObjPtr obj)
|
|
{
|
|
return obj->asyncjobs;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjIncrAsyncjobs(virStoragePoolObjPtr obj)
|
|
{
|
|
obj->asyncjobs++;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjDecrAsyncjobs(virStoragePoolObjPtr obj)
|
|
{
|
|
obj->asyncjobs--;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjDispose(void *opaque)
|
|
{
|
|
virStoragePoolObjPtr obj = opaque;
|
|
|
|
virStoragePoolObjClearVols(obj);
|
|
virObjectUnref(obj->volumes);
|
|
|
|
virStoragePoolDefFree(obj->def);
|
|
virStoragePoolDefFree(obj->newDef);
|
|
|
|
VIR_FREE(obj->configFile);
|
|
VIR_FREE(obj->autostartLink);
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjListDispose(void *opaque)
|
|
{
|
|
virStoragePoolObjListPtr pools = opaque;
|
|
|
|
virHashFree(pools->objs);
|
|
virHashFree(pools->objsName);
|
|
}
|
|
|
|
|
|
virStoragePoolObjListPtr
|
|
virStoragePoolObjListNew(void)
|
|
{
|
|
virStoragePoolObjListPtr pools;
|
|
|
|
if (virStoragePoolObjInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(pools = virObjectRWLockableNew(virStoragePoolObjListClass)))
|
|
return NULL;
|
|
|
|
if (!(pools->objs = virHashCreate(20, virObjectFreeHashData)) ||
|
|
!(pools->objsName = virHashCreate(20, virObjectFreeHashData))) {
|
|
virObjectUnref(pools);
|
|
return NULL;
|
|
}
|
|
|
|
return pools;
|
|
}
|
|
|
|
|
|
struct _virStoragePoolObjListForEachData {
|
|
virStoragePoolObjListIterator iter;
|
|
const void *opaque;
|
|
};
|
|
|
|
static int
|
|
virStoragePoolObjListForEachCb(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virStoragePoolObjPtr obj = payload;
|
|
struct _virStoragePoolObjListForEachData *data = opaque;
|
|
|
|
/* Grab a reference so that we don't rely only on references grabbed by
|
|
* hash table earlier. Remember, an iterator can remove object from the
|
|
* hash table. */
|
|
virObjectRef(obj);
|
|
virObjectLock(obj);
|
|
data->iter(obj, data->opaque);
|
|
virStoragePoolObjEndAPI(&obj);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* virStoragePoolObjListForEach
|
|
* @pools: Pointer to pools object
|
|
* @iter: Callback iteration helper
|
|
* @opaque: Opaque data to use as argument to helper
|
|
*
|
|
* For each object in @pools, call the @iter helper using @opaque as
|
|
* an argument. This function doesn't care whether the @iter fails or
|
|
* not as it's being used for Autostart and UpdateAllState callers
|
|
* that want to iterate over all the @pools objects not stopping if
|
|
* one happens to fail.
|
|
*
|
|
* NB: We cannot take the Storage Pool lock here because it's possible
|
|
* that some action as part of Autostart or UpdateAllState will need
|
|
* to modify/destroy a transient pool. Since these paths only occur
|
|
* during periods in which the storageDriverLock is held (Initialization,
|
|
* AutoStart, or Reload) this is OK.
|
|
*/
|
|
void
|
|
virStoragePoolObjListForEach(virStoragePoolObjListPtr pools,
|
|
virStoragePoolObjListIterator iter,
|
|
const void *opaque)
|
|
{
|
|
struct _virStoragePoolObjListForEachData data = { .iter = iter,
|
|
.opaque = opaque };
|
|
|
|
virHashForEach(pools->objs, virStoragePoolObjListForEachCb, &data);
|
|
}
|
|
|
|
|
|
struct _virStoragePoolObjListSearchData {
|
|
virStoragePoolObjListSearcher searcher;
|
|
const void *opaque;
|
|
};
|
|
|
|
|
|
static int
|
|
virStoragePoolObjListSearchCb(const void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
const void *opaque)
|
|
{
|
|
virStoragePoolObjPtr obj = (virStoragePoolObjPtr) payload;
|
|
struct _virStoragePoolObjListSearchData *data =
|
|
(struct _virStoragePoolObjListSearchData *)opaque;
|
|
|
|
virObjectLock(obj);
|
|
if (data->searcher(obj, data->opaque))
|
|
return 1;
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* virStoragePoolObjListSearch
|
|
* @pools: Pointer to pools object
|
|
* @search: Callback searcher helper
|
|
* @opaque: Opaque data to use as argument to helper
|
|
*
|
|
* Search through the @pools objects calling the @search helper using
|
|
* the @opaque data in order to find an object that matches some criteria
|
|
* and return that object locked.
|
|
*
|
|
* Returns a locked and reffed object when found and NULL when not found
|
|
*/
|
|
virStoragePoolObjPtr
|
|
virStoragePoolObjListSearch(virStoragePoolObjListPtr pools,
|
|
virStoragePoolObjListSearcher searcher,
|
|
const void *opaque)
|
|
{
|
|
virStoragePoolObjPtr obj = NULL;
|
|
struct _virStoragePoolObjListSearchData data = { .searcher = searcher,
|
|
.opaque = opaque };
|
|
|
|
virObjectRWLockRead(pools);
|
|
obj = virHashSearch(pools->objs, virStoragePoolObjListSearchCb, &data, NULL);
|
|
virObjectRWUnlock(pools);
|
|
|
|
return virObjectRef(obj);
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjRemove(virStoragePoolObjListPtr pools,
|
|
virStoragePoolObjPtr obj)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
virUUIDFormat(obj->def->uuid, uuidstr);
|
|
virObjectRef(obj);
|
|
virObjectUnlock(obj);
|
|
virObjectRWLockWrite(pools);
|
|
virObjectLock(obj);
|
|
virHashRemoveEntry(pools->objs, uuidstr);
|
|
virHashRemoveEntry(pools->objsName, obj->def->name);
|
|
virObjectUnref(obj);
|
|
virObjectRWUnlock(pools);
|
|
}
|
|
|
|
|
|
static virStoragePoolObjPtr
|
|
virStoragePoolObjFindByUUIDLocked(virStoragePoolObjListPtr pools,
|
|
const unsigned char *uuid)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
virUUIDFormat(uuid, uuidstr);
|
|
|
|
return virObjectRef(virHashLookup(pools->objs, uuidstr));
|
|
}
|
|
|
|
|
|
/**
|
|
* virStoragePoolObjFindByUUID
|
|
* @pools: Storage pool object list pointer
|
|
* @uuid: Storage object uuid to find
|
|
*
|
|
* Lock the @pools and lookup the object by @uuid
|
|
*
|
|
* Returns: Locked and reffed storage pool object or NULL if not found
|
|
*/
|
|
virStoragePoolObjPtr
|
|
virStoragePoolObjFindByUUID(virStoragePoolObjListPtr pools,
|
|
const unsigned char *uuid)
|
|
{
|
|
virStoragePoolObjPtr obj;
|
|
|
|
virObjectRWLockRead(pools);
|
|
obj = virStoragePoolObjFindByUUIDLocked(pools, uuid);
|
|
virObjectRWUnlock(pools);
|
|
if (obj)
|
|
virObjectLock(obj);
|
|
|
|
return obj;
|
|
}
|
|
|
|
|
|
static virStoragePoolObjPtr
|
|
virStoragePoolObjFindByNameLocked(virStoragePoolObjListPtr pools,
|
|
const char *name)
|
|
{
|
|
return virObjectRef(virHashLookup(pools->objsName, name));
|
|
}
|
|
|
|
|
|
/**
|
|
* virStoragePoolObjFindByName
|
|
* @pools: Storage pool object list pointer
|
|
* @name: Storage object name to find
|
|
*
|
|
* Lock the @pools and lookup the object by @name
|
|
*
|
|
* Returns: Locked and reffed storage pool object or NULL if not found
|
|
*/
|
|
virStoragePoolObjPtr
|
|
virStoragePoolObjFindByName(virStoragePoolObjListPtr pools,
|
|
const char *name)
|
|
{
|
|
virStoragePoolObjPtr obj;
|
|
|
|
virObjectRWLockRead(pools);
|
|
obj = virStoragePoolObjFindByNameLocked(pools, name);
|
|
virObjectRWUnlock(pools);
|
|
if (obj)
|
|
virObjectLock(obj);
|
|
|
|
return obj;
|
|
}
|
|
|
|
|
|
static virStoragePoolObjPtr
|
|
virStoragePoolSourceFindDuplicateDevices(virStoragePoolObjPtr obj,
|
|
virStoragePoolDefPtr def)
|
|
{
|
|
size_t i, j;
|
|
|
|
for (i = 0; i < obj->def->source.ndevice; i++) {
|
|
for (j = 0; j < def->source.ndevice; j++) {
|
|
if (STREQ(obj->def->source.devices[i].path, def->source.devices[j].path))
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjClearVols(virStoragePoolObjPtr obj)
|
|
{
|
|
if (!obj->volumes)
|
|
return;
|
|
|
|
virHashRemoveAll(obj->volumes->objsKey);
|
|
virHashRemoveAll(obj->volumes->objsName);
|
|
virHashRemoveAll(obj->volumes->objsPath);
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjAddVol(virStoragePoolObjPtr obj,
|
|
virStorageVolDefPtr voldef)
|
|
{
|
|
virStorageVolObjPtr volobj = NULL;
|
|
virStorageVolObjListPtr volumes = obj->volumes;
|
|
|
|
virObjectRWLockWrite(volumes);
|
|
|
|
if (!(volobj = virStorageVolObjNew()))
|
|
goto error;
|
|
|
|
if (virHashAddEntry(volumes->objsKey, voldef->key, volobj) < 0)
|
|
goto error;
|
|
virObjectRef(volobj);
|
|
|
|
if (virHashAddEntry(volumes->objsName, voldef->name, volobj) < 0) {
|
|
virHashRemoveEntry(volumes->objsKey, voldef->key);
|
|
goto error;
|
|
}
|
|
virObjectRef(volobj);
|
|
|
|
if (virHashAddEntry(volumes->objsPath, voldef->target.path, volobj) < 0) {
|
|
virHashRemoveEntry(volumes->objsKey, voldef->key);
|
|
virHashRemoveEntry(volumes->objsName, voldef->name);
|
|
goto error;
|
|
}
|
|
virObjectRef(volobj);
|
|
|
|
volobj->voldef = voldef;
|
|
virObjectRWUnlock(volumes);
|
|
virStorageVolObjEndAPI(&volobj);
|
|
return 0;
|
|
|
|
error:
|
|
virStorageVolObjEndAPI(&volobj);
|
|
virObjectRWUnlock(volumes);
|
|
return -1;
|
|
}
|
|
|
|
|
|
void
|
|
virStoragePoolObjRemoveVol(virStoragePoolObjPtr obj,
|
|
virStorageVolDefPtr voldef)
|
|
{
|
|
virStorageVolObjListPtr volumes = obj->volumes;
|
|
virStorageVolObjPtr volobj;
|
|
|
|
virObjectRWLockWrite(volumes);
|
|
volobj = virHashLookup(volumes->objsName, voldef->name);
|
|
if (!volobj) {
|
|
VIR_INFO("Cannot find volume '%s' from storage pool '%s'",
|
|
voldef->name, obj->def->name);
|
|
virObjectRWUnlock(volumes);
|
|
return;
|
|
}
|
|
VIR_INFO("Deleting volume '%s' from storage pool '%s'",
|
|
voldef->name, obj->def->name);
|
|
|
|
virObjectRef(volobj);
|
|
virObjectLock(volobj);
|
|
virHashRemoveEntry(volumes->objsKey, voldef->key);
|
|
virHashRemoveEntry(volumes->objsName, voldef->name);
|
|
virHashRemoveEntry(volumes->objsPath, voldef->target.path);
|
|
virStorageVolObjEndAPI(&volobj);
|
|
|
|
virObjectRWUnlock(volumes);
|
|
}
|
|
|
|
|
|
size_t
|
|
virStoragePoolObjGetVolumesCount(virStoragePoolObjPtr obj)
|
|
{
|
|
size_t nbElems;
|
|
|
|
virObjectRWLockRead(obj->volumes);
|
|
nbElems = virHashSize(obj->volumes->objsKey);
|
|
virObjectRWUnlock(obj->volumes);
|
|
|
|
return nbElems;
|
|
}
|
|
|
|
|
|
struct _virStoragePoolObjForEachVolData {
|
|
virStorageVolObjListIterator iter;
|
|
const void *opaque;
|
|
};
|
|
|
|
static int
|
|
virStoragePoolObjForEachVolumeCb(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
void *opaque)
|
|
{
|
|
int ret = 0;
|
|
virStorageVolObjPtr volobj = payload;
|
|
struct _virStoragePoolObjForEachVolData *data = opaque;
|
|
|
|
virObjectLock(volobj);
|
|
if (data->iter(volobj->voldef, data->opaque) < 0)
|
|
ret = -1;
|
|
virObjectUnlock(volobj);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjForEachVolume(virStoragePoolObjPtr obj,
|
|
virStorageVolObjListIterator iter,
|
|
const void *opaque)
|
|
{
|
|
struct _virStoragePoolObjForEachVolData data = {
|
|
.iter = iter, .opaque = opaque };
|
|
|
|
virObjectRWLockRead(obj->volumes);
|
|
virHashForEach(obj->volumes->objsKey, virStoragePoolObjForEachVolumeCb,
|
|
&data);
|
|
virObjectRWUnlock(obj->volumes);
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct _virStoragePoolObjSearchVolData {
|
|
virStorageVolObjListSearcher iter;
|
|
const void *opaque;
|
|
};
|
|
|
|
static int
|
|
virStoragePoolObjSearchVolumeCb(const void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
const void *opaque)
|
|
{
|
|
virStorageVolObjPtr volobj = (virStorageVolObjPtr) payload;
|
|
struct _virStoragePoolObjSearchVolData *data =
|
|
(struct _virStoragePoolObjSearchVolData *) opaque;
|
|
int found = 0;
|
|
|
|
virObjectLock(volobj);
|
|
if (data->iter(volobj->voldef, data->opaque))
|
|
found = 1;
|
|
virObjectUnlock(volobj);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
virStorageVolDefPtr
|
|
virStoragePoolObjSearchVolume(virStoragePoolObjPtr obj,
|
|
virStorageVolObjListSearcher iter,
|
|
const void *opaque)
|
|
{
|
|
virStorageVolObjPtr volobj;
|
|
struct _virStoragePoolObjSearchVolData data = {
|
|
.iter = iter, .opaque = opaque };
|
|
|
|
virObjectRWLockRead(obj->volumes);
|
|
volobj = virHashSearch(obj->volumes->objsKey,
|
|
virStoragePoolObjSearchVolumeCb,
|
|
&data, NULL);
|
|
virObjectRWUnlock(obj->volumes);
|
|
|
|
if (volobj)
|
|
return volobj->voldef;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
virStorageVolDefPtr
|
|
virStorageVolDefFindByKey(virStoragePoolObjPtr obj,
|
|
const char *key)
|
|
{
|
|
virStorageVolObjPtr volobj;
|
|
|
|
virObjectRWLockRead(obj->volumes);
|
|
volobj = virHashLookup(obj->volumes->objsKey, key);
|
|
virObjectRWUnlock(obj->volumes);
|
|
|
|
if (volobj)
|
|
return volobj->voldef;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
virStorageVolDefPtr
|
|
virStorageVolDefFindByPath(virStoragePoolObjPtr obj,
|
|
const char *path)
|
|
{
|
|
virStorageVolObjPtr volobj;
|
|
|
|
virObjectRWLockRead(obj->volumes);
|
|
volobj = virHashLookup(obj->volumes->objsPath, path);
|
|
virObjectRWUnlock(obj->volumes);
|
|
|
|
if (volobj)
|
|
return volobj->voldef;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
virStorageVolDefPtr
|
|
virStorageVolDefFindByName(virStoragePoolObjPtr obj,
|
|
const char *name)
|
|
{
|
|
virStorageVolObjPtr volobj;
|
|
|
|
virObjectRWLockRead(obj->volumes);
|
|
volobj = virHashLookup(obj->volumes->objsName, name);
|
|
virObjectRWUnlock(obj->volumes);
|
|
|
|
if (volobj)
|
|
return volobj->voldef;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
struct _virStorageVolObjCountData {
|
|
virConnectPtr conn;
|
|
virStoragePoolVolumeACLFilter filter;
|
|
virStoragePoolDefPtr pooldef;
|
|
int count;
|
|
};
|
|
|
|
|
|
static int
|
|
virStoragePoolObjNumOfVolumesCb(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virStorageVolObjPtr volobj = payload;
|
|
struct _virStorageVolObjCountData *data = opaque;
|
|
|
|
virObjectLock(volobj);
|
|
|
|
if (data->filter &&
|
|
!data->filter(data->conn, data->pooldef, volobj->voldef))
|
|
goto cleanup;
|
|
|
|
data->count++;
|
|
|
|
cleanup:
|
|
virObjectUnlock(volobj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjNumOfVolumes(virStoragePoolObjPtr obj,
|
|
virConnectPtr conn,
|
|
virStoragePoolVolumeACLFilter filter)
|
|
{
|
|
virStorageVolObjListPtr volumes = obj->volumes;
|
|
struct _virStorageVolObjCountData data = {
|
|
.conn = conn, .filter = filter, .pooldef = obj->def, .count = 0 };
|
|
|
|
virObjectRWLockRead(volumes);
|
|
virHashForEach(volumes->objsName, virStoragePoolObjNumOfVolumesCb, &data);
|
|
virObjectRWUnlock(volumes);
|
|
|
|
return data.count;
|
|
}
|
|
|
|
|
|
struct _virStorageVolObjNameData {
|
|
virConnectPtr conn;
|
|
virStoragePoolVolumeACLFilter filter;
|
|
virStoragePoolDefPtr pooldef;
|
|
bool error;
|
|
int nnames;
|
|
int maxnames;
|
|
char **const names;
|
|
};
|
|
|
|
static int
|
|
virStoragePoolObjVolumeGetNamesCb(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virStorageVolObjPtr volobj = payload;
|
|
struct _virStorageVolObjNameData *data = opaque;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
|
|
if (data->maxnames >= 0 && data->nnames == data->maxnames)
|
|
return 0;
|
|
|
|
virObjectLock(volobj);
|
|
|
|
if (data->filter &&
|
|
!data->filter(data->conn, data->pooldef, volobj->voldef))
|
|
goto cleanup;
|
|
|
|
if (data->names) {
|
|
if (VIR_STRDUP(data->names[data->nnames], volobj->voldef->name) < 0) {
|
|
data->error = true;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
data->nnames++;
|
|
|
|
cleanup:
|
|
virObjectUnlock(volobj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjVolumeGetNames(virStoragePoolObjPtr obj,
|
|
virConnectPtr conn,
|
|
virStoragePoolVolumeACLFilter filter,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
virStorageVolObjListPtr volumes = obj->volumes;
|
|
struct _virStorageVolObjNameData data = {
|
|
.conn = conn, .filter = filter, .pooldef = obj->def, .error = false,
|
|
.nnames = 0, .maxnames = maxnames, .names = names };
|
|
|
|
virObjectRWLockRead(volumes);
|
|
virHashForEach(volumes->objsName, virStoragePoolObjVolumeGetNamesCb, &data);
|
|
virObjectRWUnlock(volumes);
|
|
|
|
if (data.error)
|
|
goto error;
|
|
|
|
return data.nnames;
|
|
|
|
error:
|
|
while (--data.nnames)
|
|
VIR_FREE(data.names[data.nnames]);
|
|
return -1;
|
|
}
|
|
|
|
|
|
typedef struct _virStoragePoolObjVolumeListExportData virStoragePoolObjVolumeListExportData;
|
|
typedef virStoragePoolObjVolumeListExportData *virStoragePoolObjVolumeListExportDataPtr;
|
|
struct _virStoragePoolObjVolumeListExportData {
|
|
virConnectPtr conn;
|
|
virStoragePoolVolumeACLFilter filter;
|
|
virStoragePoolDefPtr pooldef;
|
|
bool error;
|
|
int nvols;
|
|
virStorageVolPtr *vols;
|
|
};
|
|
|
|
static int
|
|
virStoragePoolObjVolumeListExportCallback(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virStorageVolObjPtr volobj = payload;
|
|
virStoragePoolObjVolumeListExportDataPtr data = opaque;
|
|
virStorageVolPtr vol = NULL;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
|
|
virObjectLock(volobj);
|
|
|
|
if (data->filter &&
|
|
!data->filter(data->conn, data->pooldef, volobj->voldef))
|
|
goto cleanup;
|
|
|
|
if (data->vols) {
|
|
if (!(vol = virGetStorageVol(data->conn, data->pooldef->name,
|
|
volobj->voldef->name, volobj->voldef->key,
|
|
NULL, NULL))) {
|
|
data->error = true;
|
|
goto cleanup;
|
|
}
|
|
data->vols[data->nvols] = vol;
|
|
}
|
|
|
|
data->nvols++;
|
|
|
|
cleanup:
|
|
virObjectUnlock(volobj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjVolumeListExport(virConnectPtr conn,
|
|
virStoragePoolObjPtr obj,
|
|
virStorageVolPtr **vols,
|
|
virStoragePoolVolumeACLFilter filter)
|
|
{
|
|
virStorageVolObjListPtr volumes = obj->volumes;
|
|
virStoragePoolObjVolumeListExportData data = {
|
|
.conn = conn, .filter = filter, .pooldef = obj->def, .error = false,
|
|
.nvols = 0, .vols = NULL };
|
|
|
|
virObjectRWLockRead(volumes);
|
|
|
|
if (!vols) {
|
|
int ret = virHashSize(volumes->objsName);
|
|
virObjectRWUnlock(volumes);
|
|
return ret;
|
|
}
|
|
|
|
if (VIR_ALLOC_N(data.vols, virHashSize(volumes->objsName) + 1) < 0) {
|
|
virObjectRWUnlock(volumes);
|
|
return -1;
|
|
}
|
|
|
|
virHashForEach(volumes->objsName, virStoragePoolObjVolumeListExportCallback, &data);
|
|
virObjectRWUnlock(volumes);
|
|
|
|
if (data.error)
|
|
goto error;
|
|
|
|
*vols = data.vols;
|
|
|
|
return data.nvols;
|
|
|
|
error:
|
|
virObjectListFree(data.vols);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* virStoragePoolObjIsDuplicate:
|
|
* @doms : virStoragePoolObjListPtr to search
|
|
* @def : virStoragePoolDefPtr definition of pool to lookup
|
|
* @check_active: If true, ensure that pool is not active
|
|
* @objRet: returned pool object
|
|
*
|
|
* Assumes @pools is locked by caller already.
|
|
*
|
|
* Returns: -1 on error (name/uuid mismatch or check_active failure)
|
|
* 0 if pool is new
|
|
* 1 if pool is a duplicate (name and UUID match)
|
|
*/
|
|
static int
|
|
virStoragePoolObjIsDuplicate(virStoragePoolObjListPtr pools,
|
|
virStoragePoolDefPtr def,
|
|
bool check_active,
|
|
virStoragePoolObjPtr *objRet)
|
|
{
|
|
int ret = -1;
|
|
virStoragePoolObjPtr obj = NULL;
|
|
|
|
/* See if a Pool with matching UUID already exists */
|
|
obj = virStoragePoolObjFindByUUIDLocked(pools, def->uuid);
|
|
if (obj) {
|
|
virObjectLock(obj);
|
|
|
|
/* UUID matches, but if names don't match, refuse it */
|
|
if (STRNEQ(obj->def->name, def->name)) {
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
virUUIDFormat(obj->def->uuid, uuidstr);
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("pool '%s' is already defined with uuid %s"),
|
|
obj->def->name, uuidstr);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (check_active) {
|
|
/* UUID & name match, but if Pool is already active, refuse it */
|
|
if (virStoragePoolObjIsActive(obj)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("pool is already active as '%s'"),
|
|
obj->def->name);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virStoragePoolObjIsStarting(obj)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("pool '%s' is starting up"),
|
|
obj->def->name);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
VIR_STEAL_PTR(*objRet, obj);
|
|
ret = 1;
|
|
} else {
|
|
/* UUID does not match, but if a name matches, refuse it */
|
|
obj = virStoragePoolObjFindByNameLocked(pools, def->name);
|
|
if (obj) {
|
|
virObjectLock(obj);
|
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
virUUIDFormat(obj->def->uuid, uuidstr);
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("pool '%s' already exists with uuid %s"),
|
|
def->name, uuidstr);
|
|
goto cleanup;
|
|
}
|
|
ret = 0;
|
|
}
|
|
|
|
cleanup:
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
getSCSIHostNumber(virStorageAdapterSCSIHostPtr scsi_host,
|
|
unsigned int *hostnum)
|
|
{
|
|
int ret = -1;
|
|
unsigned int num;
|
|
char *name = NULL;
|
|
|
|
if (scsi_host->has_parent) {
|
|
virPCIDeviceAddressPtr addr = &scsi_host->parentaddr;
|
|
unsigned int unique_id = scsi_host->unique_id;
|
|
|
|
if (!(name = virSCSIHostGetNameByParentaddr(addr->domain,
|
|
addr->bus,
|
|
addr->slot,
|
|
addr->function,
|
|
unique_id)))
|
|
goto cleanup;
|
|
if (virSCSIHostGetNumber(name, &num) < 0)
|
|
goto cleanup;
|
|
} else {
|
|
if (virSCSIHostGetNumber(scsi_host->name, &num) < 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
*hostnum = num;
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(name);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static bool
|
|
virStorageIsSameHostnum(const char *name,
|
|
unsigned int scsi_hostnum)
|
|
{
|
|
unsigned int fc_hostnum;
|
|
|
|
if (virSCSIHostGetNumber(name, &fc_hostnum) == 0 &&
|
|
scsi_hostnum == fc_hostnum)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* matchFCHostToSCSIHost:
|
|
*
|
|
* @fchost: fc_host adapter ptr (either def or pool->def)
|
|
* @scsi_hostnum: Already determined "scsi_pool" hostnum
|
|
*
|
|
* Returns true/false whether there is a match between the incoming
|
|
* fc_adapter host# and the scsi_host host#
|
|
*/
|
|
static bool
|
|
matchFCHostToSCSIHost(virStorageAdapterFCHostPtr fchost,
|
|
unsigned int scsi_hostnum)
|
|
{
|
|
virConnectPtr conn = NULL;
|
|
bool ret = false;
|
|
char *name = NULL;
|
|
char *scsi_host_name = NULL;
|
|
char *parent_name = NULL;
|
|
|
|
/* If we have a parent defined, get its hostnum, and compare to the
|
|
* scsi_hostnum. If they are the same, then we have a match
|
|
*/
|
|
if (fchost->parent &&
|
|
virStorageIsSameHostnum(fchost->parent, scsi_hostnum))
|
|
return true;
|
|
|
|
/* If we find an fc adapter name, then either libvirt created a vHBA
|
|
* for this fc_host or a 'virsh nodedev-create' generated a vHBA.
|
|
*/
|
|
if ((name = virVHBAGetHostByWWN(NULL, fchost->wwnn, fchost->wwpn))) {
|
|
|
|
/* Get the scsi_hostN for the vHBA in order to see if it
|
|
* matches our scsi_hostnum
|
|
*/
|
|
if (virStorageIsSameHostnum(name, scsi_hostnum)) {
|
|
ret = true;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* We weren't provided a parent, so we have to query the node
|
|
* device driver in order to ascertain the parent of the vHBA.
|
|
* If the parent fc_hostnum is the same as the scsi_hostnum, we
|
|
* have a match.
|
|
*/
|
|
if (!fchost->parent &&
|
|
(conn = virGetConnectNodeDev())) {
|
|
if (virAsprintf(&scsi_host_name, "scsi_%s", name) < 0)
|
|
goto cleanup;
|
|
if ((parent_name = virNodeDeviceGetParentName(conn,
|
|
scsi_host_name))) {
|
|
if (virStorageIsSameHostnum(parent_name, scsi_hostnum)) {
|
|
ret = true;
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
/* Throw away the error and fall through */
|
|
virResetLastError();
|
|
VIR_DEBUG("Could not determine parent vHBA");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* NB: Lack of a name means that this vHBA hasn't yet been created,
|
|
* which means our scsi_host cannot be using the vHBA. Furthermore,
|
|
* lack of a provided parent means libvirt is going to choose the
|
|
* "best" fc_host capable adapter based on availability. That could
|
|
* conflict with an existing scsi_host definition, but there's no
|
|
* way to know that now.
|
|
*/
|
|
|
|
cleanup:
|
|
VIR_FREE(name);
|
|
VIR_FREE(parent_name);
|
|
VIR_FREE(scsi_host_name);
|
|
virConnectClose(conn);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static bool
|
|
matchSCSIAdapterParent(virStorageAdapterSCSIHostPtr pool_scsi_host,
|
|
virStorageAdapterSCSIHostPtr def_scsi_host)
|
|
{
|
|
virPCIDeviceAddressPtr pooladdr = &pool_scsi_host->parentaddr;
|
|
virPCIDeviceAddressPtr defaddr = &def_scsi_host->parentaddr;
|
|
|
|
if (pooladdr->domain == defaddr->domain &&
|
|
pooladdr->bus == defaddr->bus &&
|
|
pooladdr->slot == defaddr->slot &&
|
|
pooladdr->function == defaddr->function &&
|
|
pool_scsi_host->unique_id == def_scsi_host->unique_id)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
static bool
|
|
virStoragePoolSourceMatchSingleHost(virStoragePoolSourcePtr poolsrc,
|
|
virStoragePoolSourcePtr defsrc)
|
|
{
|
|
if (poolsrc->nhost != 1 && defsrc->nhost != 1)
|
|
return false;
|
|
|
|
if (defsrc->hosts[0].port &&
|
|
poolsrc->hosts[0].port != defsrc->hosts[0].port)
|
|
return false;
|
|
|
|
return STREQ(poolsrc->hosts[0].name, defsrc->hosts[0].name);
|
|
}
|
|
|
|
|
|
static bool
|
|
virStoragePoolSourceISCSIMatch(virStoragePoolObjPtr obj,
|
|
virStoragePoolDefPtr def)
|
|
{
|
|
virStoragePoolSourcePtr poolsrc = &obj->def->source;
|
|
virStoragePoolSourcePtr defsrc = &def->source;
|
|
|
|
/* NB: Do not check the source host name */
|
|
if (STRNEQ_NULLABLE(poolsrc->initiator.iqn, defsrc->initiator.iqn))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static virStoragePoolObjPtr
|
|
virStoragePoolObjSourceMatchTypeDIR(virStoragePoolObjPtr obj,
|
|
virStoragePoolDefPtr def)
|
|
{
|
|
if (obj->def->type == VIR_STORAGE_POOL_DIR) {
|
|
if (STREQ(obj->def->target.path, def->target.path))
|
|
return obj;
|
|
} else if (obj->def->type == VIR_STORAGE_POOL_GLUSTER) {
|
|
if (STREQ(obj->def->source.name, def->source.name) &&
|
|
STREQ_NULLABLE(obj->def->source.dir, def->source.dir) &&
|
|
virStoragePoolSourceMatchSingleHost(&obj->def->source,
|
|
&def->source))
|
|
return obj;
|
|
} else if (obj->def->type == VIR_STORAGE_POOL_NETFS) {
|
|
if (STREQ(obj->def->source.dir, def->source.dir) &&
|
|
virStoragePoolSourceMatchSingleHost(&obj->def->source,
|
|
&def->source))
|
|
return obj;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static virStoragePoolObjPtr
|
|
virStoragePoolObjSourceMatchTypeISCSI(virStoragePoolObjPtr obj,
|
|
virStoragePoolDefPtr def)
|
|
{
|
|
virStorageAdapterPtr pool_adapter = &obj->def->source.adapter;
|
|
virStorageAdapterPtr def_adapter = &def->source.adapter;
|
|
virStorageAdapterSCSIHostPtr pool_scsi_host;
|
|
virStorageAdapterSCSIHostPtr def_scsi_host;
|
|
virStorageAdapterFCHostPtr pool_fchost;
|
|
virStorageAdapterFCHostPtr def_fchost;
|
|
unsigned int pool_hostnum;
|
|
unsigned int def_hostnum;
|
|
unsigned int scsi_hostnum;
|
|
|
|
if (pool_adapter->type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST &&
|
|
def_adapter->type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST) {
|
|
pool_fchost = &pool_adapter->data.fchost;
|
|
def_fchost = &def_adapter->data.fchost;
|
|
|
|
if (STREQ(pool_fchost->wwnn, def_fchost->wwnn) &&
|
|
STREQ(pool_fchost->wwpn, def_fchost->wwpn))
|
|
return obj;
|
|
} else if (pool_adapter->type == VIR_STORAGE_ADAPTER_TYPE_SCSI_HOST &&
|
|
def_adapter->type == VIR_STORAGE_ADAPTER_TYPE_SCSI_HOST) {
|
|
pool_scsi_host = &pool_adapter->data.scsi_host;
|
|
def_scsi_host = &def_adapter->data.scsi_host;
|
|
|
|
if (pool_scsi_host->has_parent &&
|
|
def_scsi_host->has_parent &&
|
|
matchSCSIAdapterParent(pool_scsi_host, def_scsi_host))
|
|
return obj;
|
|
|
|
if (getSCSIHostNumber(pool_scsi_host, &pool_hostnum) < 0 ||
|
|
getSCSIHostNumber(def_scsi_host, &def_hostnum) < 0)
|
|
return NULL;
|
|
if (pool_hostnum == def_hostnum)
|
|
return obj;
|
|
} else if (pool_adapter->type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST &&
|
|
def_adapter->type == VIR_STORAGE_ADAPTER_TYPE_SCSI_HOST) {
|
|
pool_fchost = &pool_adapter->data.fchost;
|
|
def_scsi_host = &def_adapter->data.scsi_host;
|
|
|
|
/* Get the scsi_hostN for the scsi_host source adapter def */
|
|
if (getSCSIHostNumber(def_scsi_host, &scsi_hostnum) < 0)
|
|
return NULL;
|
|
|
|
if (matchFCHostToSCSIHost(pool_fchost, scsi_hostnum))
|
|
return obj;
|
|
|
|
} else if (pool_adapter->type == VIR_STORAGE_ADAPTER_TYPE_SCSI_HOST &&
|
|
def_adapter->type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST) {
|
|
pool_scsi_host = &pool_adapter->data.scsi_host;
|
|
def_fchost = &def_adapter->data.fchost;
|
|
|
|
if (getSCSIHostNumber(pool_scsi_host, &scsi_hostnum) < 0)
|
|
return NULL;
|
|
|
|
if (matchFCHostToSCSIHost(def_fchost, scsi_hostnum))
|
|
return obj;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static virStoragePoolObjPtr
|
|
virStoragePoolObjSourceMatchTypeDEVICE(virStoragePoolObjPtr obj,
|
|
virStoragePoolDefPtr def)
|
|
{
|
|
virStoragePoolObjPtr matchobj = NULL;
|
|
|
|
if (obj->def->type == VIR_STORAGE_POOL_ISCSI) {
|
|
if (def->type != VIR_STORAGE_POOL_ISCSI)
|
|
return NULL;
|
|
|
|
if ((matchobj = virStoragePoolSourceFindDuplicateDevices(obj, def))) {
|
|
if (!virStoragePoolSourceISCSIMatch(matchobj, def))
|
|
return NULL;
|
|
}
|
|
return matchobj;
|
|
}
|
|
|
|
if (def->type == VIR_STORAGE_POOL_ISCSI)
|
|
return NULL;
|
|
|
|
/* VIR_STORAGE_POOL_FS
|
|
* VIR_STORAGE_POOL_LOGICAL
|
|
* VIR_STORAGE_POOL_DISK
|
|
* VIR_STORAGE_POOL_ZFS */
|
|
return virStoragePoolSourceFindDuplicateDevices(obj, def);
|
|
}
|
|
|
|
|
|
struct _virStoragePoolObjFindDuplicateData {
|
|
virStoragePoolDefPtr def;
|
|
};
|
|
|
|
static int
|
|
virStoragePoolObjSourceFindDuplicateCb(const void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
const void *opaque)
|
|
{
|
|
virStoragePoolObjPtr obj = (virStoragePoolObjPtr) payload;
|
|
struct _virStoragePoolObjFindDuplicateData *data =
|
|
(struct _virStoragePoolObjFindDuplicateData *) opaque;
|
|
|
|
/* Don't match against ourself if re-defining existing pool ! */
|
|
if (STREQ(obj->def->name, data->def->name))
|
|
return 0;
|
|
|
|
switch ((virStoragePoolType)obj->def->type) {
|
|
case VIR_STORAGE_POOL_DIR:
|
|
case VIR_STORAGE_POOL_GLUSTER:
|
|
case VIR_STORAGE_POOL_NETFS:
|
|
if (data->def->type == obj->def->type &&
|
|
virStoragePoolObjSourceMatchTypeDIR(obj, data->def))
|
|
return 1;
|
|
break;
|
|
|
|
case VIR_STORAGE_POOL_SCSI:
|
|
if (data->def->type == obj->def->type &&
|
|
virStoragePoolObjSourceMatchTypeISCSI(obj, data->def))
|
|
return 1;
|
|
break;
|
|
|
|
case VIR_STORAGE_POOL_ISCSI:
|
|
case VIR_STORAGE_POOL_ISCSI_DIRECT:
|
|
case VIR_STORAGE_POOL_FS:
|
|
case VIR_STORAGE_POOL_LOGICAL:
|
|
case VIR_STORAGE_POOL_DISK:
|
|
case VIR_STORAGE_POOL_ZFS:
|
|
if ((data->def->type == VIR_STORAGE_POOL_ISCSI ||
|
|
data->def->type == VIR_STORAGE_POOL_ISCSI_DIRECT ||
|
|
data->def->type == VIR_STORAGE_POOL_FS ||
|
|
data->def->type == VIR_STORAGE_POOL_LOGICAL ||
|
|
data->def->type == VIR_STORAGE_POOL_DISK ||
|
|
data->def->type == VIR_STORAGE_POOL_ZFS) &&
|
|
virStoragePoolObjSourceMatchTypeDEVICE(obj, data->def))
|
|
return 1;
|
|
break;
|
|
|
|
case VIR_STORAGE_POOL_SHEEPDOG:
|
|
if (data->def->type == obj->def->type &&
|
|
virStoragePoolSourceMatchSingleHost(&obj->def->source,
|
|
&data->def->source))
|
|
return 1;
|
|
break;
|
|
|
|
case VIR_STORAGE_POOL_MPATH:
|
|
/* Only one mpath pool is valid per host */
|
|
if (data->def->type == obj->def->type)
|
|
return 1;
|
|
break;
|
|
|
|
case VIR_STORAGE_POOL_VSTORAGE:
|
|
if (data->def->type == obj->def->type &&
|
|
STREQ(obj->def->source.name, data->def->source.name))
|
|
return 1;
|
|
break;
|
|
|
|
case VIR_STORAGE_POOL_RBD:
|
|
case VIR_STORAGE_POOL_LAST:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virStoragePoolObjSourceFindDuplicate(virStoragePoolObjListPtr pools,
|
|
virStoragePoolDefPtr def)
|
|
{
|
|
struct _virStoragePoolObjFindDuplicateData data = {.def = def};
|
|
virStoragePoolObjPtr obj = NULL;
|
|
|
|
obj = virHashSearch(pools->objs, virStoragePoolObjSourceFindDuplicateCb,
|
|
&data, NULL);
|
|
|
|
if (obj) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("Storage source conflict with pool: '%s'"),
|
|
obj->def->name);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
virStoragePoolObjAssignDef(virStoragePoolObjPtr obj,
|
|
virStoragePoolDefPtr def,
|
|
unsigned int flags)
|
|
{
|
|
if (virStoragePoolObjIsActive(obj) ||
|
|
virStoragePoolObjIsStarting(obj)) {
|
|
virStoragePoolDefFree(obj->newDef);
|
|
obj->newDef = def;
|
|
} else {
|
|
if (!obj->newDef &&
|
|
flags & VIR_STORAGE_POOL_OBJ_LIST_ADD_LIVE)
|
|
VIR_STEAL_PTR(obj->newDef, obj->def);
|
|
|
|
virStoragePoolDefFree(obj->def);
|
|
obj->def = def;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* virStoragePoolObjListAdd:
|
|
* @pools: Storage Pool object list pointer
|
|
* @def: Storage pool definition to add or update
|
|
* @flags: bitwise-OR of VIR_STORAGE_POOL_OBJ_LIST_* flags
|
|
*
|
|
* Lookup the @def to see if it already exists in the @pools in order
|
|
* to either update or add if it does not exist.
|
|
*
|
|
* Use VIR_STORAGE_POOL_OBJ_LIST_ADD_LIVE to denote that @def
|
|
* refers to an active definition and thus any possible inactive
|
|
* definition found should be saved to ->newDef (in case of
|
|
* future restore).
|
|
*
|
|
* If VIR_STORAGE_POOL_OBJ_LIST_ADD_CHECK_LIVE is set in @flags
|
|
* then this will fail if the pool exists and is active.
|
|
*
|
|
* Returns locked and reffed object pointer or NULL on error
|
|
*/
|
|
virStoragePoolObjPtr
|
|
virStoragePoolObjListAdd(virStoragePoolObjListPtr pools,
|
|
virStoragePoolDefPtr def,
|
|
unsigned int flags)
|
|
{
|
|
virStoragePoolObjPtr obj = NULL;
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
int rc;
|
|
|
|
virObjectRWLockWrite(pools);
|
|
|
|
if (virStoragePoolObjSourceFindDuplicate(pools, def) < 0)
|
|
goto error;
|
|
|
|
rc = virStoragePoolObjIsDuplicate(pools, def,
|
|
!!(flags & VIR_STORAGE_POOL_OBJ_LIST_ADD_CHECK_LIVE),
|
|
&obj);
|
|
|
|
if (rc < 0)
|
|
goto error;
|
|
if (rc > 0) {
|
|
virStoragePoolObjAssignDef(obj, def, flags);
|
|
virObjectRWUnlock(pools);
|
|
return obj;
|
|
}
|
|
|
|
if (!(obj = virStoragePoolObjNew()))
|
|
goto error;
|
|
|
|
virUUIDFormat(def->uuid, uuidstr);
|
|
if (virHashAddEntry(pools->objs, uuidstr, obj) < 0)
|
|
goto error;
|
|
virObjectRef(obj);
|
|
|
|
if (virHashAddEntry(pools->objsName, def->name, obj) < 0) {
|
|
virHashRemoveEntry(pools->objs, uuidstr);
|
|
goto error;
|
|
}
|
|
virObjectRef(obj);
|
|
obj->def = def;
|
|
virObjectRWUnlock(pools);
|
|
return obj;
|
|
|
|
error:
|
|
virStoragePoolObjEndAPI(&obj);
|
|
virObjectRWUnlock(pools);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static virStoragePoolObjPtr
|
|
virStoragePoolObjLoad(virStoragePoolObjListPtr pools,
|
|
const char *file,
|
|
const char *path,
|
|
const char *autostartLink)
|
|
{
|
|
virStoragePoolObjPtr obj;
|
|
VIR_AUTOPTR(virStoragePoolDef) def = NULL;
|
|
|
|
if (!(def = virStoragePoolDefParseFile(path)))
|
|
return NULL;
|
|
|
|
if (!virStringMatchesNameSuffix(file, def->name, ".xml")) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("Storage pool config filename '%s' does "
|
|
"not match pool name '%s'"),
|
|
path, def->name);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(obj = virStoragePoolObjListAdd(pools, def, 0)))
|
|
return NULL;
|
|
def = NULL;
|
|
|
|
VIR_FREE(obj->configFile); /* for driver reload */
|
|
if (VIR_STRDUP(obj->configFile, path) < 0) {
|
|
virStoragePoolObjRemove(pools, obj);
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return NULL;
|
|
}
|
|
VIR_FREE(obj->autostartLink); /* for driver reload */
|
|
if (VIR_STRDUP(obj->autostartLink, autostartLink) < 0) {
|
|
virStoragePoolObjRemove(pools, obj);
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return NULL;
|
|
}
|
|
|
|
obj->autostart = virFileLinkPointsTo(obj->autostartLink,
|
|
obj->configFile);
|
|
|
|
return obj;
|
|
}
|
|
|
|
|
|
static virStoragePoolObjPtr
|
|
virStoragePoolObjLoadState(virStoragePoolObjListPtr pools,
|
|
const char *stateDir,
|
|
const char *name)
|
|
{
|
|
char *stateFile = NULL;
|
|
virStoragePoolObjPtr obj = NULL;
|
|
xmlDocPtr xml = NULL;
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
xmlNodePtr node = NULL;
|
|
VIR_AUTOPTR(virStoragePoolDef) def = NULL;
|
|
|
|
if (!(stateFile = virFileBuildPath(stateDir, name, ".xml")))
|
|
return NULL;
|
|
|
|
if (!(xml = virXMLParseCtxt(stateFile, NULL, _("(pool state)"), &ctxt)))
|
|
goto cleanup;
|
|
|
|
if (!(node = virXPathNode("//pool", ctxt))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not find any 'pool' element in state file"));
|
|
goto cleanup;
|
|
}
|
|
|
|
ctxt->node = node;
|
|
if (!(def = virStoragePoolDefParseXML(ctxt)))
|
|
goto cleanup;
|
|
|
|
if (STRNEQ(name, def->name)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Storage pool state file '%s' does not match "
|
|
"pool name '%s'"),
|
|
stateFile, def->name);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* create the object */
|
|
if (!(obj = virStoragePoolObjListAdd(pools, def,
|
|
VIR_STORAGE_POOL_OBJ_LIST_ADD_CHECK_LIVE)))
|
|
goto cleanup;
|
|
def = NULL;
|
|
|
|
/* XXX: future handling of some additional useful status data,
|
|
* for now, if a status file for a pool exists, the pool will be marked
|
|
* as active
|
|
*/
|
|
|
|
obj->active = true;
|
|
|
|
cleanup:
|
|
VIR_FREE(stateFile);
|
|
xmlFreeDoc(xml);
|
|
xmlXPathFreeContext(ctxt);
|
|
return obj;
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjLoadAllState(virStoragePoolObjListPtr pools,
|
|
const char *stateDir)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *entry;
|
|
int ret = -1;
|
|
int rc;
|
|
|
|
if ((rc = virDirOpenIfExists(&dir, stateDir)) <= 0)
|
|
return rc;
|
|
|
|
while ((ret = virDirRead(dir, &entry, stateDir)) > 0) {
|
|
virStoragePoolObjPtr obj;
|
|
|
|
if (!virStringStripSuffix(entry->d_name, ".xml"))
|
|
continue;
|
|
|
|
if (!(obj = virStoragePoolObjLoadState(pools, stateDir, entry->d_name)))
|
|
continue;
|
|
virStoragePoolObjEndAPI(&obj);
|
|
}
|
|
|
|
VIR_DIR_CLOSE(dir);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjLoadAllConfigs(virStoragePoolObjListPtr pools,
|
|
const char *configDir,
|
|
const char *autostartDir)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *entry;
|
|
int ret;
|
|
int rc;
|
|
|
|
if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0)
|
|
return rc;
|
|
|
|
while ((ret = virDirRead(dir, &entry, configDir)) > 0) {
|
|
char *path;
|
|
char *autostartLink;
|
|
virStoragePoolObjPtr obj;
|
|
|
|
if (!virStringHasSuffix(entry->d_name, ".xml"))
|
|
continue;
|
|
|
|
if (!(path = virFileBuildPath(configDir, entry->d_name, NULL)))
|
|
continue;
|
|
|
|
if (!(autostartLink = virFileBuildPath(autostartDir, entry->d_name,
|
|
NULL))) {
|
|
VIR_FREE(path);
|
|
continue;
|
|
}
|
|
|
|
obj = virStoragePoolObjLoad(pools, entry->d_name, path, autostartLink);
|
|
virStoragePoolObjEndAPI(&obj);
|
|
|
|
VIR_FREE(path);
|
|
VIR_FREE(autostartLink);
|
|
}
|
|
|
|
VIR_DIR_CLOSE(dir);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjSaveDef(virStorageDriverStatePtr driver,
|
|
virStoragePoolObjPtr obj,
|
|
virStoragePoolDefPtr def)
|
|
{
|
|
if (!obj->configFile) {
|
|
if (virFileMakePath(driver->configDir) < 0) {
|
|
virReportSystemError(errno,
|
|
_("cannot create config directory %s"),
|
|
driver->configDir);
|
|
return -1;
|
|
}
|
|
|
|
if (!(obj->configFile = virFileBuildPath(driver->configDir,
|
|
def->name, ".xml"))) {
|
|
return -1;
|
|
}
|
|
|
|
if (!(obj->autostartLink = virFileBuildPath(driver->autostartDir,
|
|
def->name, ".xml"))) {
|
|
VIR_FREE(obj->configFile);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return virStoragePoolSaveConfig(obj->configFile, def);
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjDeleteDef(virStoragePoolObjPtr obj)
|
|
{
|
|
if (!obj->configFile) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("no config file for %s"), obj->def->name);
|
|
return -1;
|
|
}
|
|
|
|
if (unlink(obj->configFile) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot remove config for %s"),
|
|
obj->def->name);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct _virStoragePoolCountData {
|
|
virConnectPtr conn;
|
|
virStoragePoolObjListACLFilter filter;
|
|
bool wantActive;
|
|
int count;
|
|
};
|
|
|
|
|
|
static int
|
|
virStoragePoolObjNumOfStoragePoolsCb(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virStoragePoolObjPtr obj = payload;
|
|
struct _virStoragePoolCountData *data = opaque;
|
|
|
|
virObjectLock(obj);
|
|
|
|
if (data->filter && !data->filter(data->conn, obj->def))
|
|
goto cleanup;
|
|
|
|
if (data->wantActive != virStoragePoolObjIsActive(obj))
|
|
goto cleanup;
|
|
|
|
data->count++;
|
|
|
|
cleanup:
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjNumOfStoragePools(virStoragePoolObjListPtr pools,
|
|
virConnectPtr conn,
|
|
bool wantActive,
|
|
virStoragePoolObjListACLFilter filter)
|
|
{
|
|
struct _virStoragePoolCountData data = {
|
|
.conn = conn, .filter = filter, .wantActive = wantActive, .count = 0 };
|
|
|
|
virObjectRWLockRead(pools);
|
|
virHashForEach(pools->objs, virStoragePoolObjNumOfStoragePoolsCb, &data);
|
|
virObjectRWUnlock(pools);
|
|
|
|
return data.count;
|
|
}
|
|
|
|
|
|
struct _virStoragePoolNameData {
|
|
virConnectPtr conn;
|
|
virStoragePoolObjListACLFilter filter;
|
|
bool wantActive;
|
|
bool error;
|
|
int nnames;
|
|
int maxnames;
|
|
char **const names;
|
|
};
|
|
|
|
|
|
static int
|
|
virStoragePoolObjGetNamesCb(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virStoragePoolObjPtr obj = payload;
|
|
struct _virStoragePoolNameData *data = opaque;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
|
|
if (data->maxnames >= 0 && data->nnames == data->maxnames)
|
|
return 0;
|
|
|
|
virObjectLock(obj);
|
|
|
|
if (data->filter && !data->filter(data->conn, obj->def))
|
|
goto cleanup;
|
|
|
|
if (data->wantActive != virStoragePoolObjIsActive(obj))
|
|
goto cleanup;
|
|
|
|
if (data->names) {
|
|
if (VIR_STRDUP(data->names[data->nnames], obj->def->name) < 0) {
|
|
data->error = true;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
data->nnames++;
|
|
|
|
cleanup:
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjGetNames(virStoragePoolObjListPtr pools,
|
|
virConnectPtr conn,
|
|
bool wantActive,
|
|
virStoragePoolObjListACLFilter filter,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
struct _virStoragePoolNameData data = {
|
|
.conn = conn, .filter = filter, .wantActive = wantActive,
|
|
.error = false, .nnames = 0, .maxnames = maxnames, .names = names };
|
|
|
|
virObjectRWLockRead(pools);
|
|
virHashForEach(pools->objs, virStoragePoolObjGetNamesCb, &data);
|
|
virObjectRWUnlock(pools);
|
|
|
|
if (data.error)
|
|
goto error;
|
|
|
|
return data.nnames;
|
|
|
|
error:
|
|
while (data.nnames)
|
|
VIR_FREE(data.names[--data.nnames]);
|
|
return -1;
|
|
}
|
|
|
|
|
|
#define MATCH(FLAG) (flags & (FLAG))
|
|
static bool
|
|
virStoragePoolObjMatch(virStoragePoolObjPtr obj,
|
|
unsigned int flags)
|
|
{
|
|
/* filter by active state */
|
|
if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) &&
|
|
!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE) &&
|
|
virStoragePoolObjIsActive(obj)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE) &&
|
|
!virStoragePoolObjIsActive(obj))))
|
|
return false;
|
|
|
|
/* filter by persistence */
|
|
if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_PERSISTENT) &&
|
|
!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT) &&
|
|
obj->configFile) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_TRANSIENT) &&
|
|
!obj->configFile)))
|
|
return false;
|
|
|
|
/* filter by autostart option */
|
|
if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_AUTOSTART) &&
|
|
!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_AUTOSTART) &&
|
|
obj->autostart) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_NO_AUTOSTART) &&
|
|
!obj->autostart)))
|
|
return false;
|
|
|
|
/* filter by pool type */
|
|
if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_POOL_TYPE)) {
|
|
if (!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_DIR) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_DIR)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FS) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_FS)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_NETFS) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_NETFS)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_LOGICAL) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_LOGICAL)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_DISK) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_DISK)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_ISCSI)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SCSI) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_SCSI)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_MPATH) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_MPATH)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_RBD) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_RBD)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_SHEEPDOG)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_GLUSTER)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ZFS) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_ZFS)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_VSTORAGE)) ||
|
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI_DIRECT) &&
|
|
(obj->def->type == VIR_STORAGE_POOL_ISCSI_DIRECT))))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#undef MATCH
|
|
|
|
|
|
typedef struct _virStoragePoolObjListExportData virStoragePoolObjListExportData;
|
|
typedef virStoragePoolObjListExportData *virStoragePoolObjListExportDataPtr;
|
|
struct _virStoragePoolObjListExportData {
|
|
virConnectPtr conn;
|
|
virStoragePoolObjListACLFilter filter;
|
|
bool checkActive;
|
|
bool wantActive;
|
|
bool checkMatch;
|
|
unsigned int flags;
|
|
bool error;
|
|
int nPools;
|
|
virStoragePoolPtr *pools;
|
|
};
|
|
|
|
|
|
static int
|
|
virStoragePoolObjListExportCallback(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virStoragePoolObjPtr obj = payload;
|
|
virStoragePoolObjListExportDataPtr data = opaque;
|
|
virStoragePoolPtr pool = NULL;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
|
|
virObjectLock(obj);
|
|
|
|
if (data->filter && !data->filter(data->conn, obj->def))
|
|
goto cleanup;
|
|
|
|
if (!virStoragePoolObjMatch(obj, data->flags))
|
|
goto cleanup;
|
|
|
|
if (data->pools) {
|
|
if (!(pool = virGetStoragePool(data->conn, obj->def->name,
|
|
obj->def->uuid, NULL, NULL))) {
|
|
data->error = true;
|
|
goto cleanup;
|
|
}
|
|
data->pools[data->nPools] = pool;
|
|
}
|
|
|
|
data->nPools++;
|
|
|
|
cleanup:
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virStoragePoolObjListExport(virConnectPtr conn,
|
|
virStoragePoolObjListPtr poolobjs,
|
|
virStoragePoolPtr **pools,
|
|
virStoragePoolObjListFilter filter,
|
|
unsigned int flags)
|
|
{
|
|
virStoragePoolObjListExportData data = {
|
|
.conn = conn, .filter = filter, .flags = flags, .error = false,
|
|
.nPools = 0, .pools = NULL };
|
|
|
|
virObjectRWLockRead(poolobjs);
|
|
|
|
if (pools && VIR_ALLOC_N(data.pools, virHashSize(poolobjs->objs) + 1) < 0)
|
|
goto error;
|
|
|
|
virHashForEach(poolobjs->objs, virStoragePoolObjListExportCallback, &data);
|
|
virObjectRWUnlock(poolobjs);
|
|
|
|
if (data.error)
|
|
goto error;
|
|
|
|
if (data.pools) {
|
|
/* trim the array to the final size */
|
|
ignore_value(VIR_REALLOC_N(data.pools, data.nPools + 1));
|
|
*pools = data.pools;
|
|
}
|
|
|
|
return data.nPools;
|
|
|
|
error:
|
|
virObjectListFree(data.pools);
|
|
return -1;
|
|
}
|