This implementation uses the
https://esx-server/screen?id=<id>
way to get
a screenshot of a running domain. Compared to the CreateScreenshot_Task
way this works since ESX 2.5 while CreateScreenshot_Task was added in
version 4.0.
The newly added libcurl stream driver is used to directly provide the
downloaded data without saving it to a temporary file first.
---
src/esx/esx_driver.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++-
src/esx/esx_private.h | 1 +
2 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
index ff44881..7b6b1a0 100644
--- a/src/esx/esx_driver.c
+++ b/src/esx/esx_driver.c
@@ -2,7 +2,7 @@
* esx_driver.c: core driver functions for managing VMware ESX hosts
*
* Copyright (C) 2010-2014 Red Hat, Inc.
- * Copyright (C) 2009-2013 Matthias Bolte <matthias.bolte(a)googlemail.com>
+ * Copyright (C) 2009-2014 Matthias Bolte <matthias.bolte(a)googlemail.com>
* Copyright (C) 2009 Maximilian Wilhelm <max(a)rfc2324.org>
*
* This library is free software; you can redistribute it and/or
@@ -44,6 +44,7 @@
#include "esx_vi.h"
#include "esx_vi_methods.h"
#include "esx_util.h"
+#include "esx_stream.h"
#include "virstring.h"
#include "viruri.h"
@@ -970,6 +971,7 @@ esxConnectOpen(virConnectPtr conn, virConnectAuthPtr auth,
priv->maxVcpus = -1;
priv->supportsVMotion = esxVI_Boolean_Undefined;
priv->supportsLongMode = esxVI_Boolean_Undefined;
+ priv->supportsScreenshot = esxVI_Boolean_Undefined;
priv->usedCpuTimeCounterId = -1;
/*
@@ -1145,6 +1147,40 @@ esxSupportsVMotion(esxPrivate *priv)
+static esxVI_Boolean
+esxSupportsScreenshot(esxPrivate *priv)
+{
+ esxVI_String *propertyNameList = NULL;
+ esxVI_ObjectContent *hostSystem = NULL;
+
+ if (priv->supportsScreenshot != esxVI_Boolean_Undefined)
+ return priv->supportsScreenshot;
+
+ if (esxVI_EnsureSession(priv->primary) < 0)
+ return esxVI_Boolean_Undefined;
+
+ if (esxVI_String_AppendValueToList(&propertyNameList,
+ "capability.screenshotSupported") <
0 ||
+ esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
+ &hostSystem) < 0 ||
+ esxVI_GetBoolean(hostSystem, "capability.screenshotSupported",
+ &priv->supportsScreenshot,
+ esxVI_Occurrence_RequiredItem) < 0)
+ goto cleanup;
+
+ cleanup:
+ /*
+ * If we goto cleanup in case of an error then priv->supportsScreenshot is
+ * still esxVI_Boolean_Undefined, therefore we don't need to set it.
+ */
+ esxVI_String_Free(&propertyNameList);
+ esxVI_ObjectContent_Free(&hostSystem);
+
+ return priv->supportsScreenshot;
+}
+
+
+
static int
esxConnectSupportsFeature(virConnectPtr conn, int feature)
{
@@ -2501,6 +2537,87 @@ esxDomainGetState(virDomainPtr domain,
+static char *
+esxDomainScreenshot(virDomainPtr domain, virStreamPtr stream,
+ unsigned int screen, unsigned int flags)
+{
+ char *mimeType = NULL;
+ esxPrivate *priv = domain->conn->privateData;
+ esxVI_Boolean supportsScreenshot = esxVI_Boolean_Undefined;
+ esxVI_String *propertyNameList = NULL;
+ esxVI_ObjectContent *virtualMachine = NULL;
+ esxVI_VirtualMachinePowerState powerState;
+ virBuffer buffer = VIR_BUFFER_INITIALIZER;
+ char *url = NULL;
+
+ virCheckFlags(0, NULL);
+
+ if (screen != 0) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("Screen cannot be selected"));
+ return NULL;
+ }
+
+ supportsScreenshot = esxSupportsScreenshot(priv);
+
+ if (supportsScreenshot == esxVI_Boolean_Undefined)
+ return NULL;
+
+ if (supportsScreenshot != esxVI_Boolean_True) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("Screenshot feature is unsupported"));
+ return NULL;
+ }
+
+ if (esxVI_EnsureSession(priv->primary) < 0)
+ return NULL;
+
+ if (esxVI_String_AppendValueToList(&propertyNameList,
+ "runtime.powerState") < 0 ||
+ esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
+ propertyNameList, &virtualMachine,
+ esxVI_Occurrence_RequiredItem) < 0 ||
+ esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0)
+ goto cleanup;
+
+ if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("Domain is not powered on"));
+ goto cleanup;
+ }
+
+ /* Build URL */
+ virBufferAsprintf(&buffer, "%s://%s:%d/screen?id=",
priv->parsedUri->transport,
+ domain->conn->uri->server,
domain->conn->uri->port);
+ virBufferURIEncodeString(&buffer, virtualMachine->obj->value);
+
+ if (virBufferError(&buffer)) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ url = virBufferContentAndReset(&buffer);
+
+ if (VIR_STRDUP(mimeType, "image/png") < 0)
+ goto cleanup;
+
+ if (esxStreamOpenDownload(stream, priv, url, 0, 0) < 0) {
+ VIR_FREE(mimeType);
+ goto cleanup;
+ }
+
+ cleanup:
+ if (!url)
+ virBufferFreeAndReset(&buffer);
The 'if' is redundant. Ether we get here with partially allocated
buffer, or buffer is already reset.
+
+ esxVI_String_Free(&propertyNameList);
+ esxVI_ObjectContent_Free(&virtualMachine);
+
+ return mimeType;
+}
+
+
+
static int
esxDomainSetVcpusFlags(virDomainPtr domain, unsigned int nvcpus,
unsigned int flags)
@@ -5221,6 +5338,7 @@ static virDriver esxDriver = {
.domainGetMemoryParameters = esxDomainGetMemoryParameters, /* 0.8.6 */
.domainGetInfo = esxDomainGetInfo, /* 0.7.0 */
.domainGetState = esxDomainGetState, /* 0.9.2 */
+ .domainScreenshot = esxDomainScreenshot, /* 1.2.4 */
.domainSetVcpus = esxDomainSetVcpus, /* 0.7.0 */
.domainSetVcpusFlags = esxDomainSetVcpusFlags, /* 0.8.5 */
.domainGetVcpusFlags = esxDomainGetVcpusFlags, /* 0.8.5 */
diff --git a/src/esx/esx_private.h b/src/esx/esx_private.h
index 05ee1d6..f2766eb 100644
--- a/src/esx/esx_private.h
+++ b/src/esx/esx_private.h
@@ -38,6 +38,7 @@ typedef struct _esxPrivate {
int32_t maxVcpus;
esxVI_Boolean supportsVMotion;
esxVI_Boolean supportsLongMode; /* aka x86_64 */
+ esxVI_Boolean supportsScreenshot;
int32_t usedCpuTimeCounterId;
} esxPrivate;