[libvirt] [PATCH v3 00/13] parallels: implement operations with hard disks

This patch series adds ability to list existing disk images, create a new ones and add or remove hard disk devices to the VM. Changes in v2: rebased to current sources Changes in v3: fixed errors in make syntax-check Dmitry Guryanov (13): parallels: add info about hard disk devices parallels: handle disk devices in parallelsDomainDefineXML parallels: split parallelsStorageOpen function parallels: remove unused code from storage driver parallels: create storage pools by VM list parallels: fix leaks in parallelsFindVolumes parallels: add info about volumes parallels: fill volumes capacity parameter parallels: split parallelsStorageVolumeDelete function parallels: add function parallelsGetDiskBusName parallels: add support of disks creation parallels: apply config after VM creation parallels: add support of removing disks src/parallels/parallels_driver.c | 346 +++++++++++++++++++++++++-- src/parallels/parallels_storage.c | 479 +++++++++++++++++++++++++++++++------ src/parallels/parallels_utils.h | 3 + 3 files changed, 731 insertions(+), 97 deletions(-) -- 1.7.7.6

Parse information about hard disks and fill disks array in virDomainDef structure. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_driver.c | 118 ++++++++++++++++++++++++++++++++++++++ 1 files changed, 118 insertions(+), 0 deletions(-) diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index 50efd1d..1a5851a 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -72,6 +72,8 @@ virReportErrorHelper(VIR_FROM_TEST, VIR_ERR_OPERATION_FAILED, __FILE__, \ __FUNCTION__, __LINE__, _("Can't parse prlctl output")) +#define IS_CT(def) (STREQ_NULLABLE(def->os.type, "exe")) + static int parallelsClose(virConnectPtr conn); void @@ -298,6 +300,119 @@ cleanup: } static int +parallelsGetHddInfo(virDomainDefPtr def, + virDomainDiskDefPtr disk, + const char *key, + virJSONValuePtr value) +{ + const char *tmp; + + disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; + + if (virJSONValueObjectHasKey(value, "real") == 1) { + disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK; + + if (!(tmp = virJSONValueObjectGetString(value, "real"))) { + parallelsParseError(); + return -1; + } + + if (!(disk->src = strdup(tmp))) { + virReportOOMError(); + return -1; + } + } else { + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + + if (!(tmp = virJSONValueObjectGetString(value, "image"))) { + parallelsParseError(); + return -1; + } + + if (!(disk->src = strdup(tmp))) { + virReportOOMError(); + return -1; + } + } + + tmp = virJSONValueObjectGetString(value, "port"); + if (!tmp && !IS_CT(def)) { + parallelsParseError(); + return -1; + } + + if (tmp) { + if (STRPREFIX(tmp, "ide")) { + disk->bus = VIR_DOMAIN_DISK_BUS_IDE; + } else if (STRPREFIX(tmp, "sata")) { + disk->bus = VIR_DOMAIN_DISK_BUS_SATA; + } else if (STRPREFIX(tmp, "scsi")) { + disk->bus = VIR_DOMAIN_DISK_BUS_SCSI; + } else { + parallelsParseError(); + return -1; + } + + char *colonp; + unsigned int pos; + + if (!(colonp = strchr(tmp, ':'))) { + parallelsParseError(); + return -1; + } + + if (virStrToLong_ui(colonp + 1, NULL, 10, &pos) < 0) { + parallelsParseError(); + return -1; + } + + disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; + disk->info.addr.drive.target = pos; + } else { + /* Actually there are no disk devices in containers, but in + * in Parallels Cloud Server we mount disk images as container's + * root fs during start, so it looks like a disk device. */ + disk->bus = VIR_DOMAIN_DISK_BUS_IDE; + } + + unsigned int idx; + if (virStrToLong_ui(key + strlen("hdd"), NULL, 10, &idx) < 0) { + parallelsParseError(); + return -1; + } + + if (!(disk->dst = virIndexToDiskName(idx, "sd"))) + return -1; + + return 0; +} + +static int +parallelsAddHddInfo(virDomainDefPtr def, const char *key, virJSONValuePtr value) +{ + virDomainDiskDefPtr disk = NULL; + + if (VIR_ALLOC(disk) < 0) + goto no_memory; + + if (parallelsGetHddInfo(def, disk, key, value)) + goto error; + + if (VIR_REALLOC_N(def->disks, def->ndisks + 1) < 0) + goto no_memory; + + def->disks[def->ndisks++] = disk; + + return 0; + +no_memory: + virReportOOMError(); +error: + virDomainDiskDefFree(disk); + return -1; +} + +static int parallelsAddDomainHardware(virDomainDefPtr def, virJSONValuePtr jobj) { int n; @@ -325,6 +440,9 @@ parallelsAddDomainHardware(virDomainDefPtr def, virJSONValuePtr jobj) } else if (STREQ(key, "video")) { if (parallelsAddVideoInfo(def, value)) goto cleanup; + } else if (STRPREFIX(key, "hdd")) { + if (parallelsAddHddInfo(def, key, value)) + goto cleanup; } } -- 1.7.7.6

