[libvirt] [PATCH 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. 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 | 478 +++++++++++++++++++++++++++++++------ src/parallels/parallels_utils.h | 3 + 3 files changed, 731 insertions(+), 96 deletions(-)

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 f4799f3..1dabe8d 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 @@ -297,6 +299,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; @@ -324,6 +439,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.1

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 1dabe8d..d10d7a2 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -1475,6 +1475,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]; @@ -1673,8 +1739,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 || @@ -1709,6 +1774,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.1

Move code for loading information about pools to a separate function - parallelsLoadPools. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_storage.c | 65 ++++++++++++++++++++++--------------- 1 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c index 112e288..0453de3 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -114,30 +114,12 @@ 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; - 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); + virStorageDriverStatePtr storageState = conn->storagePrivateData; + int privileged = (geteuid() == 0); + char *base = NULL; if (privileged) { if ((base = strdup(SYSCONFDIR "/libvirt")) == NULL) @@ -192,16 +174,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.1

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 0453de3..bb6327f 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -118,28 +118,12 @@ static int parallelsLoadPools(virConnectPtr conn) { parallelsConnPtr privconn = conn->privateData; virStorageDriverStatePtr storageState = conn->storagePrivateData; - int privileged = (geteuid() == 0); char *base = NULL; - 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.1

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 d10d7a2..21850d3 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -103,6 +103,7 @@ parallelsDomObjFreePrivate(void *p) return; VIR_FREE(pdom->uuid); + VIR_FREE(pdom->home); VIR_FREE(p); }; @@ -665,6 +666,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 bb6327f..c3daee0 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,11 +115,158 @@ 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; + struct parallelsPoolsAddData data; if ((base = strdup(SYSCONFDIR "/libvirt")) == NULL) goto out_of_memory; @@ -142,6 +290,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 (size_t 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.1

We always must 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 c3daee0..4a561a8 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.1

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 4a561a8..003823e 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.1

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 allocation 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 003823e..f3fe3ea 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.1

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 f3fe3ea..f9ca8db 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -1435,48 +1435,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) @@ -1505,12 +1474,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.1

Add function for conversion 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 21850d3..e2ffb48 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) { @@ -1526,17 +1539,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.1

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 e2ffb48..45c2c92 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -1496,18 +1496,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]; @@ -1541,7 +1611,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; } @@ -1554,11 +1624,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]; @@ -1791,7 +1874,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; @@ -1925,7 +2008,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.1

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 45c2c92..797adc4 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -2008,20 +2008,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)) @@ -2036,8 +2022,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)")); @@ -2045,6 +2031,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.1

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 797adc4..d15f1f1 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -1573,6 +1573,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, @@ -1589,10 +1605,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.1
participants (1)
-
Dmitry Guryanov