[libvirt] [PATCH v4 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 Changes in v4: fixed rebase errors 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

On Tue, Dec 04, 2012 at 05:43:01PM +0400, Dmitry Guryanov wrote:
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;
Okay, but as far as I know we still prefer variable declaration in block head so I's squashing in the following patch: diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index 8274c88..60bf8b5 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -320,6 +320,7 @@ parallelsGetHddInfo(virDomainDefPtr def, virJSONValuePtr value) { const char *tmp; + unsigned int idx; disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; @@ -389,7 +390,6 @@ parallelsGetHddInfo(virDomainDefPtr def, disk->bus = VIR_DOMAIN_DISK_BUS_IDE; } - unsigned int idx; if (virStrToLong_ui(key + strlen("hdd"), NULL, 10, &idx) < 0) { parallelsParseError(); return -1; Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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

On Tue, Dec 04, 2012 at 05:43:02PM +0400, Dmitry Guryanov wrote:
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; }
ACK, Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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 9075dfd..e602299 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; - bool privileged = (geteuid() == 0); parallelsConnPtr privconn = conn->privateData; + virStorageDriverStatePtr storageState = conn->storagePrivateData; + bool 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

On Tue, Dec 04, 2012 at 05:43:03PM +0400, Dmitry Guryanov wrote:
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 9075dfd..e602299 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; - bool privileged = (geteuid() == 0); parallelsConnPtr privconn = conn->privateData; + virStorageDriverStatePtr storageState = conn->storagePrivateData; + bool 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;
Okay, ACK, Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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 e602299..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; - bool 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

On Tue, Dec 04, 2012 at 05:43:04PM +0400, Dmitry Guryanov wrote:
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 e602299..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; - bool 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;
ACK from a code perspective, but that sounds like a user visible regression, is that restriction embedded in the hypervisor, or just for the libvirt bindings ? Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

On 121211 15:49:40, Daniel Veillard wrote:
On Tue, Dec 04, 2012 at 05:43:04PM +0400, Dmitry Guryanov wrote:
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 e602299..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; - bool 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;
ACK from a code perspective, but that sounds like a user visible regression, is that restriction embedded in the hypervisor, or just for the libvirt bindings ?
Parallels Cloud Server has this restriction too, but our desktop products (Parallels Desktop for Mac, Parallels Workstation), which use the same hypervisor, don't. There are no plans to support them (and it seems nobody needs it), so I think we'd rather remove this code.
Daniel
-- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/
-- Dmitry Guryanov

On Tue, Dec 11, 2012 at 12:34:36PM +0400, Dmitry Guryanov wrote:
On 121211 15:49:40, Daniel Veillard wrote:
On Tue, Dec 04, 2012 at 05:43:04PM +0400, Dmitry Guryanov wrote:
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 e602299..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; - bool 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;
ACK from a code perspective, but that sounds like a user visible regression, is that restriction embedded in the hypervisor, or just for the libvirt bindings ?
Parallels Cloud Server has this restriction too, but our desktop products (Parallels Desktop for Mac, Parallels Workstation), which use the same hypervisor, don't. There are no plans to support them (and it seems nobody needs it), so I think we'd rather remove this code.
Okay then, thanks for the details ! Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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

On Tue, Dec 04, 2012 at 05:43:05PM +0400, Dmitry Guryanov wrote:
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>
okay, I was a bit worried by ortability there, but apparently we already use this for cgroup support so as long as parallels platform is linux only we should be safe there,
#include "datatypes.h" #include "memory.h" @@ -114,12 +115,159 @@ cleanup:
ACK, Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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

On Tue, Dec 04, 2012 at 05:43:06PM +0400, Dmitry Guryanov wrote:
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;
}
ACK, Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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

On Tue, Dec 04, 2012 at 05:43:07PM +0400, Dmitry Guryanov wrote:
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.
typo it's DiskDescriptor.xml based on the patch
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; }
Okay, ACK, Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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

On Tue, Dec 04, 2012 at 05:43:08PM +0400, Dmitry Guryanov wrote:
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;
you can drop the ./ in front of the XPath query, it's redundant, but harmless :-)
+ 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;
}
ACK, daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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