Allow changing some parameters of the hard disks: bus, image and drive address. Creating new disk devices and removing existing ones require changes in the storage driver, so it will be implemented later. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_driver.c | 73 ++++++++++++++++++++++++++++++++++++- 1 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index 1a5851a..11c5b83 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -1478,6 +1478,72 @@ parallelsApplyVideoParams(parallelsDomObjPtr pdom, } static int +parallelsApplyDisksParams(parallelsDomObjPtr pdom, + virDomainDiskDefPtr *olddisks, int nold, + virDomainDiskDefPtr *newdisks, int nnew) +{ + /* TODO: allow creating and removing disks */ + if (nold != nnew) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Adding and removing disks is not supported")); + return -1; + } + + for (int i = 0; i < nold; i++) { + virDomainDiskDefPtr newdisk = NULL; + virDomainDiskDefPtr olddisk = olddisks[i]; + for (int j = 0; j < nnew; j++) { + if (STREQ_NULLABLE(newdisks[j]->dst, olddisk->dst)) { + newdisk = newdisks[j]; + break; + } + } + + if (!newdisk) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, + _("There is no disk with source '%s' " + "in the specified config"), olddisks[i]->serial); + return -1; + } + + if (olddisk->bus != newdisk->bus || + olddisk->info.addr.drive.target != newdisk->info.addr.drive.target || + !STREQ_NULLABLE(olddisk->src, newdisk->src)) { + + char prlname[16]; + char strpos[16]; + const char *strbus; + + prlname[15] = '\0'; + snprintf(prlname, 15, "hdd%d", virDiskNameToIndex(newdisk->dst)); + + strpos[15] = '\0'; + snprintf(strpos, 15, "%d", newdisk->info.addr.drive.target); + + switch (newdisk->bus) { + case VIR_DOMAIN_DISK_BUS_IDE: + strbus = "ide"; + break; + case VIR_DOMAIN_DISK_BUS_SATA: + strbus = "sata"; + break; + case VIR_DOMAIN_DISK_BUS_SCSI: + strbus = "scsi"; + break; + } + if (parallelsCmdRun(PRLCTL, "set", pdom->uuid, + "--device-set", prlname, + "--iface", strbus, + "--position", strpos, + "--image", newdisk->src, NULL)) + return -1; + } + } + + return 0; +} + +static int parallelsApplyChanges(virDomainObjPtr dom, virDomainDefPtr new) { char buf[32]; @@ -1676,8 +1742,7 @@ parallelsApplyChanges(virDomainObjPtr dom, virDomainDefPtr new) new->graphics, new->ngraphics) < 0) return -1; - if (new->ndisks != 0 || new->ncontrollers != 0 || - new->nfss != 0 || new->nnets != 0 || + if (new->nfss != 0 || new->nnets != 0 || new->nsounds != 0 || new->nhostdevs != 0 || new->nredirdevs != 0 || new->nsmartcards != 0 || new->nparallels || new->nchannels != 0 || @@ -1712,6 +1777,10 @@ parallelsApplyChanges(virDomainObjPtr dom, virDomainDefPtr new) if (parallelsApplyVideoParams(pdom, old->videos, old->nvideos, new->videos, new->nvideos) < 0) return -1; + if (parallelsApplyDisksParams(pdom, old->disks, old->ndisks, + new->disks, new->ndisks) < 0) + return -1; + return 0; } -- 1.7.7.6

Move code for loading inforation about pools to a separate function - parallelsLoadPools. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_storage.c | 66 ++++++++++++++++++++++--------------- 1 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c index 8d4e2c6..6268a5b 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -114,33 +114,14 @@ cleanup: } -static virDrvOpenStatus -parallelsStorageOpen(virConnectPtr conn, - virConnectAuthPtr auth ATTRIBUTE_UNUSED, - unsigned int flags) +static int parallelsLoadPools(virConnectPtr conn) { - char *base = NULL; - virStorageDriverStatePtr storageState; - int privileged = (geteuid() == 0); parallelsConnPtr privconn = conn->privateData; + virStorageDriverStatePtr storageState = conn->storagePrivateData; + int privileged = (geteuid() == 0); + char *base = NULL; size_t i; - virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR); - - if (STRNEQ(conn->driver->name, "Parallels")) - 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; - } - parallelsStorageLock(storageState); - if (privileged) { if ((base = strdup(SYSCONFDIR "/libvirt")) == NULL) goto out_of_memory; @@ -194,16 +175,47 @@ parallelsStorageOpen(virConnectPtr conn, virStoragePoolObjUnlock(privconn->pools.objs[i]); } - parallelsStorageUnlock(storageState); + return 0; + +out_of_memory: + virReportOOMError(); +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) { + virReportOOMError(); + 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; -out_of_memory: - virReportOOMError(); error: - VIR_FREE(base); parallelsStorageUnlock(storageState); parallelsStorageClose(conn); return -1; -- 1.7.7.6

We don't support unprivileged users anymore, so remove code, which selects configuration directory depending on user. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_storage.c | 22 +++------------------- 1 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c index 6268a5b..90c425a 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -118,29 +118,13 @@ static int parallelsLoadPools(virConnectPtr conn) { parallelsConnPtr privconn = conn->privateData; virStorageDriverStatePtr storageState = conn->storagePrivateData; - int privileged = (geteuid() == 0); char *base = NULL; size_t i; - if (privileged) { - if ((base = strdup(SYSCONFDIR "/libvirt")) == NULL) - goto out_of_memory; - } else { - char *userdir = virGetUserDirectory(); - - if (!userdir) - goto error; - - if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) { - VIR_FREE(userdir); - goto out_of_memory; - } - VIR_FREE(userdir); - } + if ((base = strdup(SYSCONFDIR "/libvirt")) == NULL) + goto out_of_memory; - /* Configuration paths are either ~/.libvirt/storage/... (session) or - * /etc/libvirt/storage/... (system). - */ + /* Configuration path is /etc/libvirt/parallels-storage/... . */ if (virAsprintf(&storageState->configDir, "%s/parallels-storage", base) == -1) goto out_of_memory; -- 1.7.7.6

There are no storage pools in Parallels Cloud Server - All VM data stored in a single directory: config, snapshots, memory dump together with disk images. Let's look through list of VMs and create a storage pool for each directory, containing VMs. So if you have 3 vms: /var/parallels/vm-1.pvm, /var/parallels/vm-2.pvm and /root/test.pvm - 2 storage pools appear: -var-parallels and -root. xml descriptions of the pools will be saved in /etc/libvirt/parallels-storage, so UUIDs will not change netween connections to libvirt. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_driver.c | 9 ++ src/parallels/parallels_storage.c | 155 +++++++++++++++++++++++++++++++++++++ src/parallels/parallels_utils.h | 1 + 3 files changed, 165 insertions(+), 0 deletions(-) diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index 11c5b83..fe91968 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -104,6 +104,7 @@ parallelsDomObjFreePrivate(void *p) return; VIR_FREE(pdom->uuid); + VIR_FREE(pdom->home); VIR_FREE(p); }; @@ -666,6 +667,14 @@ parallelsLoadDomain(parallelsConnPtr privconn, virJSONValuePtr jobj) if (!(pdom->uuid = strdup(tmp))) goto no_memory; + if (!(tmp = virJSONValueObjectGetString(jobj, "Home"))) { + parallelsParseError(); + goto cleanup; + } + + if (!(pdom->home = strdup(tmp))) + goto no_memory; + if (!(tmp = virJSONValueObjectGetString(jobj, "OS"))) goto cleanup; diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c index 90c425a..148d870 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -25,6 +25,7 @@ #include <stdlib.h> #include <dirent.h> #include <sys/statvfs.h> +#include <libgen.h> #include "datatypes.h" #include "memory.h" @@ -114,12 +115,159 @@ cleanup: } +struct parallelsPoolsAddData { + virConnectPtr conn; + bool failed; +}; + +/* + * Generate unique pool name by path + */ +static char *parallelsMakePoolName(virConnectPtr conn, const char *path) +{ + parallelsConnPtr privconn = conn->privateData; + char *name; + + for (unsigned int i = 0; i < UINT_MAX; i++) { + bool found = false; + + if (!(name = strdup(path))) { + virReportOOMError(); + return NULL; + } + + if (i == 0) + name = strdup(path); + else + virAsprintf(&name, "%s-%u", path, i); + + if (!name) { + virReportOOMError(); + return 0; + } + + for (int j = 0; j < strlen(name); j++) + if (name[j] == '/') + name[j] = '-'; + + for (int 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 no_memory; + + if (!(def->name = parallelsMakePoolName(conn, path))) + goto error; + + if (VIR_ALLOC_N(def->uuid, VIR_UUID_BUFLEN)) + goto no_memory; + + if (virUUIDGenerate(def->uuid)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Can't generate UUID")); + goto error; + } + + def->type = VIR_STORAGE_POOL_DIR; + def->target.path = strdup(path); + + if (!(pool = virStoragePoolObjAssignDef(pools, def))) + goto error; + + if (virStoragePoolObjSaveDef(conn->storagePrivateData, pool, def) < 0) { + virStoragePoolObjRemove(pools, pool); + goto error; + } + + virStoragePoolObjUnlock(pool); + + return pool; +no_memory: + virReportOOMError(); +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; + + if (!(poolPath = strdup(pdom->home))) { + virReportOOMError(); + return NULL; + } + + poolPath = dirname(poolPath); + + for (int 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 void +parallelsPoolsAdd(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *opaque) +{ + struct parallelsPoolsAddData *data = (struct parallelsPoolsAddData *)opaque; + virDomainObjPtr dom = payload; + virStoragePoolObjPtr pool; + + if (!(pool = parallelsPoolAddByDomain(data->conn, dom))) + data->failed = true; + + return; +} + static int parallelsLoadPools(virConnectPtr conn) { parallelsConnPtr privconn = conn->privateData; virStorageDriverStatePtr storageState = conn->storagePrivateData; char *base = NULL; size_t i; + struct parallelsPoolsAddData data; if ((base = strdup(SYSCONFDIR "/libvirt")) == NULL) goto out_of_memory; @@ -143,6 +291,13 @@ static int parallelsLoadPools(virConnectPtr conn) goto error; } + data.conn = conn; + data.failed = false; + virHashForEach(privconn->domains.objs, parallelsPoolsAdd, &data); + + if (data.failed) + goto error; + for (i = 0; i < privconn->pools.count; i++) { virStoragePoolObjLock(privconn->pools.objs[i]); virStoragePoolObjPtr pool; diff --git a/src/parallels/parallels_utils.h b/src/parallels/parallels_utils.h index 6a27003..fb759cf 100644 --- a/src/parallels/parallels_utils.h +++ b/src/parallels/parallels_utils.h @@ -44,6 +44,7 @@ typedef struct _parallelsConn *parallelsConnPtr; struct parallelsDomObj { int id; char *uuid; + char *home; }; typedef struct parallelsDomObj *parallelsDomObjPtr; -- 1.7.7.6

We always have to close opened dir and free 'path'. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_storage.c | 20 ++++++++++++-------- 1 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c index 148d870..0d59cce 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -86,13 +86,14 @@ parallelsFindVolumes(virStoragePoolObjPtr pool) { DIR *dir; struct dirent *ent; - char *path; + char *path = NULL; + int ret = -1; if (!(dir = opendir(pool->def->target.path))) { virReportSystemError(errno, _("cannot open path '%s'"), pool->def->target.path); - goto cleanup; + return -1; } while ((ent = readdir(dir)) != NULL) { @@ -100,18 +101,21 @@ parallelsFindVolumes(virStoragePoolObjPtr pool) continue; if (!(path = virFileBuildPath(pool->def->target.path, - ent->d_name, NULL))) - goto no_memory; + ent->d_name, NULL))) { + virReportOOMError(); + goto cleanup; + } if (!parallelsStorageVolumeDefine(pool, NULL, path, false)) goto cleanup; + VIR_FREE(path); } - return 0; -no_memory: - virReportOOMError(); + ret = 0; cleanup: - return -1; + VIR_FREE(path); + closedir(dir); + return ret; } -- 1.7.7.6

