18.11.2014 16:16, Dmitry Guryanov пишет:
From: Alexander Burluka <aburluka(a)parallels.com>
Subscribe to events from parallels server. It's
needed for 2 things: to update cached domains list
and to send corresponding libvirt events.
Parallels server sends a lot of different events, in
this patch we handle only some of them. In the future
we can handle for example, changes in a host network
configuration or devices states.
Signed-off-by: Dmitry Guryanov <dguryanov(a)parallels.com>
---
src/parallels/parallels_driver.c | 46 ++++++
src/parallels/parallels_sdk.c | 295 ++++++++++++++++++++++++++++++++++++++-
src/parallels/parallels_sdk.h | 2 +
3 files changed, 341 insertions(+), 2 deletions(-)
diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c
index 83995d5..e145766 100644
--- a/src/parallels/parallels_driver.c
+++ b/src/parallels/parallels_driver.c
@@ -223,6 +223,12 @@ parallelsOpenDefault(virConnectPtr conn)
if (!(privconn->domains = virDomainObjListNew()))
goto error;
+ if (!(privconn->domainEventState = virObjectEventStateNew()))
+ goto error;
+
+ if (prlsdkSubscribeToPCSEvents(privconn))
+ goto error;
+
conn->privateData = privconn;
if (prlsdkLoadDomains(privconn))
@@ -234,6 +240,7 @@ parallelsOpenDefault(virConnectPtr conn)
virObjectUnref(privconn->domains);
virObjectUnref(privconn->caps);
virStoragePoolObjListFree(&privconn->pools);
+ virObjectEventStateFree(privconn->domainEventState);
prlsdkDisconnect(privconn);
prlsdkDeinit();
err_free:
@@ -280,9 +287,11 @@ parallelsConnectClose(virConnectPtr conn)
parallelsConnPtr privconn = conn->privateData;
parallelsDriverLock(privconn);
+ prlsdkUnsubscribeFromPCSEvents(privconn);
virObjectUnref(privconn->caps);
virObjectUnref(privconn->xmlopt);
virObjectUnref(privconn->domains);
+ virObjectEventStateFree(privconn->domainEventState);
prlsdkDisconnect(privconn);
conn->privateData = NULL;
prlsdkDeinit();
@@ -1717,6 +1726,41 @@ parallelsNodeGetCPUMap(virConnectPtr conn ATTRIBUTE_UNUSED,
return nodeGetCPUMap(cpumap, online, flags);
}
+static int
+parallelsConnectDomainEventRegisterAny(virConnectPtr conn,
+ virDomainPtr domain,
+ int eventID,
+ virConnectDomainEventGenericCallback callback,
+ void *opaque,
+ virFreeCallback freecb)
+{
+ int ret = -1;
+ parallelsConnPtr privconn = conn->privateData;
+ if (virDomainEventStateRegisterID(conn,
+ privconn->domainEventState,
+ domain, eventID,
+ callback, opaque, freecb, &ret) < 0)
+ ret = -1;
+ return ret;
+}
+
+static int
+parallelsConnectDomainEventDeregisterAny(virConnectPtr conn,
+ int callbackID)
+{
+ parallelsConnPtr privconn = conn->privateData;
+ int ret = -1;
+
+ if (virObjectEventStateDeregisterID(conn,
+ privconn->domainEventState,
+ callbackID) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ return ret;
+}
static virHypervisorDriver parallelsDriver = {
.no = VIR_DRV_PARALLELS,
@@ -1749,6 +1793,8 @@ static virHypervisorDriver parallelsDriver = {
.domainShutdown = parallelsDomainShutdown, /* 0.10.0 */
.domainCreate = parallelsDomainCreate, /* 0.10.0 */
.domainDefineXML = parallelsDomainDefineXML, /* 0.10.0 */
+ .connectDomainEventRegisterAny = parallelsConnectDomainEventRegisterAny, /* 1.2.10
*/
+ .connectDomainEventDeregisterAny = parallelsConnectDomainEventDeregisterAny, /*
1.2.10 */
.nodeGetCPUMap = parallelsNodeGetCPUMap, /* 1.2.8 */
.connectIsEncrypted = parallelsConnectIsEncrypted, /* 1.2.5 */
.connectIsSecure = parallelsConnectIsSecure, /* 1.2.5 */
diff --git a/src/parallels/parallels_sdk.c b/src/parallels/parallels_sdk.c
index c6cf78a..01efc22 100644
--- a/src/parallels/parallels_sdk.c
+++ b/src/parallels/parallels_sdk.c
@@ -27,6 +27,7 @@
#include "virstring.h"
#include "nodeinfo.h"
#include "virlog.h"
+#include "datatypes.h"
#include "parallels_sdk.h"
@@ -1130,9 +1131,7 @@ prlsdkLoadDomain(parallelsConnPtr privconn,
* for state and domain name */
dom = olddom;
virDomainDefFree(dom->def);
- virDomainDefFree(dom->newDef);
dom->def = def;
- dom->newDef = def;
} else {
if (!(dom = virDomainObjListAdd(privconn->domains, def,
privconn->xmlopt,
@@ -1247,3 +1246,295 @@ prlsdkAddDomain(parallelsConnPtr privconn, const unsigned char
*uuid)
PrlHandle_Free(sdkdom);
return dom;
}
+
+static int
+prlsdkUpdateDomain(parallelsConnPtr privconn, virDomainObjPtr dom)
+{
+ PRL_HANDLE job;
+ virDomainObjPtr retdom = NULL;
+ parallelsDomObjPtr pdom = dom->privateData;
+
+ job = PrlVm_RefreshConfig(pdom->sdkdom);
+ if (waitJob(job, privconn->jobTimeout))
+ return -1;
+
+ retdom = prlsdkLoadDomain(privconn, pdom->sdkdom, dom);
+ return retdom ? 0 : -1;
+}
+
+static int prlsdkSendEvent(parallelsConnPtr privconn,
+ virDomainObjPtr dom,
+ virDomainEventType lvEventType,
+ int lvEventTypeDetails)
+{
+ virObjectEventPtr event = NULL;
+
+ event = virDomainEventLifecycleNewFromObj(dom,
+ lvEventType,
+ lvEventTypeDetails);
+ if (!event)
+ return -1;
+
+ virObjectEventStateQueue(privconn->domainEventState, event);
+ return 0;
+}
+
+static void
+prlsdkNewStateToEvent(VIRTUAL_MACHINE_STATE domainState,
+ virDomainEventType *lvEventType,
+ int *lvEventTypeDetails)
+{
+ /* We skip all intermediate states here, because
+ * libvirt doesn't have correspoding event types for
+ * them */
+ switch (domainState) {
+ case VMS_STOPPED:
+ case VMS_MOUNTED:
+ *lvEventType = VIR_DOMAIN_EVENT_STOPPED;
+ *lvEventTypeDetails = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN;
+ break;
+ case VMS_RUNNING:
+ *lvEventType = VIR_DOMAIN_EVENT_STARTED;
+ *lvEventTypeDetails = VIR_DOMAIN_EVENT_STARTED_BOOTED;
+ break;
+ case VMS_PAUSED:
+ *lvEventType = VIR_DOMAIN_EVENT_SUSPENDED;
+ *lvEventTypeDetails = VIR_DOMAIN_EVENT_SUSPENDED_PAUSED;
+ break;
+ case VMS_SUSPENDED:
+ *lvEventType = VIR_DOMAIN_EVENT_STOPPED;
+ *lvEventTypeDetails = VIR_DOMAIN_EVENT_STOPPED_SAVED;
+ break;
+ default:
+ VIR_DEBUG("Skip sending event about changing state to %X",
+ domainState);
+ break;
+ }
+}
+
+static PRL_RESULT
+prlsdkHandleVmStateEvent(parallelsConnPtr privconn,
+ PRL_HANDLE prlEvent,
+ unsigned char *uuid)
+{
+ PRL_RESULT pret = PRL_ERR_FAILURE;
+ PRL_HANDLE eventParam = PRL_INVALID_HANDLE;
+ PRL_INT32 domainState;
+ virDomainObjPtr dom = NULL;
+ parallelsDomObjPtr pdom;
+ virDomainEventType lvEventType;
+ int lvEventTypeDetails;
+
+ pret = PrlEvent_GetParamByName(prlEvent, "vminfo_vm_state",
&eventParam);
+ prlsdkCheckRetGoto(pret, cleanup);
+
+ pret = PrlEvtPrm_ToInt32(eventParam, &domainState);
+ prlsdkCheckRetGoto(pret, cleanup);
+
+ dom = virDomainObjListFindByUUID(privconn->domains, uuid);
+ if (dom == NULL) {
+ pret = PRL_ERR_VM_UUID_NOT_FOUND;
+ goto cleanup;
+ }
+
+ pdom = dom->privateData;
+ if (prlsdkConvertDomainState(domainState, pdom->id, dom) < 0)
+ goto cleanup;
+
+ prlsdkNewStateToEvent(domainState,
+ &lvEventType,
+ &lvEventTypeDetails);
+
+ if (prlsdkSendEvent(privconn, dom, lvEventType, lvEventTypeDetails) < 0) {
+ pret = PRL_ERR_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+
+ cleanup:
+ if (dom)
+ virObjectUnlock(dom);
+ return pret;
+}
+
+static PRL_RESULT
+prlsdkHandleVmConfigEvent(parallelsConnPtr privconn,
+ unsigned char *uuid)
+{
+ PRL_RESULT pret = PRL_ERR_FAILURE;
+ virDomainObjPtr dom = NULL;
+
+ dom = virDomainObjListFindByUUID(privconn->domains, uuid);
+ if (dom == NULL) {
+ pret = PRL_ERR_VM_UUID_NOT_FOUND;
+ goto cleanup;
+ }
+
+ if (prlsdkUpdateDomain(privconn, dom) < 0)
+ goto cleanup;
+
+ if (prlsdkSendEvent(privconn, dom, VIR_DOMAIN_EVENT_DEFINED,
+ VIR_DOMAIN_EVENT_DEFINED_UPDATED) < 0) {
+ pret = PRL_ERR_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+
+ pret = PRL_ERR_SUCCESS;
+ cleanup:
+ if (dom)
+ virObjectUnlock(dom);
+ return pret;
+}
+
+static PRL_RESULT
+prlsdkHandleVmAddedEvent(parallelsConnPtr privconn,
+ unsigned char *uuid)
+{
+ PRL_RESULT pret;
+ virDomainObjPtr dom = NULL;
+
+ dom = prlsdkAddDomain(privconn, uuid);
+ if (!dom)
+ return PRL_ERR_FAILURE;
+
+ if (prlsdkSendEvent(privconn, dom, VIR_DOMAIN_EVENT_DEFINED,
+ VIR_DOMAIN_EVENT_DEFINED_ADDED) < 0) {
+ pret = PRL_ERR_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+
+ pret = PRL_ERR_SUCCESS;
+ cleanup:
+ if (dom)
+ virObjectUnlock(dom);
+ return pret;
+}
+
+static PRL_RESULT
+prlsdkHandleVmRemovedEvent(parallelsConnPtr privconn,
+ unsigned char *uuid)
+{
+ virDomainObjPtr dom = NULL;
+ PRL_RESULT pret = PRL_ERR_SUCCESS;
+
+ dom = virDomainObjListFindByUUID(privconn->domains, uuid);
+ if (dom == NULL) {
+ /* domain was removed from the list from the libvirt
+ * API function in current connection */
+ return PRL_ERR_SUCCESS;
+ }
+
+ if (prlsdkSendEvent(privconn, dom, VIR_DOMAIN_EVENT_UNDEFINED,
+ VIR_DOMAIN_EVENT_UNDEFINED_REMOVED) < 0)
+ pret = PRL_ERR_OUT_OF_MEMORY;
+
+ virDomainObjListRemove(privconn->domains, dom);
+ return pret;
+}
+
+static PRL_RESULT
+prlsdkHandleVmEvent(parallelsConnPtr privconn, PRL_HANDLE prlEvent)
+{
+ PRL_RESULT pret;
+ char uuidstr[VIR_UUID_STRING_BUFLEN + 2];
+ unsigned char uuid[VIR_UUID_BUFLEN];
+ PRL_UINT32 bufsize = ARRAY_CARDINALITY(uuidstr);
+ PRL_EVENT_TYPE prlEventType;
+
+ pret = PrlEvent_GetType(prlEvent, &prlEventType);
+ prlsdkCheckRetGoto(pret, error);
+
+ pret = PrlEvent_GetIssuerId(prlEvent, uuidstr, &bufsize);
+ prlsdkCheckRetGoto(pret, error);
+
+ if (prlsdkUUIDParse(uuidstr, uuid) < 0)
+ return PRL_ERR_FAILURE;
+
+ switch (prlEventType) {
+ case PET_DSP_EVT_VM_STATE_CHANGED:
+ return prlsdkHandleVmStateEvent(privconn, prlEvent, uuid);
+ case PET_DSP_EVT_VM_CONFIG_CHANGED:
+ return prlsdkHandleVmConfigEvent(privconn, uuid);
+ case PET_DSP_EVT_VM_CREATED:
+ case PET_DSP_EVT_VM_ADDED:
+ return prlsdkHandleVmAddedEvent(privconn, uuid);
+ case PET_DSP_EVT_VM_DELETED:
+ case PET_DSP_EVT_VM_UNREGISTERED:
+ return prlsdkHandleVmRemovedEvent(privconn, uuid);
+ break;
+ default:
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Can't handle event of type %d"), prlEventType);
+ return PRL_ERR_FAILURE;
+ }
+
+ error:
+ return PRL_ERR_FAILURE;
+}
+
+static PRL_RESULT
+prlsdkEventsHandler(PRL_HANDLE prlEvent, PRL_VOID_PTR opaque)
+{
+ parallelsConnPtr privconn = opaque;
+ PRL_RESULT pret = PRL_ERR_UNINITIALIZED;
+ PRL_HANDLE_TYPE handleType;
+ PRL_EVENT_TYPE prlEventType;
+
+ pret = PrlHandle_GetType(prlEvent, &handleType);
+ prlsdkCheckRetGoto(pret, cleanup);
+
+ if (handleType != PHT_EVENT) {
+ /* Currently, there is no need to handle anything but events */
+ pret = PRL_ERR_SUCCESS;
+ goto cleanup;
+ }
+
+ if (privconn == NULL) {
+ pret = PRL_ERR_INVALID_ARG;
+ goto cleanup;
+ }
+
+ PrlEvent_GetType(prlEvent, &prlEventType);
+ prlsdkCheckRetGoto(pret, cleanup);
+
+ switch (prlEventType) {
+ case PET_DSP_EVT_VM_STATE_CHANGED:
+ case PET_DSP_EVT_VM_CONFIG_CHANGED:
+ case PET_DSP_EVT_VM_CREATED:
+ case PET_DSP_EVT_VM_ADDED:
+ case PET_DSP_EVT_VM_DELETED:
+ case PET_DSP_EVT_VM_UNREGISTERED:
+ pret = prlsdkHandleVmEvent(privconn, prlEvent);
+ break;
+ default:
+ VIR_DEBUG("Skipping event of type %d", prlEventType);
+ }
+
+ pret = PRL_ERR_SUCCESS;
+ cleanup:
+ PrlHandle_Free(prlEvent);
+ return pret;
+}
+
+int prlsdkSubscribeToPCSEvents(parallelsConnPtr privconn)
+{
+ PRL_RESULT pret = PRL_ERR_UNINITIALIZED;
+
+ pret = PrlSrv_RegEventHandler(privconn->server,
+ prlsdkEventsHandler,
+ privconn);
+ prlsdkCheckRetGoto(pret, error);
+ return 0;
+
+ error:
+ return -1;
+}
+
+void prlsdkUnsubscribeFromPCSEvents(parallelsConnPtr privconn)
+{
+ PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
+ ret = PrlSrv_UnregEventHandler(privconn->server,
+ prlsdkEventsHandler,
+ privconn);
+ if (PRL_FAILED(ret))
+ logPrlError(ret);
+}
diff --git a/src/parallels/parallels_sdk.h b/src/parallels/parallels_sdk.h
index d9461ca..5ffbf53 100644
--- a/src/parallels/parallels_sdk.h
+++ b/src/parallels/parallels_sdk.h
@@ -32,3 +32,5 @@ int
prlsdkLoadDomains(parallelsConnPtr privconn);
virDomainObjPtr
prlsdkAddDomain(parallelsConnPtr privconn, const unsigned char *uuid);
+int prlsdkSubscribeToPCSEvents(parallelsConnPtr privconn);
+void prlsdkUnsubscribeFromPCSEvents(parallelsConnPtr privconn);
I would protect
all event processing functions
(prlsdkHandleVmStateEvent, prlsdkHandleVmConfigEvent,
prlsdkHandleVmAddedEvent, prlsdkHandleVmRemovedEvent) with
parallelsDriverLock/parallelsDriverUnlock calls to prevent possible
races triggered by simultaneous operations on the same domain.
Otherwise, ack.