This patch implements virDomainScreenshot support for ESX versions newer
than 4.0. It adds CreateScreenshot_Task (apiVersion 4.0) and
DeleteDatastoreFile_Task (v2.5) then uses those to implement
esxDomainScreenshot function. The DeleteDatastoreFile_Task is used to
remove the screenshot file that is left on the server after invoking
CreateScreenshot_Task call.
---
src/esx/esx_driver.c | 173 +++++++++++++++++++++++++++++++++++++++++
src/esx/esx_vi_generator.input | 19 ++++-
2 files changed, 190 insertions(+), 2 deletions(-)
diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
index ff44881..e825aaf 100644
--- a/src/esx/esx_driver.c
+++ b/src/esx/esx_driver.c
@@ -22,6 +22,7 @@
*/
#include <config.h>
+#include <fcntl.h>
#include "internal.h"
#include "domain_conf.h"
@@ -44,6 +45,8 @@
#include "esx_vi.h"
#include "esx_vi_methods.h"
#include "esx_util.h"
+#include "configmake.h"
+#include "fdstream.h"
#include "virstring.h"
#include "viruri.h"
@@ -2501,6 +2504,175 @@ esxDomainGetState(virDomainPtr domain,
+static char *
+esxDomainScreenshot(virDomainPtr domain,
+ virStreamPtr st ATTRIBUTE_UNUSED,
+ unsigned int screen ATTRIBUTE_UNUSED,
+ unsigned int flags)
+{
+ esxPrivate *priv = domain->conn->privateData;
+ esxVI_String *propertyNameList = NULL;
+ esxVI_ObjectContent *virtualMachine = NULL;
+ esxVI_VirtualMachinePowerState powerState;
+ esxVI_ManagedObjectReference *task = NULL;
+ esxVI_TaskInfoState taskInfoState;
+ char *taskInfoErrorMessage = NULL;
+ esxVI_TaskInfo *taskInfo = NULL;
+ virBuffer buffer = VIR_BUFFER_INITIALIZER;
+ char *screenshotPath = NULL;
+ char *dataStoreName = NULL;
+ char *directoryName = NULL;
+ char *directoryAndFileName = NULL;
+ char *url = NULL;
+ char *screenData = NULL;
+ unsigned long long screenSize = 0;
+ char *tmp = NULL;
+ char *tmpdir = NULL;
+ int tmp_fd = -1;
+ char *ret = NULL;
+ bool unlink_tmp = false;
+
+ virCheckFlags(0, NULL);
+
+ if (esxVI_EnsureSession(priv->primary) < 0) {
+ return NULL;
+ }
+
+ if (priv->primary->apiVersion < esxVI_APIVersion_40) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+ _("Operation not supported in VI API version %s, "
+ "minimum version is 4.0"),
+ priv->primary->service->about->apiVersion);
+ }
+
+ if (esxVI_String_AppendValueToList(&propertyNameList,
+ "runtime.powerState") < 0 ||
+ esxVI_LookupVirtualMachineByUuidAndPrepareForTask
+ (priv->primary, domain->uuid, propertyNameList, &virtualMachine,
+ priv->parsedUri->autoAnswer) < 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;
+ }
+
+ if (esxVI_CreateScreenshot_Task(priv->primary, virtualMachine->obj,
+ &task) < 0 ||
+ esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
+ esxVI_Occurrence_RequiredItem,
+ priv->parsedUri->autoAnswer,
&taskInfoState,
+ &taskInfoErrorMessage) < 0) {
+ goto cleanup;
+ }
+
+ if (taskInfoState != esxVI_TaskInfoState_Success) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not create screenshot:
%s"),
+ taskInfoErrorMessage);
+ goto cleanup;
+ }
+
+ if (esxVI_LookupTaskInfoByTask(priv->primary, task, &taskInfo) < 0 ||
+ esxVI_String_CastValueFromAnyType(taskInfo->result, &screenshotPath)) {
+ goto cleanup;
+ }
+
+ if (esxUtil_ParseDatastorePath(screenshotPath, &dataStoreName,
+ &directoryName, &directoryAndFileName) < 0)
{
+ goto cleanup;
+ }
+
+ virBufferAsprintf(&buffer, "%s://%s:%d/folder/",
priv->parsedUri->transport,
+ domain->conn->uri->server,
domain->conn->uri->port);
+ virBufferURIEncodeString(&buffer, directoryAndFileName);
+ virBufferAddLit(&buffer, "?dcPath=");
+ virBufferURIEncodeString(&buffer, priv->primary->datacenterPath);
+ virBufferAddLit(&buffer, "&dsName=");
+ virBufferURIEncodeString(&buffer, dataStoreName);
+
+ if (virBufferError(&buffer)) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ url = virBufferContentAndReset(&buffer);
+
+ if (esxVI_CURL_Download(priv->primary->curl, url, &screenData, 0,
+ &screenSize) < 0) {
+ goto cleanup;
+ }
+
+ tmpdir = virGetEnvBlockSUID("TMPDIR");
+ if (!tmpdir) tmpdir = "/tmp";
+
+ if (virAsprintf(&tmp, "%s/esx.screendump.XXXXXX",
+ tmpdir) < 0) {
+ goto cleanup;
+ }
+
+ if ((tmp_fd = mkostemp(tmp, O_CLOEXEC)) == -1) {
+ virReportSystemError(errno, _("mkostemp(\"%s\") failed"),
tmp);
+ goto endjob;
+ }
+ unlink_tmp = true;
+
+ if (safewrite(tmp_fd, screenData, screenSize) < 0) {
+ virReportSystemError(errno, _("unable to write data to '%s'"),
tmp);
+ goto endjob;
+ }
+
+ if (VIR_CLOSE(tmp_fd) < 0) {
+ virReportSystemError(errno, _("unable to close %s"), tmp);
+ goto endjob;
+ }
+
+ if (virFDStreamOpenFile(st, tmp, 0, 0, O_RDONLY) < 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("unable to open stream"));
+ goto endjob;
+ }
+
+ if (VIR_STRDUP(ret, "image/png") < 0) {
+ goto endjob;
+ }
+
+ esxVI_ManagedObjectReference_Free(&task);
+ esxVI_DeleteDatastoreFile_Task(priv->primary, screenshotPath,
+ priv->primary->datacenter->_reference,
&task);
+
+ endjob:
+ VIR_FORCE_CLOSE(tmp_fd);
+
+ if (unlink_tmp)
+ unlink(tmp);
+
+ VIR_FREE(tmp);
+
+ cleanup:
+ if (!url) {
+ virBufferFreeAndReset(&buffer);
+ }
+
+ esxVI_String_Free(&propertyNameList);
+ esxVI_ObjectContent_Free(&virtualMachine);
+ esxVI_ManagedObjectReference_Free(&task);
+ esxVI_TaskInfo_Free(&taskInfo);
+ VIR_FREE(taskInfoErrorMessage);
+ VIR_FREE(screenshotPath);
+ VIR_FREE(screenData);
+ VIR_FREE(dataStoreName);
+ VIR_FREE(directoryName);
+ VIR_FREE(directoryAndFileName);
+ VIR_FREE(url);
+
+ return ret;
+}
+
+
+
static int
esxDomainSetVcpusFlags(virDomainPtr domain, unsigned int nvcpus,
unsigned int flags)
@@ -5221,6 +5393,7 @@ static virDriver esxDriver = {
.domainGetMemoryParameters = esxDomainGetMemoryParameters, /* 0.8.6 */
.domainGetInfo = esxDomainGetInfo, /* 0.7.0 */
.domainGetState = esxDomainGetState, /* 0.9.2 */
+ .domainScreenshot = esxDomainScreenshot, /* 1.2.3 */
.domainSetVcpus = esxDomainSetVcpus, /* 0.7.0 */
.domainSetVcpusFlags = esxDomainSetVcpusFlags, /* 0.8.5 */
.domainGetVcpusFlags = esxDomainGetVcpusFlags, /* 0.8.5 */
diff --git a/src/esx/esx_vi_generator.input b/src/esx/esx_vi_generator.input
index 22c114e..ebc718a 100644
--- a/src/esx/esx_vi_generator.input
+++ b/src/esx/esx_vi_generator.input
@@ -1,5 +1,5 @@
#
-# Definitions of vSphere API 2.5 enumeration and objects types used as input
+# Definitions of vSphere API enumeration and objects types used as input
# for the esx_vi_generator.py script.
#
# This format is line-based, so end-of-line is important.
@@ -33,11 +33,13 @@
#
# Method definition:
#
-# method <name> [returns <type> <occurrence>]
+# method <name> [returns <type> <occurrence>] [apiVersion
<version>]
# <type> <name> <occurrence>
# ...
# end
#
+# If apiVersion is not specified, it defaults to 2.5
+#
# The _this parameter can have a type attached to it:
#
# _this:<member>
@@ -1317,6 +1319,11 @@ method CreateFilter returns ManagedObjectReference
r
end
+method CreateScreenshot_Task returns ManagedObjectReference r
apiVersion 4.0
+ ManagedObjectReference _this r
+end
+
+
method CreateSnapshot_Task returns ManagedObjectReference r
ManagedObjectReference _this r
String name r
@@ -1376,6 +1383,14 @@ method Logout
end
+method DeleteDatastoreFile_Task returns ManagedObjectReference r
+ ManagedObjectReference _this:fileManager r
+ String name r
+ ManagedObjectReference datacenter o
+end
+
+
+
method MakeDirectory
ManagedObjectReference _this:fileManager r
String name r
--
1.9.0