Disk images in Parallels Cloud Server stored in directories. Each one has files with data and xml description of an image stored in file DiskDescriptior.xml. Since we have to support 'detached' images, which are not used by any VM, the better way to collect info about volumes is searching for directories with a file DiskDescriptior.xml in each VM directory. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_storage.c | 107 ++++++++++++++++++++++++++++++++++++- 1 files changed, 106 insertions(+), 1 deletions(-) diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c index 0d59cce..4d71eb1 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -25,6 +25,9 @@ #include <stdlib.h> #include <dirent.h> #include <sys/statvfs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> #include <libgen.h> #include "datatypes.h" @@ -250,6 +253,101 @@ parallelsPoolAddByDomain(virConnectPtr conn, virDomainObjPtr dom) return pool; } +static int parallelsAddDiskVolume(virStoragePoolObjPtr pool, + virDomainObjPtr dom, + const char *diskName, + const char *diskPath) +{ + virStorageVolDefPtr def = NULL; + + if (VIR_ALLOC(def)) + goto no_memory; + + virAsprintf(&def->name, "%s-%s", dom->def->name, diskName); + if (!def->name) + goto no_memory; + + def->type = VIR_STORAGE_VOL_FILE; + + if (!(def->target.path = realpath(diskPath, NULL))) + goto no_memory; + + if (!(def->key = strdup(def->target.path))) + goto no_memory; + + if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1) < 0) + goto no_memory; + + pool->volumes.objs[pool->volumes.count++] = def; + + return 0; +no_memory: + virStorageVolDefFree(def); + virReportOOMError(); + 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))) { + virReportOOMError(); + 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"))) { + virReportOOMError(); + goto cleanup; + } + + if (access(diskDescPath, F_OK)) + continue; + + /* here we know, that ent->d_name is a disk image directory */ + + if (parallelsAddDiskVolume(pool, dom, ent->d_name, diskPath)) + goto cleanup; + + } + + ret = 0; +cleanup: + VIR_FREE(diskPath); + VIR_FREE(diskDescPath); + closedir(dir); + return ret; + +} + static void parallelsPoolsAdd(void *payload, const void *name ATTRIBUTE_UNUSED, @@ -259,8 +357,15 @@ parallelsPoolsAdd(void *payload, virDomainObjPtr dom = payload; virStoragePoolObjPtr pool; - if (!(pool = parallelsPoolAddByDomain(data->conn, dom))) + if (!(pool = parallelsPoolAddByDomain(data->conn, dom))) { data->failed = true; + return; + } + + if (parallelsFindVmVolumes(pool, dom)) { + data->failed = true; + return; + } return; } -- 1.7.7.6

