libvirt/src/parallels/parallels_storage.c

1670 lines
46 KiB
C
Raw Normal View History

/*
* parallels_storage.c: core driver functions for managing
* Parallels Cloud Server hosts
*
build: avoid unsafe functions in libgen.h POSIX says that both basename() and dirname() may return static storage (aka they need not be thread-safe); and that they may but not must modify their input argument. Furthermore, <libgen.h> is not available on all platforms. For these reasons, you should never use these functions in a multi-threaded library. Gnulib instead recommends a way to avoid the portability nightmare: gnulib's "dirname.h" provides useful thread-safe counterparts. The obvious dir_name() and base_name() are GPL (because they malloc(), but call exit() on failure) so we can't use them; but the LGPL variants mdir_name() (malloc's or returns NULL) and last_component (always points into the incoming string without modifying it, differing from basename semantics only on corner cases like the empty string that we shouldn't be hitting in the first place) are already in use in libvirt. This finishes the swap over to the safe functions. * cfg.mk (sc_prohibit_libgen): New rule. * src/util/vircgroup.c: Fix offenders. * src/parallels/parallels_storage.c (parallelsPoolAddByDomain): Likewise. * src/parallels/parallels_network.c (parallelsGetBridgedNetInfo): Likewise. * src/node_device/node_device_udev.c (udevProcessSCSIHost) (udevProcessSCSIDevice): Likewise. * src/storage/storage_backend_disk.c (virStorageBackendDiskDeleteVol): Likewise. * src/util/virpci.c (virPCIGetDeviceAddressFromSysfsLink): Likewise. * src/util/virstoragefile.h (_virStorageFileMetadata): Avoid false positive. Signed-off-by: Eric Blake <eblake@redhat.com>
2013-04-25 20:24:42 +00:00
* Copyright (C) 2013 Red Hat, Inc.
* Copyright (C) 2012 Parallels, Inc.
*
* 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 <stdlib.h>
#include <dirent.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "datatypes.h"
build: avoid unsafe functions in libgen.h POSIX says that both basename() and dirname() may return static storage (aka they need not be thread-safe); and that they may but not must modify their input argument. Furthermore, <libgen.h> is not available on all platforms. For these reasons, you should never use these functions in a multi-threaded library. Gnulib instead recommends a way to avoid the portability nightmare: gnulib's "dirname.h" provides useful thread-safe counterparts. The obvious dir_name() and base_name() are GPL (because they malloc(), but call exit() on failure) so we can't use them; but the LGPL variants mdir_name() (malloc's or returns NULL) and last_component (always points into the incoming string without modifying it, differing from basename semantics only on corner cases like the empty string that we shouldn't be hitting in the first place) are already in use in libvirt. This finishes the swap over to the safe functions. * cfg.mk (sc_prohibit_libgen): New rule. * src/util/vircgroup.c: Fix offenders. * src/parallels/parallels_storage.c (parallelsPoolAddByDomain): Likewise. * src/parallels/parallels_network.c (parallelsGetBridgedNetInfo): Likewise. * src/node_device/node_device_udev.c (udevProcessSCSIHost) (udevProcessSCSIDevice): Likewise. * src/storage/storage_backend_disk.c (virStorageBackendDiskDeleteVol): Likewise. * src/util/virpci.c (virPCIGetDeviceAddressFromSysfsLink): Likewise. * src/util/virstoragefile.h (_virStorageFileMetadata): Avoid false positive. Signed-off-by: Eric Blake <eblake@redhat.com>
2013-04-25 20:24:42 +00:00
#include "dirname.h"
2012-12-12 18:06:53 +00:00
#include "viralloc.h"
#include "configmake.h"
#include "virstoragefile.h"
#include "virerror.h"
#include "virfile.h"
#include "parallels_utils.h"
#include "virstring.h"
#define VIR_FROM_THIS VIR_FROM_PARALLELS
#define parallelsPoolNotFoundError(pool_name) \
virReportError(VIR_ERR_INVALID_ARG, \
_("pool '%s' not found"), pool_name);
static virStorageVolDefPtr
parallelsStorageVolDefineXML(virStoragePoolObjPtr pool, const char *xmldesc,
const char *xmlfile, bool is_new);
static virStorageVolPtr
parallelsStorageVolLookupByPath(virConnectPtr conn, const char *path);
static int
parallelsStoragePoolGetAlloc(virStoragePoolDefPtr def);
static void
parallelsStorageLock(virStorageDriverStatePtr driver)
{
virMutexLock(&driver->lock);
}
static void
parallelsStorageUnlock(virStorageDriverStatePtr driver)
{
virMutexUnlock(&driver->lock);
}
static int
parallelsStorageClose(virConnectPtr conn)
{
parallelsConnPtr privconn = conn->privateData;
virStorageDriverStatePtr storageState = conn->storagePrivateData;
conn->storagePrivateData = NULL;
parallelsStorageLock(storageState);
virStoragePoolObjListFree(&privconn->pools);
VIR_FREE(storageState->configDir);
VIR_FREE(storageState->autostartDir);
parallelsStorageUnlock(storageState);
virMutexDestroy(&storageState->lock);
VIR_FREE(storageState);
return 0;
}
static int
parallelsFindVolumes(virStoragePoolObjPtr pool)
{
DIR *dir;
struct dirent *ent;
char *path = NULL;
int ret = -1;
if (!(dir = opendir(pool->def->target.path))) {
virReportSystemError(errno,
_("cannot open path '%s'"),
pool->def->target.path);
return -1;
}
while ((ent = readdir(dir)) != NULL) {
if (!virFileHasSuffix(ent->d_name, ".xml"))
continue;
if (!(path = virFileBuildPath(pool->def->target.path,
ent->d_name, NULL)))
goto cleanup;
if (!parallelsStorageVolDefineXML(pool, NULL, path, false))
goto cleanup;
VIR_FREE(path);
}
ret = 0;
cleanup:
VIR_FREE(path);
closedir(dir);
return ret;
}
/*
* Generate unique pool name by path
*/
static char *parallelsMakePoolName(virConnectPtr conn, const char *path)
{
parallelsConnPtr privconn = conn->privateData;
char *name;
size_t i;
for (i = 0; i < UINT_MAX; i++) {
bool found = false;
size_t j;
if ((!i && VIR_STRDUP(name, path) < 0) ||
(i && virAsprintf(&name, "%s-%zu", path, i) < 0))
return NULL;
for (j = 0; j < strlen(name); j++)
if (name[j] == '/')
name[j] = '-';
for (j = 0; j < privconn->pools.count; j++) {
if (STREQ(name, privconn->pools.objs[j]->def->name)) {
found = true;
break;
}
}
if (!found)
return name;
VIR_FREE(name);
}
return NULL;
}
static virStoragePoolObjPtr
parallelsPoolCreateByPath(virConnectPtr conn, const char *path)
{
parallelsConnPtr privconn = conn->privateData;
virStoragePoolObjListPtr pools = &privconn->pools;
virStoragePoolDefPtr def;
virStoragePoolObjPtr pool = NULL;
if (VIR_ALLOC(def) < 0)
goto error;
if (!(def->name = parallelsMakePoolName(conn, path)))
goto error;
if (virUUIDGenerate(def->uuid)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Can't generate UUID"));
goto error;
}
def->type = VIR_STORAGE_POOL_DIR;
if (VIR_STRDUP(def->target.path, path) < 0)
goto error;
if (!(pool = virStoragePoolObjAssignDef(pools, def)))
goto error;
if (virStoragePoolObjSaveDef(conn->storagePrivateData, pool, def) < 0) {
virStoragePoolObjRemove(pools, pool);
goto error;
}
virStoragePoolObjUnlock(pool);
return pool;
error:
virStoragePoolDefFree(def);
if (pool)
virStoragePoolObjUnlock(pool);
return NULL;
}
/*
* Create pool of type VIR_STORAGE_POOL_DIR with
* path to the VM, if it's not exists.
*/
static virStoragePoolObjPtr
parallelsPoolAddByDomain(virConnectPtr conn, virDomainObjPtr dom)
{
parallelsConnPtr privconn = conn->privateData;
parallelsDomObjPtr pdom = dom->privateData;
virStoragePoolObjListPtr pools = &privconn->pools;
char *poolPath;
virStoragePoolObjPtr pool = NULL;
size_t j;
build: avoid unsafe functions in libgen.h POSIX says that both basename() and dirname() may return static storage (aka they need not be thread-safe); and that they may but not must modify their input argument. Furthermore, <libgen.h> is not available on all platforms. For these reasons, you should never use these functions in a multi-threaded library. Gnulib instead recommends a way to avoid the portability nightmare: gnulib's "dirname.h" provides useful thread-safe counterparts. The obvious dir_name() and base_name() are GPL (because they malloc(), but call exit() on failure) so we can't use them; but the LGPL variants mdir_name() (malloc's or returns NULL) and last_component (always points into the incoming string without modifying it, differing from basename semantics only on corner cases like the empty string that we shouldn't be hitting in the first place) are already in use in libvirt. This finishes the swap over to the safe functions. * cfg.mk (sc_prohibit_libgen): New rule. * src/util/vircgroup.c: Fix offenders. * src/parallels/parallels_storage.c (parallelsPoolAddByDomain): Likewise. * src/parallels/parallels_network.c (parallelsGetBridgedNetInfo): Likewise. * src/node_device/node_device_udev.c (udevProcessSCSIHost) (udevProcessSCSIDevice): Likewise. * src/storage/storage_backend_disk.c (virStorageBackendDiskDeleteVol): Likewise. * src/util/virpci.c (virPCIGetDeviceAddressFromSysfsLink): Likewise. * src/util/virstoragefile.h (_virStorageFileMetadata): Avoid false positive. Signed-off-by: Eric Blake <eblake@redhat.com>
2013-04-25 20:24:42 +00:00
poolPath = mdir_name(pdom->home);
if (!poolPath) {
virReportOOMError();
return NULL;
}
for (j = 0; j < pools->count; j++) {
if (STREQ(poolPath, pools->objs[j]->def->target.path)) {
pool = pools->objs[j];
break;
}
}
if (!pool)
pool = parallelsPoolCreateByPath(conn, poolPath);
VIR_FREE(poolPath);
return pool;
}
static int parallelsDiskDescParseNode(xmlDocPtr xml,
xmlNodePtr root,
virStorageVolDefPtr def)
{
xmlXPathContextPtr ctxt = NULL;
int ret = -1;
if (STRNEQ((const char *)root->name, "Parallels_disk_image")) {
virReportError(VIR_ERR_XML_ERROR,
"%s", _("unknown root element for storage pool"));
goto cleanup;
}
ctxt = xmlXPathNewContext(xml);
if (ctxt == NULL) {
virReportOOMError();
goto cleanup;
}
ctxt->node = root;
if (virXPathULongLong("string(./Disk_Parameters/Disk_size)",
ctxt, &def->capacity) < 0) {
virReportError(VIR_ERR_XML_ERROR,
"%s", _("failed to get disk size from "
"the disk descriptor xml"));
goto cleanup;
}
def->capacity <<= 9;
def->allocation = def->capacity;
ret = 0;
cleanup:
xmlXPathFreeContext(ctxt);
return ret;
}
static int parallelsDiskDescParse(const char *path, virStorageVolDefPtr def)
{
xmlDocPtr xml;
int ret = -1;
if (!(xml = virXMLParse(path, NULL, NULL)))
return -1;
ret = parallelsDiskDescParseNode(xml, xmlDocGetRootElement(xml), def);
xmlFreeDoc(xml);
return ret;
}
static int parallelsAddDiskVolume(virStoragePoolObjPtr pool,
virDomainObjPtr dom,
const char *diskName,
const char *diskPath,
const char *diskDescPath)
{
virStorageVolDefPtr def = NULL;
if (VIR_ALLOC(def))
goto error;
if (virAsprintf(&def->name, "%s-%s", dom->def->name, diskName) < 0)
goto error;
def->type = VIR_STORAGE_VOL_FILE;
if (parallelsDiskDescParse(diskDescPath, def) < 0)
goto error;
if (!(def->target.path = realpath(diskPath, NULL)))
goto no_memory;
if (VIR_STRDUP(def->key, def->target.path) < 0)
goto error;
if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1) < 0)
goto error;
pool->volumes.objs[pool->volumes.count++] = def;
return 0;
no_memory:
virReportOOMError();
error:
virStorageVolDefFree(def);
return -1;
}
static int parallelsFindVmVolumes(virStoragePoolObjPtr pool,
virDomainObjPtr dom)
{
parallelsDomObjPtr pdom = dom->privateData;
DIR *dir;
struct dirent *ent;
char *diskPath = NULL, *diskDescPath = NULL;
struct stat sb;
int ret = -1;
if (!(dir = opendir(pdom->home))) {
virReportSystemError(errno,
_("cannot open path '%s'"),
pdom->home);
goto cleanup;
}
while ((ent = readdir(dir)) != NULL) {
VIR_FREE(diskPath);
VIR_FREE(diskDescPath);
if (!(diskPath = virFileBuildPath(pdom->home, ent->d_name, NULL)))
goto cleanup;
if (lstat(diskPath, &sb) < 0) {
virReportSystemError(errno,
_("cannot stat path '%s'"),
ent->d_name);
goto cleanup;
}
if (!S_ISDIR(sb.st_mode))
continue;
if (!(diskDescPath = virFileBuildPath(diskPath,
"DiskDescriptor", ".xml")))
goto cleanup;
if (!virFileExists(diskDescPath))
continue;
/* here we know, that ent->d_name is a disk image directory */
if (parallelsAddDiskVolume(pool, dom, ent->d_name,
diskPath, diskDescPath))
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(diskPath);
VIR_FREE(diskDescPath);
closedir(dir);
return ret;
}
static int
parallelsPoolsAdd(virDomainObjPtr dom,
void *opaque)
{
virConnectPtr conn = opaque;
virStoragePoolObjPtr pool;
if (!(pool = parallelsPoolAddByDomain(conn, dom)))
return -1;
if (parallelsFindVmVolumes(pool, dom))
return -1;
return 0;
}
static int parallelsLoadPools(virConnectPtr conn)
{
parallelsConnPtr privconn = conn->privateData;
virStorageDriverStatePtr storageState = conn->storagePrivateData;
char *base = NULL;
size_t i;
if (VIR_STRDUP(base, SYSCONFDIR "/libvirt") < 0)
goto error;
/* Configuration path is /etc/libvirt/parallels-storage/... . */
if (virAsprintf(&storageState->configDir,
"%s/parallels-storage", base) == -1)
goto error;
if (virAsprintf(&storageState->autostartDir,
"%s/parallels-storage/autostart", base) == -1)
goto error;
VIR_FREE(base);
if (virStoragePoolLoadAllConfigs(&privconn->pools,
storageState->configDir,
storageState->autostartDir) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed to load pool configs"));
goto error;
}
if (virDomainObjListForEach(privconn->domains, parallelsPoolsAdd, conn) < 0)
goto error;
for (i = 0; i < privconn->pools.count; i++) {
virStoragePoolObjLock(privconn->pools.objs[i]);
virStoragePoolObjPtr pool;
pool = privconn->pools.objs[i];
pool->active = 1;
if (parallelsStoragePoolGetAlloc(pool->def) < 0)
goto error;
if (parallelsFindVolumes(pool) < 0)
goto error;
virStoragePoolObjUnlock(privconn->pools.objs[i]);
}
return 0;
error:
VIR_FREE(base);
return -1;
}
static virDrvOpenStatus
parallelsStorageOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
unsigned int flags)
{
virStorageDriverStatePtr storageState;
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
if (STRNEQ(conn->driver->name, "Parallels"))
return VIR_DRV_OPEN_DECLINED;
if (VIR_ALLOC(storageState) < 0)
return VIR_DRV_OPEN_ERROR;
if (virMutexInit(&storageState->lock) < 0) {
VIR_FREE(storageState);
return VIR_DRV_OPEN_ERROR;
}
conn->storagePrivateData = storageState;
parallelsStorageLock(storageState);
if (parallelsLoadPools(conn))
goto error;
parallelsStorageUnlock(storageState);
return VIR_DRV_OPEN_SUCCESS;
error:
parallelsStorageUnlock(storageState);
parallelsStorageClose(conn);
return -1;
}
static int
parallelsConnectNumOfStoragePools(virConnectPtr conn)
{
parallelsConnPtr privconn = conn->privateData;
int numActive = 0;
size_t i;
parallelsDriverLock(privconn);
for (i = 0; i < privconn->pools.count; i++)
if (virStoragePoolObjIsActive(privconn->pools.objs[i]))
numActive++;
parallelsDriverUnlock(privconn);
return numActive;
}
static int
parallelsConnectListStoragePools(virConnectPtr conn, char **const names, int nnames)
{
parallelsConnPtr privconn = conn->privateData;
int n = 0;
size_t i;
parallelsDriverLock(privconn);
memset(names, 0, sizeof(*names) * nnames);
for (i = 0; i < privconn->pools.count && n < nnames; i++) {
virStoragePoolObjLock(privconn->pools.objs[i]);
if (virStoragePoolObjIsActive(privconn->pools.objs[i]) &&
VIR_STRDUP(names[n++], privconn->pools.objs[i]->def->name) < 0) {
virStoragePoolObjUnlock(privconn->pools.objs[i]);
goto error;
}
virStoragePoolObjUnlock(privconn->pools.objs[i]);
}
parallelsDriverUnlock(privconn);
return n;
error:
for (n = 0; n < nnames; n++)
VIR_FREE(names[n]);
parallelsDriverUnlock(privconn);
return -1;
}
static int
parallelsConnectNumOfDefinedStoragePools(virConnectPtr conn)
{
parallelsConnPtr privconn = conn->privateData;
int numInactive = 0;
size_t i;
parallelsDriverLock(privconn);
for (i = 0; i < privconn->pools.count; i++) {
virStoragePoolObjLock(privconn->pools.objs[i]);
if (!virStoragePoolObjIsActive(privconn->pools.objs[i]))
numInactive++;
virStoragePoolObjUnlock(privconn->pools.objs[i]);
}
parallelsDriverUnlock(privconn);
return numInactive;
}
static int
parallelsConnectListDefinedStoragePools(virConnectPtr conn,
char **const names, int nnames)
{
parallelsConnPtr privconn = conn->privateData;
int n = 0;
size_t i;
parallelsDriverLock(privconn);
memset(names, 0, sizeof(*names) * nnames);
for (i = 0; i < privconn->pools.count && n < nnames; i++) {
virStoragePoolObjLock(privconn->pools.objs[i]);
if (!virStoragePoolObjIsActive(privconn->pools.objs[i]) &&
VIR_STRDUP(names[n++], privconn->pools.objs[i]->def->name) < 0) {
virStoragePoolObjUnlock(privconn->pools.objs[i]);
goto error;
}
virStoragePoolObjUnlock(privconn->pools.objs[i]);
}
parallelsDriverUnlock(privconn);
return n;
error:
for (n = 0; n < nnames; n++)
VIR_FREE(names[n]);
parallelsDriverUnlock(privconn);
return -1;
}
static int
parallelsStoragePoolIsActive(virStoragePoolPtr pool)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
int ret = -1;
parallelsDriverLock(privconn);
obj = virStoragePoolObjFindByUUID(&privconn->pools, pool->uuid);
parallelsDriverUnlock(privconn);
if (!obj) {
virReportError(VIR_ERR_NO_STORAGE_POOL, NULL);
goto cleanup;
}
ret = virStoragePoolObjIsActive(obj);
cleanup:
if (obj)
virStoragePoolObjUnlock(obj);
return ret;
}
static int
parallelsStoragePoolIsPersistent(virStoragePoolPtr pool ATTRIBUTE_UNUSED)
{
return 1;
}
static virStoragePoolPtr
parallelsStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
{
parallelsConnPtr privconn = conn->privateData;
virStoragePoolObjPtr pool;
virStoragePoolPtr ret = NULL;
parallelsDriverLock(privconn);
pool = virStoragePoolObjFindByUUID(&privconn->pools, uuid);
parallelsDriverUnlock(privconn);
if (pool == NULL) {
virReportError(VIR_ERR_NO_STORAGE_POOL, NULL);
goto cleanup;
}
ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid,
NULL, NULL);
cleanup:
if (pool)
virStoragePoolObjUnlock(pool);
return ret;
}
static virStoragePoolPtr
parallelsStoragePoolLookupByName(virConnectPtr conn, const char *name)
{
parallelsConnPtr privconn = conn->privateData;
virStoragePoolObjPtr pool;
virStoragePoolPtr ret = NULL;
parallelsDriverLock(privconn);
pool = virStoragePoolObjFindByName(&privconn->pools, name);
parallelsDriverUnlock(privconn);
if (pool == NULL) {
virReportError(VIR_ERR_NO_STORAGE_POOL, NULL);
goto cleanup;
}
ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid,
NULL, NULL);
cleanup:
if (pool)
virStoragePoolObjUnlock(pool);
return ret;
}
static virStoragePoolPtr
parallelsStoragePoolLookupByVolume(virStorageVolPtr vol)
{
return parallelsStoragePoolLookupByName(vol->conn, vol->pool);
}
/*
* Fill capacity, available and allocation
* fields in pool definition.
*/
static int
parallelsStoragePoolGetAlloc(virStoragePoolDefPtr def)
{
struct statvfs sb;
if (statvfs(def->target.path, &sb) < 0) {
virReportSystemError(errno,
_("cannot statvfs path '%s'"),
def->target.path);
return -1;
}
def->capacity = ((unsigned long long)sb.f_frsize *
(unsigned long long)sb.f_blocks);
def->available = ((unsigned long long)sb.f_bfree *
(unsigned long long)sb.f_frsize);
def->allocation = def->capacity - def->available;
return 0;
}
static virStoragePoolPtr
parallelsStoragePoolDefineXML(virConnectPtr conn,
const char *xml, unsigned int flags)
{
parallelsConnPtr privconn = conn->privateData;
virStoragePoolDefPtr def;
virStoragePoolObjPtr pool = NULL;
virStoragePoolPtr ret = NULL;
virCheckFlags(0, NULL);
parallelsDriverLock(privconn);
if (!(def = virStoragePoolDefParseString(xml)))
goto cleanup;
if (def->type != VIR_STORAGE_POOL_DIR) {
virReportError(VIR_ERR_NO_SUPPORT, "%s",
_("Only local directories are supported"));
goto cleanup;
}
if (virStoragePoolObjIsDuplicate(&privconn->pools, def, 0) < 0)
goto cleanup;
if (virStoragePoolSourceFindDuplicate(&privconn->pools, def) < 0)
goto cleanup;
if (parallelsStoragePoolGetAlloc(def))
goto cleanup;
if (!(pool = virStoragePoolObjAssignDef(&privconn->pools, def)))
goto cleanup;
if (virStoragePoolObjSaveDef(conn->storagePrivateData, pool, def) < 0) {
virStoragePoolObjRemove(&privconn->pools, pool);
def = NULL;
goto cleanup;
}
def = NULL;
if (VIR_STRDUP(pool->configFile, "\0") < 0)
goto cleanup;
ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid,
NULL, NULL);
cleanup:
virStoragePoolDefFree(def);
if (pool)
virStoragePoolObjUnlock(pool);
parallelsDriverUnlock(privconn);
return ret;
}
static int
parallelsStoragePoolUndefine(virStoragePoolPtr pool)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
int ret = -1;
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
if (virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is still active"), pool->name);
goto cleanup;
}
if (virStoragePoolObjDeleteDef(privpool) < 0)
goto cleanup;
VIR_FREE(privpool->configFile);
virStoragePoolObjRemove(&privconn->pools, privpool);
ret = 0;
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
parallelsDriverUnlock(privconn);
return ret;
}
static int
parallelsStoragePoolCreate(virStoragePoolPtr pool, unsigned int flags)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
int ret = -1;
virCheckFlags(0, -1);
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
if (virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is already active"), pool->name);
goto cleanup;
}
privpool->active = 1;
ret = 0;
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static int
parallelsStoragePoolDestroy(virStoragePoolPtr pool)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
int ret = -1;
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->name);
goto cleanup;
}
if (privpool->configFile == NULL) {
virStoragePoolObjRemove(&privconn->pools, privpool);
privpool = NULL;
}
ret = 0;
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
parallelsDriverUnlock(privconn);
return ret;
}
static int
parallelsStoragePoolRefresh(virStoragePoolPtr pool, unsigned int flags)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
int ret = -1;
virCheckFlags(0, -1);
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->name);
goto cleanup;
}
ret = 0;
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static int
parallelsStoragePoolGetInfo(virStoragePoolPtr pool, virStoragePoolInfoPtr info)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
int ret = -1;
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
memset(info, 0, sizeof(virStoragePoolInfo));
if (privpool->active)
info->state = VIR_STORAGE_POOL_RUNNING;
else
info->state = VIR_STORAGE_POOL_INACTIVE;
info->capacity = privpool->def->capacity;
info->allocation = privpool->def->allocation;
info->available = privpool->def->available;
ret = 0;
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static char *
parallelsStoragePoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
char *ret = NULL;
virCheckFlags(0, NULL);
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
ret = virStoragePoolDefFormat(privpool->def);
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static int
parallelsStoragePoolGetAutostart(virStoragePoolPtr pool, int *autostart)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
int ret = -1;
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
if (!privpool->configFile) {
*autostart = 0;
} else {
*autostart = privpool->autostart;
}
ret = 0;
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static int
parallelsStoragePoolSetAutostart(virStoragePoolPtr pool, int autostart)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
int ret = -1;
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
if (!privpool->configFile) {
virReportError(VIR_ERR_INVALID_ARG, "%s", _("pool has no config file"));
goto cleanup;
}
privpool->autostart = (autostart != 0);
ret = 0;
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static int
parallelsStoragePoolNumOfVolumes(virStoragePoolPtr pool)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
int ret = -1;
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->name);
goto cleanup;
}
ret = privpool->volumes.count;
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static int
parallelsStoragePoolListVolumes(virStoragePoolPtr pool,
char **const names, int maxnames)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
int n = 0;
size_t i = 0;
memset(names, 0, maxnames * sizeof(*names));
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto error;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->name);
goto error;
}
for (i = 0; i < privpool->volumes.count && n < maxnames; i++) {
if (VIR_STRDUP(names[n++], privpool->volumes.objs[i]->name) < 0)
goto error;
}
virStoragePoolObjUnlock(privpool);
return n;
error:
for (n = 0; n < maxnames; n++)
VIR_FREE(names[i]);
if (privpool)
virStoragePoolObjUnlock(privpool);
return -1;
}
static virStorageVolPtr
parallelsStorageVolLookupByName(virStoragePoolPtr pool,
const char *name)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
virStorageVolDefPtr privvol;
virStorageVolPtr ret = NULL;
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->name);
goto cleanup;
}
privvol = virStorageVolDefFindByName(privpool, name);
if (!privvol) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"), name);
goto cleanup;
}
ret = virGetStorageVol(pool->conn, privpool->def->name,
privvol->name, privvol->key,
NULL, NULL);
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static virStorageVolPtr
parallelsStorageVolLookupByKey(virConnectPtr conn, const char *key)
{
parallelsConnPtr privconn = conn->privateData;
size_t i;
virStorageVolPtr ret = NULL;
parallelsDriverLock(privconn);
for (i = 0; i < privconn->pools.count; i++) {
virStoragePoolObjLock(privconn->pools.objs[i]);
if (virStoragePoolObjIsActive(privconn->pools.objs[i])) {
virStorageVolDefPtr privvol =
virStorageVolDefFindByKey(privconn->pools.objs[i], key);
if (privvol) {
ret = virGetStorageVol(conn,
privconn->pools.objs[i]->def->name,
privvol->name, privvol->key,
NULL, NULL);
virStoragePoolObjUnlock(privconn->pools.objs[i]);
break;
}
}
virStoragePoolObjUnlock(privconn->pools.objs[i]);
}
parallelsDriverUnlock(privconn);
if (!ret)
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching key '%s'"), key);
return ret;
}
virStorageVolPtr
parallelsStorageVolLookupByPathLocked(virConnectPtr conn, const char *path)
{
parallelsConnPtr privconn = conn->privateData;
size_t i;
virStorageVolPtr ret = NULL;
for (i = 0; i < privconn->pools.count; i++) {
virStoragePoolObjLock(privconn->pools.objs[i]);
if (virStoragePoolObjIsActive(privconn->pools.objs[i])) {
virStorageVolDefPtr privvol =
virStorageVolDefFindByPath(privconn->pools.objs[i], path);
if (privvol) {
ret = virGetStorageVol(conn,
privconn->pools.objs[i]->def->name,
privvol->name, privvol->key,
NULL, NULL);
virStoragePoolObjUnlock(privconn->pools.objs[i]);
break;
}
}
virStoragePoolObjUnlock(privconn->pools.objs[i]);
}
if (!ret)
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching path '%s'"), path);
return ret;
}
static virStorageVolPtr
parallelsStorageVolLookupByPath(virConnectPtr conn, const char *path)
{
parallelsConnPtr privconn = conn->privateData;
virStorageVolPtr ret = NULL;
parallelsDriverLock(privconn);
ret = parallelsStorageVolLookupByPathLocked(conn, path);
parallelsDriverUnlock(privconn);
return ret;
}
static virStorageVolDefPtr
parallelsStorageVolDefineXML(virStoragePoolObjPtr pool,
const char *xmldesc,
const char *xmlfile, bool is_new)
{
virStorageVolDefPtr privvol = NULL;
virStorageVolDefPtr ret = NULL;
char *xml_path = NULL;
if (xmlfile)
privvol = virStorageVolDefParseFile(pool->def, xmlfile);
else
privvol = virStorageVolDefParseString(pool->def, xmldesc);
if (privvol == NULL)
goto cleanup;
if (virStorageVolDefFindByName(pool, privvol->name)) {
virReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("storage vol already exists"));
goto cleanup;
}
if (is_new) {
/* Make sure enough space */
if ((pool->def->allocation + privvol->allocation) >
pool->def->capacity) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Not enough free space in pool for volume '%s'"),
privvol->name);
goto cleanup;
}
}
if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1) < 0)
goto cleanup;
if (virAsprintf(&privvol->target.path, "%s/%s",
pool->def->target.path, privvol->name) < 0)
goto cleanup;
if (VIR_STRDUP(privvol->key, privvol->target.path) < 0)
goto cleanup;
if (is_new) {
xml_path = parallelsAddFileExt(privvol->target.path, ".xml");
if (!xml_path)
goto cleanup;
if (virXMLSaveFile(xml_path, NULL, "volume-create", xmldesc)) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("Can't create file with volume description"));
goto cleanup;
}
pool->def->allocation += privvol->allocation;
pool->def->available = (pool->def->capacity -
pool->def->allocation);
}
pool->volumes.objs[pool->volumes.count++] = privvol;
ret = privvol;
privvol = NULL;
cleanup:
virStorageVolDefFree(privvol);
VIR_FREE(xml_path);
return ret;
}
static virStorageVolPtr
parallelsStorageVolCreateXML(virStoragePoolPtr pool,
const char *xmldesc, unsigned int flags)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
virStorageVolPtr ret = NULL;
virStorageVolDefPtr privvol = NULL;
virCheckFlags(0, NULL);
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->name);
goto cleanup;
}
privvol = parallelsStorageVolDefineXML(privpool, xmldesc, NULL, true);
if (!privvol)
goto cleanup;
ret = virGetStorageVol(pool->conn, privpool->def->name,
privvol->name, privvol->key,
NULL, NULL);
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static virStorageVolPtr
parallelsStorageVolCreateXMLFrom(virStoragePoolPtr pool,
const char *xmldesc,
virStorageVolPtr clonevol,
unsigned int flags)
{
parallelsConnPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr privpool;
virStorageVolDefPtr privvol = NULL, origvol = NULL;
virStorageVolPtr ret = NULL;
virCheckFlags(0, NULL);
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(pool->name);
goto cleanup;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->name);
goto cleanup;
}
privvol = virStorageVolDefParseString(privpool->def, xmldesc);
if (privvol == NULL)
goto cleanup;
if (virStorageVolDefFindByName(privpool, privvol->name)) {
virReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("storage vol already exists"));
goto cleanup;
}
origvol = virStorageVolDefFindByName(privpool, clonevol->name);
if (!origvol) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"),
clonevol->name);
goto cleanup;
}
/* Make sure enough space */
if ((privpool->def->allocation + privvol->allocation) >
privpool->def->capacity) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Not enough free space in pool for volume '%s'"),
privvol->name);
goto cleanup;
}
privpool->def->available = (privpool->def->capacity -
privpool->def->allocation);
if (VIR_REALLOC_N(privpool->volumes.objs,
privpool->volumes.count + 1) < 0)
goto cleanup;
if (virAsprintf(&privvol->target.path, "%s/%s",
privpool->def->target.path, privvol->name) == -1)
goto cleanup;
if (VIR_STRDUP(privvol->key, privvol->target.path) < 0)
goto cleanup;
privpool->def->allocation += privvol->allocation;
privpool->def->available = (privpool->def->capacity -
privpool->def->allocation);
privpool->volumes.objs[privpool->volumes.count++] = privvol;
ret = virGetStorageVol(pool->conn, privpool->def->name,
privvol->name, privvol->key,
NULL, NULL);
privvol = NULL;
cleanup:
virStorageVolDefFree(privvol);
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
int parallelsStorageVolDefRemove(virStoragePoolObjPtr privpool,
virStorageVolDefPtr privvol)
{
int ret = -1;
char *xml_path = NULL;
size_t i;
privpool->def->allocation -= privvol->allocation;
privpool->def->available = (privpool->def->capacity -
privpool->def->allocation);
for (i = 0; i < privpool->volumes.count; i++) {
if (privpool->volumes.objs[i] == privvol) {
xml_path = parallelsAddFileExt(privvol->target.path, ".xml");
if (!xml_path)
goto cleanup;
if (unlink(xml_path)) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("Can't remove file '%s'"), xml_path);
goto cleanup;
}
virStorageVolDefFree(privvol);
if (i < (privpool->volumes.count - 1))
memmove(privpool->volumes.objs + i,
privpool->volumes.objs + i + 1,
sizeof(*(privpool->volumes.objs)) *
(privpool->volumes.count - (i + 1)));
if (VIR_REALLOC_N(privpool->volumes.objs,
privpool->volumes.count - 1) < 0) {
; /* Failure to reduce memory allocation isn't fatal */
}
privpool->volumes.count--;
break;
}
}
ret = 0;
cleanup:
VIR_FREE(xml_path);
return ret;
}
static int
parallelsStorageVolDelete(virStorageVolPtr vol, unsigned int flags)
{
parallelsConnPtr privconn = vol->conn->privateData;
virStoragePoolObjPtr privpool;
virStorageVolDefPtr privvol;
int ret = -1;
virCheckFlags(0, -1);
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(vol->pool);
goto cleanup;
}
privvol = virStorageVolDefFindByName(privpool, vol->name);
if (privvol == NULL) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"), vol->name);
goto cleanup;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), vol->pool);
goto cleanup;
}
if (parallelsStorageVolDefRemove(privpool, privvol))
goto cleanup;
ret = 0;
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static int
parallelsStorageVolTypeForPool(int pooltype)
{
switch (pooltype) {
case VIR_STORAGE_POOL_DIR:
case VIR_STORAGE_POOL_FS:
case VIR_STORAGE_POOL_NETFS:
return VIR_STORAGE_VOL_FILE;
default:
return VIR_STORAGE_VOL_BLOCK;
}
}
static int
parallelsStorageVolGetInfo(virStorageVolPtr vol, virStorageVolInfoPtr info)
{
parallelsConnPtr privconn = vol->conn->privateData;
virStoragePoolObjPtr privpool;
virStorageVolDefPtr privvol;
int ret = -1;
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(vol->pool);
goto cleanup;
}
privvol = virStorageVolDefFindByName(privpool, vol->name);
if (privvol == NULL) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"), vol->name);
goto cleanup;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), vol->pool);
goto cleanup;
}
memset(info, 0, sizeof(*info));
info->type = parallelsStorageVolTypeForPool(privpool->def->type);
info->capacity = privvol->capacity;
info->allocation = privvol->allocation;
ret = 0;
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static char *
parallelsStorageVolGetXMLDesc(virStorageVolPtr vol, unsigned int flags)
{
parallelsConnPtr privconn = vol->conn->privateData;
virStoragePoolObjPtr privpool;
virStorageVolDefPtr privvol;
char *ret = NULL;
virCheckFlags(0, NULL);
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(vol->pool);
goto cleanup;
}
privvol = virStorageVolDefFindByName(privpool, vol->name);
if (privvol == NULL) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"), vol->name);
goto cleanup;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), vol->pool);
goto cleanup;
}
ret = virStorageVolDefFormat(privpool->def, privvol);
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static char *
parallelsStorageVolGetPath(virStorageVolPtr vol)
{
parallelsConnPtr privconn = vol->conn->privateData;
virStoragePoolObjPtr privpool;
virStorageVolDefPtr privvol;
char *ret = NULL;
parallelsDriverLock(privconn);
privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool);
parallelsDriverUnlock(privconn);
if (privpool == NULL) {
parallelsPoolNotFoundError(vol->pool);
goto cleanup;
}
privvol = virStorageVolDefFindByName(privpool, vol->name);
if (privvol == NULL) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"), vol->name);
goto cleanup;
}
if (!virStoragePoolObjIsActive(privpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), vol->pool);
goto cleanup;
}
ignore_value(VIR_STRDUP(ret, privvol->target.path));
cleanup:
if (privpool)
virStoragePoolObjUnlock(privpool);
return ret;
}
static virStorageDriver parallelsStorageDriver = {
.name = "Parallels",
.storageOpen = parallelsStorageOpen, /* 0.10.0 */
.storageClose = parallelsStorageClose, /* 0.10.0 */
.connectNumOfStoragePools = parallelsConnectNumOfStoragePools, /* 0.10.0 */
.connectListStoragePools = parallelsConnectListStoragePools, /* 0.10.0 */
.connectNumOfDefinedStoragePools = parallelsConnectNumOfDefinedStoragePools, /* 0.10.0 */
.connectListDefinedStoragePools = parallelsConnectListDefinedStoragePools, /* 0.10.0 */
.storagePoolLookupByName = parallelsStoragePoolLookupByName, /* 0.10.0 */
.storagePoolLookupByUUID = parallelsStoragePoolLookupByUUID, /* 0.10.0 */
.storagePoolLookupByVolume = parallelsStoragePoolLookupByVolume, /* 0.10.0 */
.storagePoolDefineXML = parallelsStoragePoolDefineXML, /* 0.10.0 */
.storagePoolUndefine = parallelsStoragePoolUndefine, /* 0.10.0 */
.storagePoolCreate = parallelsStoragePoolCreate, /* 0.10.0 */
.storagePoolDestroy = parallelsStoragePoolDestroy, /* 0.10.0 */
.storagePoolRefresh = parallelsStoragePoolRefresh, /* 0.10.0 */
.storagePoolGetInfo = parallelsStoragePoolGetInfo, /* 0.10.0 */
.storagePoolGetXMLDesc = parallelsStoragePoolGetXMLDesc, /* 0.10.0 */
.storagePoolGetAutostart = parallelsStoragePoolGetAutostart, /* 0.10.0 */
.storagePoolSetAutostart = parallelsStoragePoolSetAutostart, /* 0.10.0 */
.storagePoolNumOfVolumes = parallelsStoragePoolNumOfVolumes, /* 0.10.0 */
.storagePoolListVolumes = parallelsStoragePoolListVolumes, /* 0.10.0 */
.storageVolLookupByName = parallelsStorageVolLookupByName, /* 0.10.0 */
.storageVolLookupByKey = parallelsStorageVolLookupByKey, /* 0.10.0 */
.storageVolLookupByPath = parallelsStorageVolLookupByPath, /* 0.10.0 */
.storageVolCreateXML = parallelsStorageVolCreateXML, /* 0.10.0 */
.storageVolCreateXMLFrom = parallelsStorageVolCreateXMLFrom, /* 0.10.0 */
.storageVolDelete = parallelsStorageVolDelete, /* 0.10.0 */
.storageVolGetInfo = parallelsStorageVolGetInfo, /* 0.10.0 */
.storageVolGetXMLDesc = parallelsStorageVolGetXMLDesc, /* 0.10.0 */
.storageVolGetPath = parallelsStorageVolGetPath, /* 0.10.0 */
.storagePoolIsActive = parallelsStoragePoolIsActive, /* 0.10.0 */
.storagePoolIsPersistent = parallelsStoragePoolIsPersistent, /* 0.10.0 */
};
int
parallelsStorageRegister(void)
{
if (virRegisterStorageDriver(&parallelsStorageDriver) < 0)
return -1;
return 0;
}