#include "nodeinfo.h"
#include "virstring.h"
#include "cpu/cpu.h"
+#include "virtypedparam.h"
#include "parallels_driver.h"
#include "parallels_utils.h"
return ret;
}
+static int
+parallelsDomainBlockStats(virDomainPtr domain, const char *path,
+ virDomainBlockStatsPtr stats)
+{
+ virDomainObjPtr dom = NULL;
+ int ret = -1;
+ size_t i;
+ int idx;
+
+ if (!(dom = parallelsDomObjFromDomainRef(domain)))
+ return -1;
+
+ if (*path) {
+ if ((idx = virDomainDiskIndexByName(dom->def, path, false)) < 0) {
+ virReportError(VIR_ERR_INVALID_ARG, _("invalid path: %s"), path);
+ goto cleanup;
+ }
+ if (prlsdkGetBlockStats(dom, dom->def->disks[idx], stats) < 0)
+ goto cleanup;
+ } else {
+ virDomainBlockStatsStruct s;
+
+#define PARALLELS_ZERO_STATS(VAR, TYPE, NAME) \
+ stats->VAR = 0;
+
+ PARALLELS_BLOCK_STATS_FOREACH(PARALLELS_ZERO_STATS)
+
+#undef PARALLELS_ZERO_STATS
+
+ for (i = 0; i < dom->def->ndisks; i++) {
+ if (prlsdkGetBlockStats(dom, dom->def->disks[i], &s) < 0)
+ goto cleanup;
+
+#define PARALLELS_SUM_STATS(VAR, TYPE, NAME) \
+ if (s.VAR != -1) \
+ stats->VAR += s.VAR;
+
+ PARALLELS_BLOCK_STATS_FOREACH(PARALLELS_SUM_STATS)
+
+#undef PARALLELS_SUM_STATS
+ }
+ }
+ stats->errs = -1;
+ ret = 0;
+
+ cleanup:
+ if (dom)
+ virDomainObjEndAPI(&dom);
+
+ return ret;
+}
+
+static int
+parallelsDomainBlockStatsFlags(virDomainPtr domain,
+ const char *path,
+ virTypedParameterPtr params,
+ int *nparams,
+ unsigned int flags)
+{
+ virDomainBlockStatsStruct stats;
+ int ret = -1;
+ size_t i;
+
+ virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1);
+ /* We don't return strings, and thus trivially support this flag. */
+ flags &= ~VIR_TYPED_PARAM_STRING_OKAY;
+
+ if (parallelsDomainBlockStats(domain, path, &stats) < 0)
+ goto cleanup;
+
+ if (*nparams == 0) {
+#define PARALLELS_COUNT_STATS(VAR, TYPE, NAME) \
+ if ((stats.VAR) != -1) \
+ ++*nparams;
+
+ PARALLELS_BLOCK_STATS_FOREACH(PARALLELS_COUNT_STATS)
+
+#undef PARALLELS_COUNT_STATS
+ ret = 0;
+ goto cleanup;
+ }
+
+ i = 0;
+#define PARALLELS_BLOCK_STATS_ASSIGN_PARAM(VAR, TYPE, NAME) \
+ if (i < *nparams && (stats.VAR) != -1) { \
+ if (virTypedParameterAssign(params + i, TYPE, \
+ VIR_TYPED_PARAM_LLONG, (stats.VAR)) < 0) \
+ goto cleanup; \
+ i++; \
+ }
+
+ PARALLELS_BLOCK_STATS_FOREACH(PARALLELS_BLOCK_STATS_ASSIGN_PARAM)
+
+#undef PARALLELS_BLOCK_STATS_ASSIGN_PARAM
+
+ *nparams = i;
+ ret = 0;
+
+ cleanup:
+ return ret;
+}
+
+
static virHypervisorDriver vzDriver = {
.name = "vz",
.connectOpen = parallelsConnectOpen, /* 0.10.0 */
.domainManagedSave = parallelsDomainManagedSave, /* 1.2.14 */
.domainManagedSaveRemove = parallelsDomainManagedSaveRemove, /* 1.2.14 */
.domainGetMaxMemory = parallelsDomainGetMaxMemory, /* 1.2.15 */
+ .domainBlockStats = parallelsDomainBlockStats, /* 1.2.17 */
+ .domainBlockStatsFlags = parallelsDomainBlockStatsFlags, /* 1.2.17 */
};
static virConnectDriver vzConnectDriver = {
*/
#include <config.h>
+#include <stdarg.h>
#include "virerror.h"
#include "viralloc.h"
#include "virlog.h"
#include "datatypes.h"
#include "domain_conf.h"
+#include "virtime.h"
#include "parallels_sdk.h"
return;
PrlHandle_Free(pdom->sdkdom);
+ PrlHandle_Free(pdom->cache.stats);
+ virCondDestroy(&pdom->cache.cond);
VIR_FREE(pdom->uuid);
VIR_FREE(pdom->home);
VIR_FREE(p);
* to NULL temporarily */
pdom->uuid = NULL;
+ pdom->cache.stats = PRL_INVALID_HANDLE;
+ pdom->cache.count = -1;
+ if (virCondInit(&pdom->cache.cond) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot initialize condition"));
+ goto error;
+ }
+
if (prlsdkGetDomainIds(sdkdom, &def->name, def->uuid) < 0)
goto error;
return;
}
+#define PARALLELS_STATISTICS_DROP_COUNT 3
+
+static PRL_RESULT
+prlsdkHandlePerfEvent(parallelsConnPtr privconn,
+ PRL_HANDLE event,
+ unsigned char *uuid)
+{
+ virDomainObjPtr dom = NULL;
+ parallelsDomObjPtr privdom = NULL;
+ PRL_HANDLE job = PRL_INVALID_HANDLE;
+
+ dom = virDomainObjListFindByUUID(privconn->domains, uuid);
+ if (dom == NULL)
+ goto cleanup;
+ privdom = dom->privateData;
+
+ // delayed event after unsubscribe
+ if (privdom->cache.count == -1)
+ goto cleanup;
+
+ PrlHandle_Free(privdom->cache.stats);
+ privdom->cache.stats = PRL_INVALID_HANDLE;
+
+ if (privdom->cache.count > PARALLELS_STATISTICS_DROP_COUNT) {
+ job = PrlVm_UnsubscribeFromPerfStats(privdom->sdkdom);
+ if (PRL_FAILED(waitJob(job)))
+ goto cleanup;
+ // change state to unsubscribed
+ privdom->cache.count = -1;
+ } else {
+ ++privdom->cache.count;
+ privdom->cache.stats = event;
+ // thus we get own of event handle
+ event = PRL_INVALID_HANDLE;
+ virCondSignal(&privdom->cache.cond);
+ }
+
+ cleanup:
+ PrlHandle_Free(event);
+ if (dom)
+ virObjectUnlock(dom);
+
+ return PRL_ERR_SUCCESS;
+}
+
static void
prlsdkHandleVmEvent(parallelsConnPtr privconn, PRL_HANDLE prlEvent)
{
PRL_EVENT_TYPE prlEventType;
pret = PrlEvent_GetType(prlEvent, &prlEventType);
- prlsdkCheckRetGoto(pret, error);
+ prlsdkCheckRetGoto(pret, cleanup);
pret = PrlEvent_GetIssuerId(prlEvent, uuidstr, &bufsize);
- prlsdkCheckRetGoto(pret, error);
+ prlsdkCheckRetGoto(pret, cleanup);
if (prlsdkUUIDParse(uuidstr, uuid) < 0)
- return;
+ goto cleanup;
switch (prlEventType) {
case PET_DSP_EVT_VM_STATE_CHANGED:
case PET_DSP_EVT_VM_UNREGISTERED:
prlsdkHandleVmRemovedEvent(privconn, uuid);
break;
+ case PET_DSP_EVT_VM_PERFSTATS:
+ prlsdkHandlePerfEvent(privconn, prlEvent, uuid);
+ // above function takes own of event
+ prlEvent = PRL_INVALID_HANDLE;
+ break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Can't handle event of type %d"), prlEventType);
}
- error:
+ cleanup:
+ PrlHandle_Free(prlEvent);
return;
}
switch (prlIssuerType) {
case PIE_VIRTUAL_MACHINE:
prlsdkHandleVmEvent(privconn, prlEvent);
+ // above function takes own of event
+ prlEvent = PRL_INVALID_HANDLE;
break;
default:
VIR_DEBUG("Skipping event of issuer type %d", prlIssuerType);
return PRL_ERR_SUCCESS;
}
+
int prlsdkSubscribeToPCSEvents(parallelsConnPtr privconn)
{
PRL_RESULT pret = PRL_ERR_UNINITIALIZED;
return 0;
}
+
+static int
+prlsdkExtractStatsParam(PRL_HANDLE sdkstats, const char *name, long long *val)
+{
+ PRL_HANDLE param = PRL_INVALID_HANDLE;
+ PRL_RESULT pret;
+ PRL_INT64 pval = 0;
+ int ret = -1;
+
+ pret = PrlEvent_GetParamByName(sdkstats, name, ¶m);
+ if (pret == PRL_ERR_NO_DATA) {
+ *val = -1;
+ ret = 0;
+ goto cleanup;
+ } else if (PRL_FAILED(pret)) {
+ logPrlError(pret);
+ goto cleanup;
+ }
+ pret = PrlEvtPrm_ToInt64(param, &pval);
+ prlsdkCheckRetGoto(pret, cleanup);
+
+ *val = pval;
+ ret = 0;
+
+ cleanup:
+ PrlHandle_Free(param);
+ return ret;
+}
+
+#define PARALLELS_STATISTICS_TIMEOUT (60 * 1000)
+
+static int
+prlsdkGetStatsParam(virDomainObjPtr dom, const char *name, long long *val)
+{
+ parallelsDomObjPtr privdom = dom->privateData;
+ PRL_HANDLE job = PRL_INVALID_HANDLE;
+ unsigned long long now;
+
+ if (privdom->cache.stats != PRL_INVALID_HANDLE) {
+ // reset count to keep subscribtion
+ privdom->cache.count = 0;
+ return prlsdkExtractStatsParam(privdom->cache.stats, name, val);
+ }
+
+ if (privdom->cache.count == -1) {
+ job = PrlVm_SubscribeToPerfStats(privdom->sdkdom, NULL);
+ if (PRL_FAILED(waitJob(job)))
+ goto error;
+ }
+
+ // change state to subscribed in case of unsubscribed
+ // or reset count so we stop unsubscribe attempts
+ privdom->cache.count = 0;
+
+ if (virTimeMillisNow(&now) < 0) {
+ virReportSystemError(errno, "%s", _("Unable to get current time"));
+ goto error;
+ }
+
+ while (privdom->cache.stats == PRL_INVALID_HANDLE) {
+ if (virCondWaitUntil(&privdom->cache.cond, &dom->parent.lock,
+ now + PARALLELS_STATISTICS_TIMEOUT) < 0) {
+ if (errno == ETIMEDOUT) {
+ virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s",
+ _("Timeout on waiting statistics event."));
+ goto error;
+ } else {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to wait on monitor condition"));
+ goto error;
+ }
+ }
+ }
+
+ return prlsdkExtractStatsParam(privdom->cache.stats, name, val);
+ error:
+ return -1;
+}
+
+int
+prlsdkGetBlockStats(virDomainObjPtr dom, virDomainDiskDefPtr disk, virDomainBlockStatsPtr stats)
+{
+ virDomainDeviceDriveAddressPtr address;
+ int idx;
+ const char *prefix;
+ int ret = -1;
+ char *name = NULL;
+
+ address = &disk->info.addr.drive;
+ switch (disk->bus) {
+ case VIR_DOMAIN_DISK_BUS_IDE:
+ prefix = "ide";
+ idx = address->bus * 2 + address->unit;
+ break;
+ case VIR_DOMAIN_DISK_BUS_SATA:
+ prefix = "sata";
+ idx = address->unit;
+ break;
+ case VIR_DOMAIN_DISK_BUS_SCSI:
+ prefix = "scsi";
+ idx = address->unit;
+ break;
+ default:
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unknown disk bus: %X"), disk->bus);
+ goto cleanup;
+ }
+
+
+#define PRLSDK_GET_STAT_PARAM(VAL, TYPE, NAME) \
+ if (virAsprintf(&name, "devices.%s%d.%s", prefix, idx, NAME) < 0) \
+ goto cleanup; \
+ if (prlsdkGetStatsParam(dom, name, &stats->VAL) < 0) \
+ goto cleanup; \
+ VIR_FREE(name);
+
+ PARALLELS_BLOCK_STATS_FOREACH(PRLSDK_GET_STAT_PARAM)
+
+#undef PRLSDK_GET_STAT_PARAM
+
+ ret = 0;
+
+ cleanup:
+
+ VIR_FREE(name);
+ return ret;
+}