Read disk images size from xml description and fill virStorageVolDef.capacity and allocation (let's consider that allocation is the same as capacity, calculating real allcoation will be implemented later). Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_storage.c | 54 +++++++++++++++++++++++++++++++++++- 1 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c index 4d71eb1..1448a74 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -253,10 +253,57 @@ parallelsPoolAddByDomain(virConnectPtr conn, virDomainObjPtr dom) return pool; } +static int parallelsDiskDescParseNode(xmlDocPtr xml, + xmlNodePtr root, + virStorageVolDefPtr def) +{ + xmlXPathContextPtr ctxt = NULL; + int ret; + + 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)) + ret = -1; + + def->capacity <<= 9; + def->allocation = def->capacity; +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 *diskPath, + const char *diskDescPath) { virStorageVolDefPtr def = NULL; @@ -269,6 +316,8 @@ static int parallelsAddDiskVolume(virStoragePoolObjPtr pool, def->type = VIR_STORAGE_VOL_FILE; + parallelsDiskDescParse(diskDescPath, def); + if (!(def->target.path = realpath(diskPath, NULL))) goto no_memory; @@ -334,7 +383,8 @@ static int parallelsFindVmVolumes(virStoragePoolObjPtr pool, /* here we know, that ent->d_name is a disk image directory */ - if (parallelsAddDiskVolume(pool, dom, ent->d_name, diskPath)) + if (parallelsAddDiskVolume(pool, dom, ent->d_name, + diskPath, diskDescPath)) goto cleanup; } -- 1.7.7.6

Move part, which deletes existing volume, to a new function parallelsStorageVolumeDefRemove so that we can use it later in parallels_driver.c Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_storage.c | 83 +++++++++++++++++++++--------------- src/parallels/parallels_utils.h | 2 + 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c index 1448a74..ad15687 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -1443,48 +1443,17 @@ cleanup: return ret; } -static int -parallelsStorageVolumeDelete(virStorageVolPtr vol, unsigned int flags) +int parallelsStorageVolumeDefRemove(virStoragePoolObjPtr privpool, + virStorageVolDefPtr privvol) { - parallelsConnPtr privconn = vol->conn->privateData; - virStoragePoolObjPtr privpool; - virStorageVolDefPtr privvol; - size_t i; int ret = -1; char *xml_path = NULL; - 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; - } - - privpool->def->allocation -= privvol->allocation; privpool->def->available = (privpool->def->capacity - privpool->def->allocation); - for (i = 0; i < privpool->volumes.count; i++) { + for (size_t i = 0; i < privpool->volumes.count; i++) { if (privpool->volumes.objs[i] == privvol) { xml_path = parallelsAddFileExt(privvol->target.path, ".xml"); if (!xml_path) @@ -1513,12 +1482,56 @@ parallelsStorageVolumeDelete(virStorageVolPtr vol, unsigned int flags) break; } } + + ret = 0; +cleanup: + VIR_FREE(xml_path); + return ret; +} + +static int +parallelsStorageVolumeDelete(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 (parallelsStorageVolumeDefRemove(privpool, privvol)) + goto cleanup; + ret = 0; cleanup: if (privpool) virStoragePoolObjUnlock(privpool); - VIR_FREE(xml_path); return ret; } diff --git a/src/parallels/parallels_utils.h b/src/parallels/parallels_utils.h index fb759cf..e4b3f8b 100644 --- a/src/parallels/parallels_utils.h +++ b/src/parallels/parallels_utils.h @@ -62,5 +62,7 @@ void parallelsDriverLock(parallelsConnPtr driver); void parallelsDriverUnlock(parallelsConnPtr driver); virStorageVolPtr parallelsStorageVolumeLookupByPathLocked(virConnectPtr conn, const char *path); +int parallelsStorageVolumeDefRemove(virStoragePoolObjPtr privpool, + virStorageVolDefPtr privvol); #endif -- 1.7.7.6

