PVS has one serious discrepancy with libvirt: libvirt stores domain configuration files in one place, and storage files in other places (with the API of storage pools and storage volumes). PVS stores all domain data in a single directory, for example, you may have domain with name fedora-15, which will be located in '/var/parallels/fedora-15.pvm', and it's hard disk image will be in '/var/parallels/fedora-15.pvm/harddisk1.hdd'. I've decided to create storage driver, which produces pseudo-volumes (xml files with volume description), and they will be 'converted' to real disk images after attaching to a VM. So if someone creates VM with one hard disk using virt-manager, at first virt-manager creates a new volume, and then defines a domain. We can lookup a volume by path in XML domain definition and find out location of new domain and size of its hard disk. Signed-off-by: Dmitry Guryanov <dguryanov@xxxxxxxxxxxxx> --- po/POTFILES.in | 1 + src/Makefile.am | 3 +- src/pvs/pvs_driver.c | 6 +- src/pvs/pvs_driver.h | 5 + src/pvs/pvs_storage.c | 1462 +++++++++++++++++++++++++++++++++++++++++++++++++ src/pvs/pvs_utils.c | 24 + 6 files changed, 1498 insertions(+), 3 deletions(-) create mode 100644 src/pvs/pvs_storage.c diff --git a/po/POTFILES.in b/po/POTFILES.in index 02ce984..b2f1744 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -63,6 +63,7 @@ src/phyp/phyp_driver.c src/pvs/pvs_driver.c src/pvs/pvs_driver.h src/pvs/pvs_utils.c +src/pvs/pvs_storage.c src/qemu/qemu_agent.c src/qemu/qemu_bridge_filter.c src/qemu/qemu_capabilities.c diff --git a/src/Makefile.am b/src/Makefile.am index 030c86d..60b75e4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -469,7 +469,8 @@ HYPERV_DRIVER_EXTRA_DIST = \ PVS_DRIVER_SOURCES = \ pvs/pvs_driver.h \ pvs/pvs_driver.c \ - pvs/pvs_utils.c + pvs/pvs_utils.c \ + pvs/pvs_storage.c NETWORK_DRIVER_SOURCES = \ network/bridge_driver.h network/bridge_driver.c diff --git a/src/pvs/pvs_driver.c b/src/pvs/pvs_driver.c index b0168cb..a085ab5 100644 --- a/src/pvs/pvs_driver.c +++ b/src/pvs/pvs_driver.c @@ -65,13 +65,13 @@ static int pvsStart(virDomainObjPtr privdom); static int pvsKill(virDomainObjPtr privdom); static int pvsStop(virDomainObjPtr privdom); -static void +void pvsDriverLock(pvsConnPtr driver) { virMutexLock(&driver->lock); } -static void +void pvsDriverUnlock(pvsConnPtr driver) { virMutexUnlock(&driver->lock); @@ -1226,6 +1226,8 @@ pvsRegister(void) if (virRegisterDriver(&pvsDriver) < 0) return -1; + if (pvsStorageRegister()) + return -1; return 0; } diff --git a/src/pvs/pvs_driver.h b/src/pvs/pvs_driver.h index 30c6b36..967ad75 100644 --- a/src/pvs/pvs_driver.h +++ b/src/pvs/pvs_driver.h @@ -26,6 +26,7 @@ # include "domain_conf.h" # include "storage_conf.h" +# include "driver.h" # include "domain_event.h" # include "json.h" @@ -59,8 +60,12 @@ typedef struct _pvsConn pvsConn; typedef struct _pvsConn *pvsConnPtr; int pvsRegister(void); +int pvsStorageRegister(void); virJSONValuePtr pvsParseOutput(const char *binary, ...) ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL; int pvsCmdRun(const char *binary, ...) ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL; +char * pvsAddFileExt(const char *path, const char *ext); +void pvsDriverLock(pvsConnPtr driver); +void pvsDriverUnlock(pvsConnPtr driver); #endif diff --git a/src/pvs/pvs_storage.c b/src/pvs/pvs_storage.c new file mode 100644 index 0000000..b2a9a2c --- /dev/null +++ b/src/pvs/pvs_storage.c @@ -0,0 +1,1462 @@ +/* + * pvs_storage.c: core driver functions for managing + * Parallels Virtuozzo Server hosts + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> + +#include <stdlib.h> +#include <dirent.h> +#include <sys/statvfs.h> + +#include "datatypes.h" +#include "memory.h" +#include "configmake.h" +#include "storage_file.h" +#include "virterror_internal.h" + +#include "pvs_driver.h" + +#define VIR_FROM_THIS VIR_FROM_PVS + +static int pvsStorageClose(virConnectPtr conn); +static virStorageVolDefPtr pvsStorageVolumeDefine(virStoragePoolObjPtr pool, + const char *xmldesc, + const char *xmlfile, + bool is_new); +static virStorageVolPtr pvsStorageVolumeLookupByPathLocked(virConnectPtr + conn, + const char + *path); +static virStorageVolPtr pvsStorageVolumeLookupByPath(virConnectPtr conn, + const char *path); +static int pvsStoragePoolGetAlloc(virStoragePoolDefPtr def); + +static void +pvsStorageLock(virStorageDriverStatePtr driver) +{ + virMutexLock(&driver->lock); +} + +static void +pvsStorageUnlock(virStorageDriverStatePtr driver) +{ + virMutexUnlock(&driver->lock); +} + +static int +pvsFindVolumes(virStoragePoolObjPtr pool) +{ + DIR *dir; + struct dirent *ent; + char *path; + + if (!(dir = opendir(pool->def->target.path))) { + virReportSystemError(errno, + _("cannot open path '%s'"), + pool->def->target.path); + goto cleanup; + } + + while ((ent = readdir(dir)) != NULL) { + if (!virFileHasSuffix(ent->d_name, ".xml")) + continue; + + if (!(path = virFileBuildPath(pool->def->target.path, + ent->d_name, NULL))) + goto no_memory; + if (!pvsStorageVolumeDefine(pool, NULL, path, false)) + goto cleanup; + VIR_FREE(path); + } + + return 0; + no_memory: + virReportOOMError(); + cleanup: + return -1; + +} + +static virDrvOpenStatus +pvsStorageOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, unsigned int flags) +{ + char *base = NULL; + virStorageDriverStatePtr storageState; + int privileged = (geteuid() == 0); + pvsConnPtr privconn = conn->privateData; + virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR); + + if (STRNEQ(conn->driver->name, "PVS")) + return VIR_DRV_OPEN_DECLINED; + + if (VIR_ALLOC(storageState) < 0) { + virReportOOMError(); + return VIR_DRV_OPEN_ERROR; + } + + if (virMutexInit(&storageState->lock) < 0) { + VIR_FREE(storageState); + return VIR_DRV_OPEN_ERROR; + } + pvsStorageLock(storageState); + + if (privileged) { + if ((base = strdup(SYSCONFDIR "/libvirt")) == NULL) + goto out_of_memory; + } else { + uid_t uid = geteuid(); + + char *userdir = virGetUserDirectory(uid); + + if (!userdir) + goto error; + + if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) { + VIR_FREE(userdir); + goto out_of_memory; + } + VIR_FREE(userdir); + } + + /* Configuration paths are either ~/.libvirt/storage/... (session) or + * /etc/libvirt/storage/... (system). + */ + if (virAsprintf(&storageState->configDir, + "%s/pvs-storage", base) == -1) + goto out_of_memory; + + if (virAsprintf(&storageState->autostartDir, + "%s/pvs-storage/autostart", base) == -1) + goto out_of_memory; + + VIR_FREE(base); + + if (virStoragePoolLoadAllConfigs(&privconn->pools, + storageState->configDir, + storageState->autostartDir) < 0) { + pvsError(VIR_ERR_INTERNAL_ERROR, _("Failed to load pool configs")); + goto error; + } + + for (int i = 0; i < privconn->pools.count; i++) { + virStoragePoolObjLock(privconn->pools.objs[i]); + virStoragePoolObjPtr pool; + + pool = privconn->pools.objs[i]; + pool->active = 1; + + if (pvsStoragePoolGetAlloc(pool->def) < 0) + goto error; + + if (pvsFindVolumes(pool) < 0) + goto error; + + virStoragePoolObjUnlock(privconn->pools.objs[i]); + } + + pvsStorageUnlock(storageState); + + conn->storagePrivateData = storageState; + + return VIR_DRV_OPEN_SUCCESS; + + out_of_memory: + virReportOOMError(); + error: + VIR_FREE(base); + pvsStorageUnlock(storageState); + pvsStorageClose(conn); + return -1; +} + +static int +pvsStorageClose(virConnectPtr conn) +{ + pvsConnPtr privconn = conn->privateData; + + virStorageDriverStatePtr storageState = conn->storagePrivateData; + conn->storagePrivateData = NULL; + + pvsStorageLock(storageState); + virStoragePoolObjListFree(&privconn->pools); + VIR_FREE(storageState->configDir); + VIR_FREE(storageState->autostartDir); + pvsStorageUnlock(storageState); + virMutexDestroy(&storageState->lock); + VIR_FREE(storageState); + + return 0; +} + +static int +pvsStorageNumPools(virConnectPtr conn) +{ + pvsConnPtr privconn = conn->privateData; + int numActive = 0, i; + + pvsDriverLock(privconn); + for (i = 0; i < privconn->pools.count; i++) + if (virStoragePoolObjIsActive(privconn->pools.objs[i])) + numActive++; + pvsDriverUnlock(privconn); + + return numActive; +} + +static int +pvsStorageListPools(virConnectPtr conn, char **const names, int nnames) +{ + pvsConnPtr privconn = conn->privateData; + int n = 0, i; + + pvsDriverLock(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]) && + !(names[n++] = strdup(privconn->pools.objs[i]->def->name))) { + virStoragePoolObjUnlock(privconn->pools.objs[i]); + goto no_memory; + } + virStoragePoolObjUnlock(privconn->pools.objs[i]); + } + pvsDriverUnlock(privconn); + + return n; + + no_memory: + virReportOOMError(); + for (n = 0; n < nnames; n++) + VIR_FREE(names[n]); + pvsDriverUnlock(privconn); + return -1; +} + +static int +pvsStorageNumDefinedPools(virConnectPtr conn) +{ + pvsConnPtr privconn = conn->privateData; + int numInactive = 0, i; + + pvsDriverLock(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]); + } + pvsDriverUnlock(privconn); + + return numInactive; +} + +static int +pvsStorageListDefinedPools(virConnectPtr conn, + char **const names, int nnames) +{ + pvsConnPtr privconn = conn->privateData; + int n = 0, i; + + pvsDriverLock(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]) && + !(names[n++] = strdup(privconn->pools.objs[i]->def->name))) { + virStoragePoolObjUnlock(privconn->pools.objs[i]); + goto no_memory; + } + virStoragePoolObjUnlock(privconn->pools.objs[i]); + } + pvsDriverUnlock(privconn); + + return n; + + no_memory: + virReportOOMError(); + for (n = 0; n < nnames; n++) + VIR_FREE(names[n]); + pvsDriverUnlock(privconn); + return -1; +} + + +static int +pvsStoragePoolIsActive(virStoragePoolPtr pool) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr obj; + int ret = -1; + + pvsDriverLock(privconn); + obj = virStoragePoolObjFindByUUID(&privconn->pools, pool->uuid); + pvsDriverUnlock(privconn); + if (!obj) { + pvsError(VIR_ERR_NO_STORAGE_POOL, NULL); + goto cleanup; + } + ret = virStoragePoolObjIsActive(obj); + + cleanup: + if (obj) + virStoragePoolObjUnlock(obj); + return ret; +} + +static int +pvsStoragePoolIsPersistent(virStoragePoolPtr pool ATTRIBUTE_UNUSED) +{ + return 1; +} + +static char * +pvsStorageFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED, + const char *type ATTRIBUTE_UNUSED, + const char *srcSpec ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(0, NULL); + + return NULL; +} + +static virStoragePoolPtr +pvsStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid) +{ + pvsConnPtr privconn = conn->privateData; + virStoragePoolObjPtr pool; + virStoragePoolPtr ret = NULL; + + pvsDriverLock(privconn); + pool = virStoragePoolObjFindByUUID(&privconn->pools, uuid); + pvsDriverUnlock(privconn); + + if (pool == NULL) { + pvsError(VIR_ERR_NO_STORAGE_POOL, NULL); + goto cleanup; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + + cleanup: + if (pool) + virStoragePoolObjUnlock(pool); + return ret; +} + +static virStoragePoolPtr +pvsStoragePoolLookupByName(virConnectPtr conn, const char *name) +{ + pvsConnPtr privconn = conn->privateData; + virStoragePoolObjPtr pool; + virStoragePoolPtr ret = NULL; + + pvsDriverLock(privconn); + pool = virStoragePoolObjFindByName(&privconn->pools, name); + pvsDriverUnlock(privconn); + + if (pool == NULL) { + pvsError(VIR_ERR_NO_STORAGE_POOL, NULL); + goto cleanup; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + + cleanup: + if (pool) + virStoragePoolObjUnlock(pool); + return ret; +} + +static virStoragePoolPtr +pvsStoragePoolLookupByVolume(virStorageVolPtr vol) +{ + return pvsStoragePoolLookupByName(vol->conn, vol->pool); +} + +/* + * Fill capacity, available and allocation + * fields in pool definition. + */ +static int +pvsStoragePoolGetAlloc(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_bsize); + def->allocation = def->capacity - def->available; + + return 0; +} + +static virStoragePoolPtr +pvsStoragePoolDefine(virConnectPtr conn, + const char *xml, unsigned int flags) +{ + pvsConnPtr privconn = conn->privateData; + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool = NULL; + virStoragePoolPtr ret = NULL; + + virCheckFlags(0, NULL); + + pvsDriverLock(privconn); + if (!(def = virStoragePoolDefParseString(xml))) + goto cleanup; + + if (def->type != VIR_STORAGE_POOL_DIR) { + pvsError(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 (pvsStoragePoolGetAlloc(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; + + pool->configFile = strdup("\0"); + if (!pool->configFile) { + virReportOOMError(); + goto cleanup; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + + cleanup: + virStoragePoolDefFree(def); + if (pool) + virStoragePoolObjUnlock(pool); + pvsDriverUnlock(privconn); + return ret; +} + +static int +pvsStoragePoolUndefine(virStoragePoolPtr pool) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int ret = -1; + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (virStoragePoolObjIsActive(privpool)) { + pvsError(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); + pvsDriverUnlock(privconn); + return ret; +} + +static int +pvsStoragePoolBuild(virStoragePoolPtr pool, unsigned int flags) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int ret = -1; + + virCheckFlags(0, -1); + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (virStoragePoolObjIsActive(privpool)) { + pvsError(VIR_ERR_OPERATION_INVALID, + _("storage pool '%s' is already active"), pool->name); + goto cleanup; + } + ret = 0; + + cleanup: + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + +static int +pvsStoragePoolStart(virStoragePoolPtr pool, unsigned int flags) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int ret = -1; + + virCheckFlags(0, -1); + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (virStoragePoolObjIsActive(privpool)) { + pvsError(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 +pvsStoragePoolDestroy(virStoragePoolPtr pool) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int ret = -1; + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(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); + pvsDriverUnlock(privconn); + return ret; +} + + +static int +pvsStoragePoolDelete(virStoragePoolPtr pool, unsigned int flags) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int ret = -1; + + virCheckFlags(0, -1); + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (virStoragePoolObjIsActive(privpool)) { + pvsError(VIR_ERR_OPERATION_INVALID, + _("storage pool '%s' is already active"), pool->name); + goto cleanup; + } + + ret = 0; + + cleanup: + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + + +static int +pvsStoragePoolRefresh(virStoragePoolPtr pool, unsigned int flags) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int ret = -1; + + virCheckFlags(0, -1); + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(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 +pvsStoragePoolGetInfo(virStoragePoolPtr pool, virStoragePoolInfoPtr info) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int ret = -1; + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + memset(info, 0, sizeof(virStoragePoolInfo)); + info->state = VIR_STORAGE_POOL_RUNNING; + 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 * +pvsStoragePoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + char *ret = NULL; + + virCheckFlags(0, NULL); + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + ret = virStoragePoolDefFormat(privpool->def); + + cleanup: + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + +static int +pvsStoragePoolGetAutostart(virStoragePoolPtr pool, int *autostart) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int ret = -1; + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (!privpool->configFile) { + *autostart = 0; + } else { + *autostart = privpool->autostart; + } + ret = 0; + + cleanup: + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + +static int +pvsStoragePoolSetAutostart(virStoragePoolPtr pool, int autostart) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int ret = -1; + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (!privpool->configFile) { + pvsError(VIR_ERR_INVALID_ARG, "%s", _("pool has no config file")); + goto cleanup; + } + + autostart = (autostart != 0); + privpool->autostart = autostart; + ret = 0; + + cleanup: + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + +static int +pvsStoragePoolNumVolumes(virStoragePoolPtr pool) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int ret = -1; + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(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 +pvsStoragePoolListVolumes(virStoragePoolPtr pool, + char **const names, int maxnames) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + int i = 0, n = 0; + + memset(names, 0, maxnames * sizeof(*names)); + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(VIR_ERR_OPERATION_INVALID, + _("storage pool '%s' is not active"), pool->name); + goto cleanup; + } + + for (i = 0; i < privpool->volumes.count && n < maxnames; i++) { + if ((names[n++] = strdup(privpool->volumes.objs[i]->name)) == NULL) { + virReportOOMError(); + goto cleanup; + } + } + + virStoragePoolObjUnlock(privpool); + return n; + + cleanup: + for (n = 0; n < maxnames; n++) + VIR_FREE(names[i]); + + memset(names, 0, maxnames * sizeof(*names)); + if (privpool) + virStoragePoolObjUnlock(privpool); + return -1; +} + +static virStorageVolPtr +pvsStorageVolumeLookupByName(virStoragePoolPtr pool, + const char *name ATTRIBUTE_UNUSED) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + virStorageVolDefPtr privvol; + virStorageVolPtr ret = NULL; + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(VIR_ERR_OPERATION_INVALID, + _("storage pool '%s' is not active"), pool->name); + goto cleanup; + } + + privvol = virStorageVolDefFindByName(privpool, name); + + if (!privvol) { + pvsError(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); + + cleanup: + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + + +static virStorageVolPtr +pvsStorageVolumeLookupByKey(virConnectPtr conn, const char *key) +{ + pvsConnPtr privconn = conn->privateData; + unsigned int i; + virStorageVolPtr ret = NULL; + + pvsDriverLock(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); + virStoragePoolObjUnlock(privconn->pools.objs[i]); + break; + } + } + virStoragePoolObjUnlock(privconn->pools.objs[i]); + } + pvsDriverUnlock(privconn); + + if (!ret) + pvsError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching key '%s'"), key); + + return ret; +} + +static virStorageVolPtr +pvsStorageVolumeLookupByPathLocked(virConnectPtr conn, const char *path) +{ + pvsConnPtr privconn = conn->privateData; + unsigned int 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); + virStoragePoolObjUnlock(privconn->pools.objs[i]); + break; + } + } + virStoragePoolObjUnlock(privconn->pools.objs[i]); + } + + if (!ret) + pvsError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching path '%s'"), path); + + return ret; +} + +static virStorageVolPtr +pvsStorageVolumeLookupByPath(virConnectPtr conn, const char *path) +{ + pvsConnPtr privconn = conn->privateData; + virStorageVolPtr ret = NULL; + + pvsDriverLock(privconn); + ret = pvsStorageVolumeLookupByPathLocked(conn, path); + pvsDriverUnlock(privconn); + + return ret; +} + +static virStorageVolDefPtr +pvsStorageVolumeDefine(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)) { + pvsError(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) { + pvsError(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) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&privvol->target.path, "%s/%s", + pool->def->target.path, privvol->name) < 0) { + virReportOOMError(); + goto cleanup; + } + + privvol->key = strdup(privvol->target.path); + if (privvol->key == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (is_new) { + xml_path = pvsAddFileExt(privvol->target.path, ".xml"); + if (!xml_path) + goto cleanup; + + if (virXMLSaveFile + (xml_path, privvol->name, "volume-create", xmldesc)) { + pvsError(VIR_ERR_OPERATION_FAILED, + "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 +pvsStorageVolumeCreateXML(virStoragePoolPtr pool, + const char *xmldesc, unsigned int flags) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + virStorageVolPtr ret = NULL; + virStorageVolDefPtr privvol = NULL; + + virCheckFlags(0, NULL); + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(VIR_ERR_OPERATION_INVALID, + _("storage pool '%s' is not active"), pool->name); + goto cleanup; + } + + privvol = pvsStorageVolumeDefine(privpool, xmldesc, NULL, true); + if (!privvol) + goto cleanup; + + ret = virGetStorageVol(pool->conn, privpool->def->name, + privvol->name, privvol->key); + cleanup: + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + +static virStorageVolPtr +pvsStorageVolumeCreateXMLFrom(virStoragePoolPtr pool, + const char *xmldesc, + virStorageVolPtr clonevol, + unsigned int flags) +{ + pvsConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + virStorageVolDefPtr privvol = NULL, origvol = NULL; + virStorageVolPtr ret = NULL; + + virCheckFlags(0, NULL); + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(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)) { + pvsError(VIR_ERR_OPERATION_FAILED, + "%s", _("storage vol already exists")); + goto cleanup; + } + + origvol = virStorageVolDefFindByName(privpool, clonevol->name); + if (!origvol) { + pvsError(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) { + pvsError(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) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&privvol->target.path, "%s/%s", + privpool->def->target.path, privvol->name) == -1) { + virReportOOMError(); + goto cleanup; + } + + privvol->key = strdup(privvol->target.path); + if (privvol->key == NULL) { + virReportOOMError(); + 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); + privvol = NULL; + + cleanup: + virStorageVolDefFree(privvol); + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + +static int +pvsStorageVolumeDelete(virStorageVolPtr vol, unsigned int flags) +{ + pvsConnPtr privconn = vol->conn->privateData; + virStoragePoolObjPtr privpool; + virStorageVolDefPtr privvol; + int i; + int ret = -1; + char *xml_path = NULL; + + virCheckFlags(0, -1); + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + + privvol = virStorageVolDefFindByName(privpool, vol->name); + + if (privvol == NULL) { + pvsError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching name '%s'"), vol->name); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(VIR_ERR_OPERATION_INVALID, + _("storage pool '%s' is not active"), vol->pool); + goto cleanup; + } + + + 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 = pvsAddFileExt(privvol->target.path, ".xml"); + if (!xml_path) + goto cleanup; + + if (unlink(xml_path)) { + pvsError(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: + if (privpool) + virStoragePoolObjUnlock(privpool); + VIR_FREE(xml_path); + return ret; +} + + +static int +pvsStorageVolumeTypeForPool(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 +pvsStorageVolumeGetInfo(virStorageVolPtr vol, virStorageVolInfoPtr info) +{ + pvsConnPtr privconn = vol->conn->privateData; + virStoragePoolObjPtr privpool; + virStorageVolDefPtr privvol; + int ret = -1; + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + privvol = virStorageVolDefFindByName(privpool, vol->name); + + if (privvol == NULL) { + pvsError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching name '%s'"), vol->name); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(VIR_ERR_OPERATION_INVALID, + _("storage pool '%s' is not active"), vol->pool); + goto cleanup; + } + + memset(info, 0, sizeof(*info)); + info->type = pvsStorageVolumeTypeForPool(privpool->def->type); + info->capacity = privvol->capacity; + info->allocation = privvol->allocation; + ret = 0; + + cleanup: + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + +static char * +pvsStorageVolumeGetXMLDesc(virStorageVolPtr vol, unsigned int flags) +{ + pvsConnPtr privconn = vol->conn->privateData; + virStoragePoolObjPtr privpool; + virStorageVolDefPtr privvol; + char *ret = NULL; + + virCheckFlags(0, NULL); + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + privvol = virStorageVolDefFindByName(privpool, vol->name); + + if (privvol == NULL) { + pvsError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching name '%s'"), vol->name); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(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 * +pvsStorageVolumeGetPath(virStorageVolPtr vol) +{ + pvsConnPtr privconn = vol->conn->privateData; + virStoragePoolObjPtr privpool; + virStorageVolDefPtr privvol; + char *ret = NULL; + + pvsDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool); + pvsDriverUnlock(privconn); + + if (privpool == NULL) { + pvsError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + privvol = virStorageVolDefFindByName(privpool, vol->name); + + if (privvol == NULL) { + pvsError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching name '%s'"), vol->name); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(privpool)) { + pvsError(VIR_ERR_OPERATION_INVALID, + _("storage pool '%s' is not active"), vol->pool); + goto cleanup; + } + + ret = strdup(privvol->target.path); + if (ret == NULL) + virReportOOMError(); + + cleanup: + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + +static virStorageDriver pvsStorageDriver = { + .name = "PVS", + .open = pvsStorageOpen, /* 0.9.12 */ + .close = pvsStorageClose, /* 0.9.12 */ + + .numOfPools = pvsStorageNumPools, /* 0.9.12 */ + .listPools = pvsStorageListPools, /* 0.9.12 */ + .numOfDefinedPools = pvsStorageNumDefinedPools, /* 0.9.12 */ + .listDefinedPools = pvsStorageListDefinedPools, /* 0.9.12 */ + .findPoolSources = pvsStorageFindPoolSources, /* 0.9.12 */ + .poolLookupByName = pvsStoragePoolLookupByName, /* 0.9.12 */ + .poolLookupByUUID = pvsStoragePoolLookupByUUID, /* 0.9.12 */ + .poolLookupByVolume = pvsStoragePoolLookupByVolume, /* 0.9.12 */ + .poolDefineXML = pvsStoragePoolDefine, /* 0.9.12 */ + .poolBuild = pvsStoragePoolBuild, /* 0.9.12 */ + .poolUndefine = pvsStoragePoolUndefine, /* 0.9.12 */ + .poolCreate = pvsStoragePoolStart, /* 0.9.12 */ + .poolDestroy = pvsStoragePoolDestroy, /* 0.9.12 */ + .poolDelete = pvsStoragePoolDelete, /* 0.9.12 */ + .poolRefresh = pvsStoragePoolRefresh, /* 0.9.12 */ + .poolGetInfo = pvsStoragePoolGetInfo, /* 0.9.12 */ + .poolGetXMLDesc = pvsStoragePoolGetXMLDesc, /* 0.9.12 */ + .poolGetAutostart = pvsStoragePoolGetAutostart, /* 0.9.12 */ + .poolSetAutostart = pvsStoragePoolSetAutostart, /* 0.9.12 */ + .poolNumOfVolumes = pvsStoragePoolNumVolumes, /* 0.9.12 */ + .poolListVolumes = pvsStoragePoolListVolumes, /* 0.9.12 */ + + .volLookupByName = pvsStorageVolumeLookupByName, /* 0.9.12 */ + .volLookupByKey = pvsStorageVolumeLookupByKey, /* 0.9.12 */ + .volLookupByPath = pvsStorageVolumeLookupByPath, /* 0.9.12 */ + .volCreateXML = pvsStorageVolumeCreateXML, /* 0.9.12 */ + .volCreateXMLFrom = pvsStorageVolumeCreateXMLFrom, /* 0.9.12 */ + .volDelete = pvsStorageVolumeDelete, /* 0.9.12 */ + .volGetInfo = pvsStorageVolumeGetInfo, /* 0.9.12 */ + .volGetXMLDesc = pvsStorageVolumeGetXMLDesc, /* 0.9.12 */ + .volGetPath = pvsStorageVolumeGetPath, /* 0.9.12 */ + .poolIsActive = pvsStoragePoolIsActive, /* 0.9.12 */ + .poolIsPersistent = pvsStoragePoolIsPersistent, /* 0.9.12 */ +}; + +int +pvsStorageRegister(void) +{ + if (virRegisterStorageDriver(&pvsStorageDriver) < 0) + return -1; + + return 0; +} diff --git a/src/pvs/pvs_utils.c b/src/pvs/pvs_utils.c index 5a730ff..7f6a40d 100644 --- a/src/pvs/pvs_utils.c +++ b/src/pvs/pvs_utils.c @@ -30,6 +30,8 @@ #include "pvs_driver.h" +#define VIR_FROM_THIS VIR_FROM_PVS + static int pvsDoCmdRun(char **outbuf, const char *binary, va_list list) { @@ -105,3 +107,25 @@ pvsCmdRun(const char *binary, ...) return ret; } + +/* + * Return new file path in malloced string created by + * concatenating first and second function arguments. + */ +char * +pvsAddFileExt(const char *path, const char *ext) +{ + char *new_path = NULL; + size_t len = strlen(path) + strlen(ext) + 1; + + if (VIR_ALLOC_N(new_path, len) < 0) { + virReportOOMError(); + return NULL; + } + + if (!virStrcpy(new_path, path, len)) + return NULL; + strcat(new_path, ext); + + return new_path; +} -- 1.7.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list