On Tue, Dec 04, 2012 at 05:43:09PM +0400, Dmitry Guryanov wrote:
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++) {
Again variable decalration goes in block start, so squashing in the following: diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c index ad15687..90fa104 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -1448,12 +1448,13 @@ int parallelsStorageVolumeDefRemove(virStoragePoolObjPtr privpool, { int ret = -1; char *xml_path = NULL; + size_t i; privpool->def->allocation -= privvol->allocation; privpool->def->available = (privpool->def->capacity - privpool->def->allocation); - for (size_t i = 0; i < privpool->volumes.count; i++) { + for (i = 0; i < privpool->volumes.count; i++) { if (privpool->volumes.objs[i] == privvol) { xml_path = parallelsAddFileExt(privvol->target.path, ".xml"); if (!xml_path) Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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

On Tue, Dec 04, 2012 at 05:43:10PM +0400, Dmitry Guryanov wrote:
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,
yeah, but the function must be labelled static if it is not exported so adding: diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index bbbc9d5..8274c88 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -76,7 +76,7 @@ static int parallelsClose(virConnectPtr conn); -const char * parallelsGetDiskBusName(int bus) { +static const char * parallelsGetDiskBusName(int bus) { switch (bus) { case VIR_DOMAIN_DISK_BUS_IDE: return "ide"; Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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

On Tue, Dec 04, 2012 at 05:43:11PM +0400, Dmitry Guryanov wrote:
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;
again need to be moved to block start
+ 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++) {
more here
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);
interesting bug, I'm surprized this wasn't caught at compile time
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++)
same for variable declarations
+ 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; }
squashing the following in: diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index 60bf8b5..118dc13 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -1505,12 +1505,12 @@ static int parallelsAddHddByVolume(parallelsDomObjPtr pdom, virStorageVolDefPtr voldef) { int ret = -1; + const char *strbus; + 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); @@ -1597,10 +1597,12 @@ parallelsApplyDisksParams(virConnectPtr conn, parallelsDomObjPtr pdom, virDomainDiskDefPtr *olddisks, int nold, virDomainDiskDefPtr *newdisks, int nnew) { - for (int i = 0; i < nold; i++) { + int i, j; + + for (i = 0; i < nold; i++) { virDomainDiskDefPtr newdisk = NULL; virDomainDiskDefPtr olddisk = olddisks[i]; - for (int j = 0; j < nnew; j++) { + for (j = 0; j < nnew; j++) { if (STREQ_NULLABLE(newdisks[j]->dst, olddisk->dst)) { newdisk = newdisks[j]; break; @@ -1647,10 +1649,10 @@ parallelsApplyDisksParams(virConnectPtr conn, parallelsDomObjPtr pdom, } } - for (int i = 0; i < nnew; i++) { + for (i = 0; i < nnew; i++) { virDomainDiskDefPtr newdisk = newdisks[i]; bool found = false; - for (int j = 0; j < nold; j++) + for (j = 0; j < nold; j++) if (STREQ_NULLABLE(olddisks[j]->dst, newdisk->dst)) found = true; if (found) Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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

On Tue, Dec 04, 2012 at 05:43:12PM +0400, Dmitry Guryanov wrote:
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;
ACK, Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

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

On Tue, Dec 04, 2012 at 05:43:13PM +0400, Dmitry Guryanov wrote:
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 ||
ACK, Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

On Tue, Dec 04, 2012 at 05:43:00PM +0400, Dmitry Guryanov wrote:
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
Changes in v4: fixed rebase errors
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(-)
Okay, pushed with the 4 fixups that I raised ! Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

On 121211 16:27:51, Daniel Veillard wrote:
On Tue, Dec 04, 2012 at 05:43:00PM +0400, Dmitry Guryanov wrote:
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
Changes in v4: fixed rebase errors
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(-)
Okay, pushed with the 4 fixups that I raised !
Thank you very much, Daniel, for review and fixes ! I have one more patch series for networking support. I think it's better to send it after release, isn't it ?
Daniel
-- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

On Tue, Dec 11, 2012 at 12:45:24PM +0400, Dmitry Guryanov wrote:
On 121211 16:27:51, Daniel Veillard wrote:
On Tue, Dec 04, 2012 at 05:43:00PM +0400, Dmitry Guryanov wrote:
This patch series adds ability to list existing disk images, create a new ones and add or remove hard disk devices to the VM. [...] Thank you very much, Daniel, for review and fixes !
I have one more patch series for networking support. I think it's better to send it after release, isn't it ?
Well if it's not too big and you can send a rebased version within an hour or so I will check before freezing for rc1 later today :-) Youre not the only one trying to push things today :-), so send them I will check ! No promises though ... Daniel -- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/

On 121211 16:50:50, Daniel Veillard wrote:
On Tue, Dec 11, 2012 at 12:45:24PM +0400, Dmitry Guryanov wrote:
On 121211 16:27:51, Daniel Veillard wrote:
On Tue, Dec 04, 2012 at 05:43:00PM +0400, Dmitry Guryanov wrote:
This patch series adds ability to list existing disk images, create a new ones and add or remove hard disk devices to the VM. [...] Thank you very much, Daniel, for review and fixes !
I have one more patch series for networking support. I think it's better to send it after release, isn't it ?
Well if it's not too big and you can send a rebased version within an hour or so I will check before freezing for rc1 later today :-) Youre not the only one trying to push things today :-), so send them I will check ! No promises though ...
OK, I'll send it, but it's quite big ...
Daniel
-- Daniel Veillard | Open Source and Standards, Red Hat veillard@redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (2)
-
Daniel Veillard
-
Dmitry Guryanov