Add function for convertion bus from libvirt's numeric constant to a name, used in a parallels command-line tools. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_driver.c | 28 ++++++++++++++++++---------- 1 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index fe91968..967f545 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -76,6 +76,19 @@ static int parallelsClose(virConnectPtr conn); +const char * parallelsGetDiskBusName(int bus) { + switch (bus) { + case VIR_DOMAIN_DISK_BUS_IDE: + return "ide"; + case VIR_DOMAIN_DISK_BUS_SATA: + return "sata"; + case VIR_DOMAIN_DISK_BUS_SCSI: + return "scsi"; + default: + return NULL; + } +} + void parallelsDriverLock(parallelsConnPtr driver) { @@ -1529,17 +1542,12 @@ parallelsApplyDisksParams(parallelsDomObjPtr pdom, strpos[15] = '\0'; snprintf(strpos, 15, "%d", newdisk->info.addr.drive.target); - switch (newdisk->bus) { - case VIR_DOMAIN_DISK_BUS_IDE: - strbus = "ide"; - break; - case VIR_DOMAIN_DISK_BUS_SATA: - strbus = "sata"; - break; - case VIR_DOMAIN_DISK_BUS_SCSI: - strbus = "scsi"; - break; + if (!(strbus = parallelsGetDiskBusName(newdisk->bus))) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, + _("Unsupported disk bus: %d", newdisk->bus)); + return -1; } + if (parallelsCmdRun(PRLCTL, "set", pdom->uuid, "--device-set", prlname, "--iface", strbus, -- 1.7.7.6

