[libvirt] [PATCH 0/6] parallels: implement managed save

This patch is intended to implement all needed code, so that suspend/resume in openstack nova will work. It implements .domainHasManagedSaveImage, .domainManagedSave and .domainManagedSaveRemove functions. Also it adds workaround to parallelsDomainDefineXMLFlags to skip applying configuration, if VM is in managed save state and config hasn't been changed, because it's not possible to change VM config in suspended state in PCS. Dmitry Guryanov (6): parallels: fix headers in parallels_sdk.h parallels: split prlsdkDomainChangeState function parallels: implement virDomainManagedSave parallels: report, that cdroms are readonly parallels: add controllers in prlsdkLoadDomain parallels: fix virDomainDefineXML for domain in saved state src/parallels/parallels_driver.c | 101 +++++++++++++++++++++++++++++++++++++-- src/parallels/parallels_sdk.c | 65 ++++++++++++++++++------- src/parallels/parallels_sdk.h | 17 +++++-- 3 files changed, 157 insertions(+), 26 deletions(-) -- 2.1.0

Return value of functions prlsdkStart/Kill/Stop e.t.c. is PRL_RESULT in parallels_sdk.c and int in parallels_sdk.h. PRL_RESULT is int, so compiler didn't report errors. Let's fix the difference. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_sdk.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/parallels/parallels_sdk.h b/src/parallels/parallels_sdk.h index 694c19b..cb8d3fb 100644 --- a/src/parallels/parallels_sdk.h +++ b/src/parallels/parallels_sdk.h @@ -35,11 +35,11 @@ prlsdkAddDomain(parallelsConnPtr privconn, const unsigned char *uuid); int prlsdkUpdateDomain(parallelsConnPtr privconn, virDomainObjPtr dom); int prlsdkSubscribeToPCSEvents(parallelsConnPtr privconn); void prlsdkUnsubscribeFromPCSEvents(parallelsConnPtr privconn); -int prlsdkStart(parallelsConnPtr privconn, PRL_HANDLE sdkdom); -int prlsdkKill(parallelsConnPtr privconn, PRL_HANDLE sdkdom); -int prlsdkStop(parallelsConnPtr privconn, PRL_HANDLE sdkdom); -int prlsdkPause(parallelsConnPtr privconn, PRL_HANDLE sdkdom); -int prlsdkResume(parallelsConnPtr privconn, PRL_HANDLE sdkdom); +PRL_RESULT prlsdkStart(parallelsConnPtr privconn, PRL_HANDLE sdkdom); +PRL_RESULT prlsdkKill(parallelsConnPtr privconn, PRL_HANDLE sdkdom); +PRL_RESULT prlsdkStop(parallelsConnPtr privconn, PRL_HANDLE sdkdom); +PRL_RESULT prlsdkPause(parallelsConnPtr privconn, PRL_HANDLE sdkdom); +PRL_RESULT prlsdkResume(parallelsConnPtr privconn, PRL_HANDLE sdkdom); typedef PRL_RESULT (*prlsdkChangeStateFunc)(parallelsConnPtr privconn, PRL_HANDLE sdkdom); int -- 2.1.0