Implement creation of new disks - if a new disk found in configuration, find a volume by disk path and actually create a disk image by issuing prlctl command. If it's successfully finished - remove the file with volume definition. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_driver.c | 107 +++++++++++++++++++++++++++++++++---- 1 files changed, 95 insertions(+), 12 deletions(-) diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index 967f545..fe034f6 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -1499,18 +1499,88 @@ parallelsApplyVideoParams(parallelsDomObjPtr pdom, return 0; } -static int -parallelsApplyDisksParams(parallelsDomObjPtr pdom, - virDomainDiskDefPtr *olddisks, int nold, - virDomainDiskDefPtr *newdisks, int nnew) +static int parallelsAddHddByVolume(parallelsDomObjPtr pdom, + virDomainDiskDefPtr disk, + virStoragePoolObjPtr pool, + virStorageVolDefPtr voldef) { - /* TODO: allow creating and removing disks */ - if (nold != nnew) { - virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", - _("Adding and removing disks is not supported")); + int ret = -1; + virCommandPtr cmd = virCommandNewArgList(PRLCTL, "set", pdom->uuid, + "--device-add", "hdd", NULL); + virCommandAddArgFormat(cmd, "--size=%lluM", voldef->capacity >> 20); + + const char *strbus; + + if (!(strbus = parallelsGetDiskBusName(disk->bus))) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, + _("Invalid disk bus: %d"), disk->bus); + goto cleanup; + } + + virCommandAddArgFormat(cmd, "--iface=%s", strbus); + + if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) + virCommandAddArgFormat(cmd, "--position=%d", + disk->info.addr.drive.target); + + if (virCommandRun(cmd, NULL)) + goto cleanup; + + if (parallelsStorageVolumeDefRemove(pool, voldef)) + goto cleanup; + + ret = 0; +cleanup: + virCommandFree(cmd); + return ret; +} + +static int parallelsAddHdd(virConnectPtr conn, + parallelsDomObjPtr pdom, + virDomainDiskDefPtr disk) +{ + parallelsConnPtr privconn = conn->privateData; + virStorageVolDefPtr voldef = NULL; + virStoragePoolObjPtr pool = NULL; + virStorageVolPtr vol = NULL; + int ret = -1; + + if (!(vol = parallelsStorageVolumeLookupByPathLocked(conn, disk->src))) { + virReportError(VIR_ERR_INVALID_ARG, + _("Can't find volume with path '%s'"), disk->src); return -1; } + pool = virStoragePoolObjFindByName(&privconn->pools, vol->pool); + if (!pool) { + virReportError(VIR_ERR_INVALID_ARG, + _("Can't find storage pool with name '%s'"), + vol->pool); + goto cleanup; + } + + voldef = virStorageVolDefFindByPath(pool, disk->src); + if (!voldef) { + virReportError(VIR_ERR_INVALID_ARG, + _("Can't find storage volume definition for path '%s'"), + disk->src); + goto cleanup; + } + + ret = parallelsAddHddByVolume(pdom, disk, pool, voldef); + +cleanup: + if (pool) + virStoragePoolObjUnlock(pool); + virObjectUnref(vol); + return ret; +} + +static int +parallelsApplyDisksParams(virConnectPtr conn, parallelsDomObjPtr pdom, + virDomainDiskDefPtr *olddisks, int nold, + virDomainDiskDefPtr *newdisks, int nnew) +{ for (int i = 0; i < nold; i++) { virDomainDiskDefPtr newdisk = NULL; virDomainDiskDefPtr olddisk = olddisks[i]; @@ -1544,7 +1614,7 @@ parallelsApplyDisksParams(parallelsDomObjPtr pdom, if (!(strbus = parallelsGetDiskBusName(newdisk->bus))) { virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, - _("Unsupported disk bus: %d", newdisk->bus)); + _("Unsupported disk bus: %d"), newdisk->bus); return -1; } @@ -1557,11 +1627,24 @@ parallelsApplyDisksParams(parallelsDomObjPtr pdom, } } + for (int i = 0; i < nnew; i++) { + virDomainDiskDefPtr newdisk = newdisks[i]; + bool found = false; + for (int j = 0; j < nold; j++) + if (STREQ_NULLABLE(olddisks[j]->dst, newdisk->dst)) + found = true; + if (found) + continue; + + if (parallelsAddHdd(conn, pdom, newdisk)) + return -1; + } + return 0; } static int -parallelsApplyChanges(virDomainObjPtr dom, virDomainDefPtr new) +parallelsApplyChanges(virConnectPtr conn, virDomainObjPtr dom, virDomainDefPtr new) { char buf[32]; @@ -1794,7 +1877,7 @@ parallelsApplyChanges(virDomainObjPtr dom, virDomainDefPtr new) if (parallelsApplyVideoParams(pdom, old->videos, old->nvideos, new->videos, new->nvideos) < 0) return -1; - if (parallelsApplyDisksParams(pdom, old->disks, old->ndisks, + if (parallelsApplyDisksParams(conn, pdom, old->disks, old->ndisks, new->disks, new->ndisks) < 0) return -1; @@ -1928,7 +2011,7 @@ parallelsDomainDefineXML(virConnectPtr conn, const char *xml) if (dupVM == 1) { olddom = virDomainFindByUUID(&privconn->domains, def->uuid); - if (parallelsApplyChanges(olddom, def) < 0) { + if (parallelsApplyChanges(conn, olddom, def) < 0) { virDomainObjUnlock(olddom); goto cleanup; } -- 1.7.7.6

New VM will have default values for all parameters, like cpu number, we have to change its configuration as provided by xml definition, given to parallelsDomainDefineXML. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_driver.c | 33 +++++++++++++++++---------------- 1 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index fe034f6..64bf409 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -2011,20 +2011,6 @@ parallelsDomainDefineXML(virConnectPtr conn, const char *xml) if (dupVM == 1) { olddom = virDomainFindByUUID(&privconn->domains, def->uuid); - if (parallelsApplyChanges(conn, olddom, def) < 0) { - virDomainObjUnlock(olddom); - goto cleanup; - } - virDomainObjUnlock(olddom); - - if (!(dom = virDomainAssignDef(privconn->caps, - &privconn->domains, def, false))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Can't allocate domobj")); - goto cleanup; - } - - def = NULL; } else { if (STREQ(def->os.type, "hvm")) { if (parallelsCreateVm(conn, def)) @@ -2039,8 +2025,8 @@ parallelsDomainDefineXML(virConnectPtr conn, const char *xml) } if (parallelsLoadDomains(privconn, def->name)) goto cleanup; - dom = virDomainFindByName(&privconn->domains, def->name); - if (!dom) { + olddom = virDomainFindByName(&privconn->domains, def->name); + if (!olddom) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Domain for '%s' is not defined after creation"), def->name ? def->name : _("(unnamed)")); @@ -2048,6 +2034,21 @@ parallelsDomainDefineXML(virConnectPtr conn, const char *xml) } } + if (parallelsApplyChanges(conn, olddom, def) < 0) { + virDomainObjUnlock(olddom); + goto cleanup; + } + virDomainObjUnlock(olddom); + + if (!(dom = virDomainAssignDef(privconn->caps, + &privconn->domains, def, false))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Can't allocate domobj")); + goto cleanup; + } + + def = NULL; + ret = virGetDomain(conn, dom->def->name, dom->def->uuid); if (ret) ret->id = dom->def->id; -- 1.7.7.6

If some hard disk is not found in new domain configuration, it should be removed. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_driver.c | 28 ++++++++++++++++++++++++---- 1 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index 64bf409..bbbc9d5 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -1576,6 +1576,22 @@ cleanup: return ret; } +static int parallelsRemoveHdd(parallelsDomObjPtr pdom, + virDomainDiskDefPtr disk) +{ + char prlname[16]; + + prlname[15] = '\0'; + snprintf(prlname, 15, "hdd%d", virDiskNameToIndex(disk->dst)); + + if (parallelsCmdRun(PRLCTL, "set", pdom->uuid, + "--device-del", prlname, + "--detach-only", NULL)) + return -1; + + return 0; +} + static int parallelsApplyDisksParams(virConnectPtr conn, parallelsDomObjPtr pdom, virDomainDiskDefPtr *olddisks, int nold, @@ -1592,10 +1608,14 @@ parallelsApplyDisksParams(virConnectPtr conn, parallelsDomObjPtr pdom, } if (!newdisk) { - virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, - _("There is no disk with source '%s' " - "in the specified config"), olddisks[i]->serial); - return -1; + if (parallelsRemoveHdd(pdom, olddisk)) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, + _("Can't remove disk '%s' " + "in the specified config"), olddisks[i]->serial); + return -1; + } + + continue; } if (olddisk->bus != newdisk->bus || -- 1.7.7.6
participants (1)
-
Dmitry Guryanov