Split function prlsdkDomainChangeState into prlsdkDomainChangeStateLocked and prlsdkDomainChangeState. So it can be used from places, where virDomainObj already found and locked. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_sdk.c | 35 +++++++++++++++++++++-------------- src/parallels/parallels_sdk.h | 4 ++++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/parallels/parallels_sdk.c b/src/parallels/parallels_sdk.c index 0b2478e..e677d36 100644 --- a/src/parallels/parallels_sdk.c +++ b/src/parallels/parallels_sdk.c @@ -1704,22 +1704,14 @@ PRL_RESULT prlsdkResume(parallelsConnPtr privconn, PRL_HANDLE sdkdom) } int -prlsdkDomainChangeState(virDomainPtr domain, - prlsdkChangeStateFunc chstate) +prlsdkDomainChangeStateLocked(parallelsConnPtr privconn, + virDomainObjPtr dom, + prlsdkChangeStateFunc chstate) { - parallelsConnPtr privconn = domain->conn->privateData; - virDomainObjPtr dom; parallelsDomObjPtr pdom; PRL_RESULT pret; - int ret = -1; virErrorNumber virerr; - dom = virDomainObjListFindByUUID(privconn->domains, domain->uuid); - if (dom == NULL) { - parallelsDomNotFoundError(domain); - return -1; - } - pdom = dom->privateData; pret = chstate(privconn, pdom->sdkdom); virReportError(VIR_ERR_OPERATION_FAILED, @@ -1737,12 +1729,27 @@ prlsdkDomainChangeState(virDomainPtr domain, } virReportError(virerr, "%s", _("Can't change domain state.")); - goto cleanup; + return -1; } - ret = prlsdkUpdateDomain(privconn, dom); + return prlsdkUpdateDomain(privconn, dom); +} - cleanup: +int +prlsdkDomainChangeState(virDomainPtr domain, + prlsdkChangeStateFunc chstate) +{ + parallelsConnPtr privconn = domain->conn->privateData; + virDomainObjPtr dom; + int ret = -1; + + dom = virDomainObjListFindByUUID(privconn->domains, domain->uuid); + if (dom == NULL) { + parallelsDomNotFoundError(domain); + return -1; + } + + ret = prlsdkDomainChangeStateLocked(privconn, dom, chstate); virObjectUnlock(dom); return ret; } diff --git a/src/parallels/parallels_sdk.h b/src/parallels/parallels_sdk.h index cb8d3fb..780a226 100644 --- a/src/parallels/parallels_sdk.h +++ b/src/parallels/parallels_sdk.h @@ -46,6 +46,10 @@ int prlsdkDomainChangeState(virDomainPtr domain, prlsdkChangeStateFunc chstate); int +prlsdkDomainChangeStateLocked(parallelsConnPtr privconn, + virDomainObjPtr dom, + prlsdkChangeStateFunc chstate); +int prlsdkApplyConfig(virConnectPtr conn, virDomainObjPtr dom, virDomainDefPtr new); -- 2.1.0

Implement virDomainManagedSave api function. In PCS this feature called "suspend". You can suspend VM or CT while it is in running or paused state. And after resuming (or starting) it will have the same state, as before suspend. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_driver.c | 69 +++++++++++++++++++++++++++++++++++++++- src/parallels/parallels_sdk.c | 21 ++++++++++++ src/parallels/parallels_sdk.h | 3 ++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index bf29a96..9be2c6b 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -971,6 +971,8 @@ parallelsDomainHasManagedSaveImage(virDomainPtr domain, unsigned int flags) { parallelsConnPtr privconn = domain->conn->privateData; virDomainObjPtr dom = NULL; + int state, reason; + int ret = 0; virCheckFlags(0, -1); @@ -980,9 +982,72 @@ parallelsDomainHasManagedSaveImage(virDomainPtr domain, unsigned int flags) return -1; } + state = virDomainObjGetState(dom, &reason); + if (state == VIR_DOMAIN_SHUTOFF && reason == VIR_DOMAIN_SHUTOFF_SAVED) + ret = 1; virObjectUnlock(dom); - return 0; + return ret; +} + +static int +parallelsDomainManagedSave(virDomainPtr domain, unsigned int flags) +{ + parallelsConnPtr privconn = domain->conn->privateData; + virDomainObjPtr dom = NULL; + int state, reason; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_SAVE_RUNNING | + VIR_DOMAIN_SAVE_PAUSED, -1); + + dom = virDomainObjListFindByUUID(privconn->domains, domain->uuid); + if (dom == NULL) { + parallelsDomNotFoundError(domain); + return -1; + } + + state = virDomainObjGetState(dom, &reason); + + if (state == VIR_DOMAIN_RUNNING && (flags & VIR_DOMAIN_SAVE_PAUSED)) { + ret = prlsdkDomainChangeStateLocked(privconn, dom, prlsdkPause); + if (ret) + goto cleanup; + } + + ret = prlsdkDomainChangeStateLocked(privconn, dom, prlsdkSuspend); + + cleanup: + virObjectUnlock(dom); + return ret; +} + +static int +parallelsDomainManagedSaveRemove(virDomainPtr domain, unsigned int flags) +{ + parallelsConnPtr privconn = domain->conn->privateData; + virDomainObjPtr dom = NULL; + int state, reason; + int ret = -1; + + virCheckFlags(0, -1); + + dom = virDomainObjListFindByUUID(privconn->domains, domain->uuid); + if (dom == NULL) { + parallelsDomNotFoundError(domain); + return -1; + } + + state = virDomainObjGetState(dom, &reason); + + if (!(state == VIR_DOMAIN_SHUTOFF && reason == VIR_DOMAIN_SHUTOFF_SAVED)) + goto cleanup; + + ret = prlsdkDomainManagedSaveRemove(privconn, dom); + + cleanup: + virObjectUnlock(dom); + return ret; } static virHypervisorDriver parallelsDriver = { @@ -1027,6 +1092,8 @@ static virHypervisorDriver parallelsDriver = { .connectIsSecure = parallelsConnectIsSecure, /* 1.2.5 */ .connectIsAlive = parallelsConnectIsAlive, /* 1.2.5 */ .domainHasManagedSaveImage = parallelsDomainHasManagedSaveImage, /* 1.2.13 */ + .domainManagedSave = parallelsDomainManagedSave, /* 1.2.14 */ + .domainManagedSaveRemove = parallelsDomainManagedSaveRemove, /* 1.2.14 */ }; static virConnectDriver parallelsConnectDriver = { diff --git a/src/parallels/parallels_sdk.c b/src/parallels/parallels_sdk.c index e677d36..6b3321b 100644 --- a/src/parallels/parallels_sdk.c +++ b/src/parallels/parallels_sdk.c @@ -1703,6 +1703,14 @@ PRL_RESULT prlsdkResume(parallelsConnPtr privconn, PRL_HANDLE sdkdom) return waitJob(job, privconn->jobTimeout); } +PRL_RESULT prlsdkSuspend(parallelsConnPtr privconn, PRL_HANDLE sdkdom) +{ + PRL_HANDLE job = PRL_INVALID_HANDLE; + + job = PrlVm_Suspend(sdkdom); + return waitJob(job, privconn->jobTimeout); +} + int prlsdkDomainChangeStateLocked(parallelsConnPtr privconn, virDomainObjPtr dom, @@ -3089,3 +3097,16 @@ prlsdkUnregisterDomain(parallelsConnPtr privconn, virDomainObjPtr dom) virDomainObjListRemove(privconn->domains, dom); return 0; } + +int +prlsdkDomainManagedSaveRemove(parallelsConnPtr privconn, virDomainObjPtr dom) +{ + parallelsDomObjPtr privdom = dom->privateData; + PRL_HANDLE job; + + job = PrlVm_DropSuspendedState(privdom->sdkdom); + if (PRL_FAILED(waitJob(job, privconn->jobTimeout))) + return -1; + + return 0; +} diff --git a/src/parallels/parallels_sdk.h b/src/parallels/parallels_sdk.h index 780a226..b084678 100644 --- a/src/parallels/parallels_sdk.h +++ b/src/parallels/parallels_sdk.h @@ -40,6 +40,7 @@ PRL_RESULT prlsdkKill(parallelsConnPtr privconn, PRL_HANDLE sdkdom); PRL_RESULT prlsdkStop(parallelsConnPtr privconn, PRL_HANDLE sdkdom); PRL_RESULT prlsdkPause(parallelsConnPtr privconn, PRL_HANDLE sdkdom); PRL_RESULT prlsdkResume(parallelsConnPtr privconn, PRL_HANDLE sdkdom); +PRL_RESULT prlsdkSuspend(parallelsConnPtr privconn, PRL_HANDLE sdkdom); typedef PRL_RESULT (*prlsdkChangeStateFunc)(parallelsConnPtr privconn, PRL_HANDLE sdkdom); int @@ -57,3 +58,5 @@ int prlsdkCreateVm(virConnectPtr conn, virDomainDefPtr def); int prlsdkCreateCt(virConnectPtr conn, virDomainDefPtr def); int prlsdkUnregisterDomain(parallelsConnPtr privconn, virDomainObjPtr dom); +int +prlsdkDomainManagedSaveRemove(parallelsConnPtr privconn, virDomainObjPtr dom); -- 2.1.0

When retriving list of domains from PCS, set readonly flag for cdrom devices. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_sdk.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/parallels/parallels_sdk.c b/src/parallels/parallels_sdk.c index 6b3321b..ea090b7 100644 --- a/src/parallels/parallels_sdk.c +++ b/src/parallels/parallels_sdk.c @@ -478,10 +478,12 @@ prlsdkGetDiskInfo(PRL_HANDLE prldisk, virDomainDiskSetFormat(disk, VIR_STORAGE_FILE_RAW); } - if (isCdrom) + if (isCdrom) { disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; - else + disk->src->readonly = true; + } else { disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; + } pret = PrlVmDev_GetFriendlyName(prldisk, NULL, &buflen); prlsdkCheckRetGoto(pret, cleanup); -- 2.1.0

Call virDomainDefAddImplicitControllers to add disk controllers, so virDomainDef, filled by this function will look exactly like the one returned by virDomainDefParseString. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_sdk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/parallels/parallels_sdk.c b/src/parallels/parallels_sdk.c index ea090b7..4441524 100644 --- a/src/parallels/parallels_sdk.c +++ b/src/parallels/parallels_sdk.c @@ -1247,6 +1247,9 @@ prlsdkLoadDomain(parallelsConnPtr privconn, pret = PrlVmCfg_GetHomePath(sdkdom, pdom->home, &buflen); prlsdkCheckRetGoto(pret, error); + if (virDomainDefAddImplicitControllers(def) < 0) + goto error; + if (olddom) { /* assign new virDomainDef without any checks */ /* we can't use virDomainObjAssignDef, because it checks -- 2.1.0

PCS doesn't store domain config in managed save state file. It's forbidden to change config for VMs in this state. It's possible to change config for containers, but after restoring domain will have that new config, not a config, which domain had at the moment of virDomainManagedSave. So we need to handle this case differently from other states. Let's forbid this operation, if config is changed and if it's not changed - just do nothing. Openstack/nova calls virDomainDefineXML on resume with current domain config, so we can't forbid this operation in managed save state. Signed-off-by: Dmitry Guryanov <dguryanov@parallels.com> --- src/parallels/parallels_driver.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index 9be2c6b..80facb3 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -713,11 +713,35 @@ parallelsDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int if (!olddom) goto cleanup; } else { - if (prlsdkApplyConfig(conn, olddom, def)) - goto cleanup; + int state, reason; + + state = virDomainObjGetState(olddom, &reason); + + if (state == VIR_DOMAIN_SHUTOFF && + reason == VIR_DOMAIN_SHUTOFF_SAVED) { + + /* PCS doesn't store domain config in managed save state file. + * It's forbidden to change config for VMs in this state. + * It's possible to change config for containers, but after + * restoring domain will have that new config, not a config, + * which domain had at the moment of virDomainManagedSave. + * + * So forbid this operation, if config is changed. If it's + * not changed - just do nothing. */ + + if (!virDomainDefCheckABIStability(olddom->def, def)) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Can't change domain configuration " + "in managed save state")); + goto cleanup; + } + } else { + if (prlsdkApplyConfig(conn, olddom, def)) + goto cleanup; - if (prlsdkUpdateDomain(privconn, olddom)) - goto cleanup; + if (prlsdkUpdateDomain(privconn, olddom)) + goto cleanup; + } } retdom = virGetDomain(conn, def->name, def->uuid); -- 2.1.0
participants (1)
-
Dmitry Guryanov