blob: 7e6b8c19ad4d7fe0ae6d7e20bf1746679256b2ae [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.hypervisor.vmware.manager;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.BackupSnapshotAnswer;
import com.cloud.agent.api.BackupSnapshotCommand;
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
import com.cloud.agent.api.CreateVMSnapshotAnswer;
import com.cloud.agent.api.CreateVMSnapshotCommand;
import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
import com.cloud.agent.api.DeleteVMSnapshotAnswer;
import com.cloud.agent.api.DeleteVMSnapshotCommand;
import com.cloud.agent.api.RevertToVMSnapshotAnswer;
import com.cloud.agent.api.RevertToVMSnapshotCommand;
import com.cloud.agent.api.storage.CopyVolumeAnswer;
import com.cloud.agent.api.storage.CopyVolumeCommand;
import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand;
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
import com.cloud.agent.api.to.DataObjectType;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.agent.api.to.NfsTO;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.hypervisor.vmware.mo.CustomFieldConstants;
import com.cloud.hypervisor.vmware.mo.DatacenterMO;
import com.cloud.hypervisor.vmware.mo.DatastoreFile;
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
import com.cloud.hypervisor.vmware.mo.HostDatastoreBrowserMO;
import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
import com.cloud.hypervisor.vmware.util.VmwareContext;
import com.cloud.hypervisor.vmware.util.VmwareHelper;
import com.cloud.storage.JavaStorageLayer;
import com.cloud.storage.Storage;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.StorageLayer;
import com.cloud.storage.Volume;
import com.cloud.storage.resource.VmwareStorageProcessor;
import com.cloud.storage.template.OVAProcessor;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.snapshot.VMSnapshot;
import com.vmware.vim25.FileInfo;
import com.vmware.vim25.FileQueryFlags;
import com.vmware.vim25.HostDatastoreBrowserSearchResults;
import com.vmware.vim25.HostDatastoreBrowserSearchSpec;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.TaskInfo;
import com.vmware.vim25.TaskInfoState;
import com.vmware.vim25.VirtualDisk;
public class VmwareStorageManagerImpl implements VmwareStorageManager {
private String _nfsVersion;
@Override
public boolean execute(VmwareHostService hostService, CreateEntityDownloadURLCommand cmd) {
DataTO data = cmd.getData();
int timeout = NumbersUtil.parseInt(cmd.getContextParam(VmwareManager.s_vmwareOVAPackageTimeout.key()),
Integer.valueOf(VmwareManager.s_vmwareOVAPackageTimeout.defaultValue()) * VmwareManager.s_vmwareOVAPackageTimeout.multiplier());
if (data == null) {
return false;
}
String newPath = null;
if (data.getObjectType() == DataObjectType.VOLUME) {
newPath = createOvaForVolume((VolumeObjectTO)data, timeout);
} else if (data.getObjectType() == DataObjectType.TEMPLATE) {
newPath = createOvaForTemplate((TemplateObjectTO)data, timeout);
} else if (data.getObjectType() == DataObjectType.ARCHIVE) {
newPath = cmd.getInstallPath();
}
if (newPath != null) {
cmd.setInstallPath(newPath);
return true;
}
return false;
}
@Override
public void createOva(String path, String name, int archiveTimeout) {
Script commandSync = new Script(true, "sync", 0, s_logger);
commandSync.execute();
Script command = new Script(false, "tar", archiveTimeout, s_logger);
command.setWorkDir(path);
command.add("-cf", name + ".ova");
command.add(name + ".ovf"); // OVF file should be the first file in OVA archive
command.add(name + "-disk0.vmdk");
s_logger.info("Package OVA with command: " + command.toString());
command.execute();
}
private static final Logger s_logger = Logger.getLogger(VmwareStorageManagerImpl.class);
private final VmwareStorageMount _mountService;
private final StorageLayer _storage = new JavaStorageLayer();
private int _timeout;
public VmwareStorageManagerImpl(VmwareStorageMount mountService) {
assert (mountService != null);
_mountService = mountService;
}
public VmwareStorageManagerImpl(VmwareStorageMount mountService, String nfsVersion) {
assert (mountService != null);
_mountService = mountService;
_nfsVersion = nfsVersion;
}
public void configure(Map<String, Object> params) {
s_logger.info("Configure VmwareStorageManagerImpl");
String value = (String)params.get("scripts.timeout");
_timeout = NumbersUtil.parseInt(value, 1440) * 1000;
}
@Override
public String createOvaForTemplate(TemplateObjectTO template, int archiveTimeout) {
DataStoreTO storeTO = template.getDataStore();
if (!(storeTO instanceof NfsTO)) {
s_logger.debug("Can only handle NFS storage, while creating OVA from template");
return null;
}
NfsTO nfsStore = (NfsTO)storeTO;
String secStorageUrl = nfsStore.getUrl();
assert (secStorageUrl != null);
String installPath = template.getPath();
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl, nfsStore.getNfsVersion());
String installFullPath = secondaryMountPoint + "/" + installPath;
try {
if (installFullPath.endsWith(".ova")) {
if (new File(installFullPath).exists()) {
s_logger.debug("OVA file found at: " + installFullPath);
} else {
if (new File(installFullPath + ".meta").exists()) {
createOVAFromMetafile(installFullPath + ".meta", archiveTimeout);
} else {
String msg = "Unable to find OVA or OVA MetaFile to prepare template.";
s_logger.error(msg);
throw new Exception(msg);
}
}
return installPath;
}
} catch (Throwable e) {
s_logger.debug("Failed to create OVA: " + e.toString());
}
return null;
}
//Fang: new command added;
// Important! we need to sync file system before we can safely use tar to work around a linux kernel bug(or feature)
public String createOvaForVolume(VolumeObjectTO volume, int archiveTimeout) {
DataStoreTO storeTO = volume.getDataStore();
if (!(storeTO instanceof NfsTO)) {
s_logger.debug("can only handle nfs storage, when create ova from volume");
return null;
}
NfsTO nfsStore = (NfsTO)storeTO;
String secStorageUrl = nfsStore.getUrl();
assert (secStorageUrl != null);
//Note the volume path is volumes/accountId/volumeId/uuid/, the actual volume is uuid/uuid.vmdk
String installPath = volume.getPath();
int index = installPath.lastIndexOf(File.separator);
String volumeUuid = installPath.substring(index + 1);
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl, nfsStore.getNfsVersion());
//The real volume path
String volumePath = installPath + File.separator + volumeUuid + ".ova";
String installFullPath = secondaryMountPoint + "/" + installPath;
try {
if (new File(secondaryMountPoint + File.separator + volumePath).exists()) {
s_logger.debug("ova already exists:" + volumePath);
return volumePath;
} else {
Script commandSync = new Script(true, "sync", 0, s_logger);
commandSync.execute();
Script command = new Script(false, "tar", archiveTimeout, s_logger);
command.setWorkDir(installFullPath);
command.add("-cf", volumeUuid + ".ova");
command.add(volumeUuid + ".ovf"); // OVF file should be the first file in OVA archive
command.add(volumeUuid + "-disk0.vmdk");
String result = command.execute();
if (result != Script.ERR_TIMEOUT) {
return volumePath;
}
}
} catch (Throwable e) {
s_logger.info("Exception for createVolumeOVA");
}
return null;
}
@Override
public Answer execute(VmwareHostService hostService, PrimaryStorageDownloadCommand cmd) {
String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
assert (secondaryStorageUrl != null);
String templateUrl = cmd.getUrl();
String templateName = null;
String mountPoint = null;
if (templateUrl.endsWith(".ova")) {
int index = templateUrl.lastIndexOf("/");
mountPoint = templateUrl.substring(0, index);
mountPoint = mountPoint.substring(secondaryStorageUrl.length() + 1);
if (!mountPoint.endsWith("/")) {
mountPoint = mountPoint + "/";
}
templateName = templateUrl.substring(index + 1).replace("." + ImageFormat.OVA.getFileExtension(), "");
if (templateName == null || templateName.isEmpty()) {
templateName = cmd.getName();
}
} else {
mountPoint = templateUrl.substring(secondaryStorageUrl.length() + 1);
if (!mountPoint.endsWith("/")) {
mountPoint = mountPoint + "/";
}
templateName = cmd.getName();
}
VmwareContext context = hostService.getServiceContext(cmd);
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
String templateUuidName = UUID.nameUUIDFromBytes((templateName + "@" + cmd.getPoolUuid() + "-" + hyperHost.getMor().getValue()).getBytes("UTF-8")).toString();
// truncate template name to 32 chars to ensure they work well with vSphere API's.
templateUuidName = templateUuidName.replace("-", "");
DatacenterMO dcMo = new DatacenterMO(context, hyperHost.getHyperHostDatacenter());
VirtualMachineMO templateMo = VmwareHelper.pickOneVmOnRunningHost(dcMo.findVmByNameAndLabel(templateUuidName), true);
if (templateMo == null) {
if (s_logger.isInfoEnabled()) {
s_logger.info("Template " + templateName + " is not setup yet, setup template from secondary storage with uuid name: " + templateUuidName);
}
ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, cmd.getPoolUuid());
assert (morDs != null);
DatastoreMO primaryStorageDatastoreMo = new DatastoreMO(context, morDs);
copyTemplateFromSecondaryToPrimary(hyperHost, primaryStorageDatastoreMo, secondaryStorageUrl, mountPoint, templateName, templateUuidName, cmd.getNfsVersion());
} else {
s_logger.info("Template " + templateName + " has already been setup, skip the template setup process in primary storage");
}
return new PrimaryStorageDownloadAnswer(templateUuidName, 0);
} catch (Throwable e) {
return new PrimaryStorageDownloadAnswer(hostService.createLogMessageException(e, cmd));
}
}
@Override
@Deprecated
public Answer execute(VmwareHostService hostService, BackupSnapshotCommand cmd) {
Long accountId = cmd.getAccountId();
Long volumeId = cmd.getVolumeId();
String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
String snapshotUuid = cmd.getSnapshotUuid(); // not null: Precondition.
String prevSnapshotUuid = cmd.getPrevSnapshotUuid();
String prevBackupUuid = cmd.getPrevBackupUuid();
String searchExcludedFolders = cmd.getContextParam("searchexludefolders");
VirtualMachineMO workerVm = null;
String workerVMName = null;
String volumePath = cmd.getVolumePath();
ManagedObjectReference morDs = null;
DatastoreMO dsMo = null;
// By default assume failure
String details = null;
boolean success = false;
String snapshotBackupUuid = null;
VmwareContext context = hostService.getServiceContext(cmd);
VirtualMachineMO vmMo = null;
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, cmd.getPool().getUuid());
try {
vmMo = hyperHost.findVmOnHyperHost(cmd.getVmName());
if (vmMo == null) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Unable to find owner VM for BackupSnapshotCommand on host " + hyperHost.getHyperHostName() + ", will try within datacenter");
}
vmMo = hyperHost.findVmOnPeerHyperHost(cmd.getVmName());
if (vmMo == null) {
dsMo = new DatastoreMO(hyperHost.getContext(), morDs);
workerVMName = hostService.getWorkerName(context, cmd, 0, dsMo);
vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, workerVMName, null);
if (vmMo == null) {
throw new Exception("Failed to find the newly create or relocated VM. vmName: " + workerVMName);
}
workerVm = vmMo;
// attach volume to worker VM
String datastoreVolumePath = getVolumePathInDatastore(dsMo, volumePath + ".vmdk", searchExcludedFolders);
vmMo.attachDisk(new String[] {datastoreVolumePath}, morDs);
}
}
if (!vmMo.createSnapshot(snapshotUuid, "Snapshot taken for " + cmd.getSnapshotName(), false, false)) {
throw new Exception("Failed to take snapshot " + cmd.getSnapshotName() + " on vm: " + cmd.getVmName());
}
snapshotBackupUuid = backupSnapshotToSecondaryStorage(vmMo, accountId, volumeId, cmd.getVolumePath(), snapshotUuid, secondaryStorageUrl, prevSnapshotUuid,
prevBackupUuid, hostService.getWorkerName(context, cmd, 1, dsMo), cmd.getNfsVersion());
success = (snapshotBackupUuid != null);
if (success) {
details = "Successfully backedUp the snapshotUuid: " + snapshotUuid + " to secondary storage.";
}
} finally {
if (vmMo != null) {
ManagedObjectReference snapshotMor = vmMo.getSnapshotMor(snapshotUuid);
if (snapshotMor != null) {
vmMo.removeSnapshot(snapshotUuid, false);
}
}
try {
if (workerVm != null) {
workerVm.detachAllDisksAndDestroy();
}
} catch (Throwable e) {
s_logger.warn(String.format("Failed to destroy worker VM [%s] due to: [%s].", workerVMName, e.getMessage()), e);
}
}
} catch (Throwable e) {
return new BackupSnapshotAnswer(cmd, false, hostService.createLogMessageException(e, cmd), snapshotBackupUuid, true);
}
return new BackupSnapshotAnswer(cmd, success, details, snapshotBackupUuid, true);
}
@Override
public Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromVolumeCommand cmd) {
String secondaryStoragePoolURL = cmd.getSecondaryStorageUrl();
String volumePath = cmd.getVolumePath();
Long accountId = cmd.getAccountId();
Long templateId = cmd.getTemplateId();
String details = null;
VmwareContext context = hostService.getServiceContext(cmd);
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(cmd.getVmName());
if (vmMo == null) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Unable to find the owner VM for CreatePrivateTemplateFromVolumeCommand on host " + hyperHost.getHyperHostName() + ", try within datacenter");
}
vmMo = hyperHost.findVmOnPeerHyperHost(cmd.getVmName());
if (vmMo == null) {
String msg = "Unable to find the owner VM for volume operation. vm: " + cmd.getVmName();
s_logger.error(msg);
throw new Exception(msg);
}
}
Ternary<String, Long, Long> result = createTemplateFromVolume(vmMo, accountId, templateId, cmd.getUniqueName(), secondaryStoragePoolURL, volumePath,
hostService.getWorkerName(context, cmd, 0, null), cmd.getNfsVersion());
return new CreatePrivateTemplateAnswer(cmd, true, null, result.first(), result.third(), result.second(), cmd.getUniqueName(), ImageFormat.OVA);
} catch (Throwable e) {
return new CreatePrivateTemplateAnswer(cmd, false, hostService.createLogMessageException(e, cmd));
}
}
@Override
public Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromSnapshotCommand cmd) {
Long accountId = cmd.getAccountId();
Long volumeId = cmd.getVolumeId();
String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
Long newTemplateId = cmd.getNewTemplateId();
String details;
String uniqeName = UUID.randomUUID().toString();
VmwareContext context = hostService.getServiceContext(cmd);
try {
Ternary<String, Long, Long> result = createTemplateFromSnapshot(accountId, newTemplateId, uniqeName, secondaryStorageUrl, volumeId, backedUpSnapshotUuid,
cmd.getNfsVersion());
return new CreatePrivateTemplateAnswer(cmd, true, null, result.first(), result.third(), result.second(), uniqeName, ImageFormat.OVA);
} catch (Throwable e) {
return new CreatePrivateTemplateAnswer(cmd, false, hostService.createLogMessageException(e, cmd));
}
}
@Override
public Answer execute(VmwareHostService hostService, CopyVolumeCommand cmd) {
Long volumeId = cmd.getVolumeId();
String volumePath = cmd.getVolumePath();
String secondaryStorageURL = cmd.getSecondaryStorageURL();
String vmName = cmd.getVmName();
VmwareContext context = hostService.getServiceContext(cmd);
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
Pair<String, String> result;
if (cmd.toSecondaryStorage()) {
result = copyVolumeToSecStorage(hostService, hyperHost, cmd, vmName, volumeId, cmd.getPool().getUuid(), volumePath, secondaryStorageURL,
hostService.getWorkerName(context, cmd, 0, null), cmd.getNfsVersion());
} else {
StorageFilerTO poolTO = cmd.getPool();
ManagedObjectReference morDatastore = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, poolTO.getUuid());
if (morDatastore == null) {
morDatastore = hyperHost.mountDatastore(false, poolTO.getHost(), 0, poolTO.getPath(), poolTO.getUuid().replace("-", ""), true);
if (morDatastore == null) {
throw new Exception("Unable to mount storage pool on host. storeUrl: " + poolTO.getHost() + ":/" + poolTO.getPath());
}
}
result = copyVolumeFromSecStorage(hyperHost, volumeId, new DatastoreMO(context, morDatastore), secondaryStorageURL, volumePath, cmd.getNfsVersion());
deleteVolumeDirOnSecondaryStorage(volumeId, secondaryStorageURL, cmd.getNfsVersion());
}
return new CopyVolumeAnswer(cmd, true, null, result.first(), result.second());
} catch (Throwable e) {
return new CopyVolumeAnswer(cmd, false, hostService.createLogMessageException(e, cmd), null, null);
}
}
@Override
public Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd) {
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
Long accountId = cmd.getAccountId();
Long volumeId = cmd.getVolumeId();
String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
String details = null;
boolean success = false;
String newVolumeName = UUID.randomUUID().toString().replace("-", "");
VmwareContext context = hostService.getServiceContext(cmd);
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
ManagedObjectReference morPrimaryDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStorageNameLabel);
if (morPrimaryDs == null) {
String msg = "Unable to find datastore: " + primaryStorageNameLabel;
s_logger.error(msg);
throw new Exception(msg);
}
DatastoreMO primaryDsMo = new DatastoreMO(hyperHost.getContext(), morPrimaryDs);
details = createVolumeFromSnapshot(hyperHost, primaryDsMo, newVolumeName, accountId, volumeId, secondaryStorageUrl, backedUpSnapshotUuid, cmd.getNfsVersion());
if (details == null) {
success = true;
}
} catch (Throwable e) {
details = hostService.createLogMessageException(e, cmd);
}
return new CreateVolumeFromSnapshotAnswer(cmd, success, details, newVolumeName);
}
// templateName: name in secondary storage
// templateUuid: will be used at hypervisor layer
private void copyTemplateFromSecondaryToPrimary(VmwareHypervisorHost hyperHost, DatastoreMO datastoreMo, String secondaryStorageUrl, String templatePathAtSecondaryStorage,
String templateName, String templateUuid, String nfsVersion) throws Exception {
s_logger.info("Executing copyTemplateFromSecondaryToPrimary. secondaryStorage: " + secondaryStorageUrl + ", templatePathAtSecondaryStorage: "
+ templatePathAtSecondaryStorage + ", templateName: " + templateName);
String secondaryMountPoint = _mountService.getMountPoint(secondaryStorageUrl, nfsVersion);
s_logger.info("Secondary storage mount point: " + secondaryMountPoint);
String srcOVAFileName = secondaryMountPoint + "/" + templatePathAtSecondaryStorage + templateName + "." + ImageFormat.OVA.getFileExtension();
String srcFileName = getOVFFilePath(srcOVAFileName);
if (srcFileName == null) {
Script command = new Script("tar", 0, s_logger);
command.add("--no-same-owner");
command.add("-xf", srcOVAFileName);
command.setWorkDir(secondaryMountPoint + "/" + templatePathAtSecondaryStorage);
s_logger.info("Executing command: " + command.toString());
String result = command.execute();
if (result != null) {
String msg = "Unable to unpack snapshot OVA file at: " + srcOVAFileName;
s_logger.error(msg);
throw new Exception(msg);
}
}
srcFileName = getOVFFilePath(srcOVAFileName);
if (srcFileName == null) {
String msg = "Unable to locate OVF file in template package directory: " + srcOVAFileName;
s_logger.error(msg);
throw new Exception(msg);
}
String vmName = templateUuid;
hyperHost.importVmFromOVF(srcFileName, vmName, datastoreMo, "thin", null);
VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmName);
if (vmMo == null) {
String msg = "Failed to import OVA template. secondaryStorage: " + secondaryStorageUrl + ", templatePathAtSecondaryStorage: " + templatePathAtSecondaryStorage
+ ", templateName: " + templateName + ", templateUuid: " + templateUuid;
s_logger.error(msg);
throw new Exception(msg);
}
if (vmMo.createSnapshot("cloud.template.base", "Base snapshot", false, false)) {
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_UUID, templateUuid);
vmMo.markAsTemplate();
} else {
vmMo.destroy();
String msg = "Unable to create base snapshot for template, templateName: " + templateName + ", templateUuid: " + templateUuid;
s_logger.error(msg);
throw new Exception(msg);
}
}
private Ternary<String, Long, Long> createTemplateFromVolume(VirtualMachineMO vmMo, long accountId, long templateId, String templateUniqueName, String secStorageUrl,
String volumePath, String workerVmName, String nfsVersion) throws Exception {
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl, nfsVersion);
String installPath = getTemplateRelativeDirInSecStorage(accountId, templateId);
String installFullPath = secondaryMountPoint + "/" + installPath;
synchronized (installPath.intern()) {
Script command = new Script(false, "mkdir", _timeout, s_logger);
command.add("-p");
command.add(installFullPath);
String result = command.execute();
if (result != null) {
String msg = "unable to prepare template directory: " + installPath + ", storage: " + secStorageUrl + ", error msg: " + result;
s_logger.error(msg);
throw new Exception(msg);
}
}
VirtualMachineMO clonedVm = null;
try {
Pair<VirtualDisk, String> volumeDeviceInfo = vmMo.getDiskDevice(volumePath);
if (volumeDeviceInfo == null) {
String msg = "Unable to find related disk device for volume. volume path: " + volumePath;
s_logger.error(msg);
throw new Exception(msg);
}
if (!vmMo.createSnapshot(templateUniqueName, "Temporary snapshot for template creation", false, false)) {
String msg = "Unable to take snapshot for creating template from volume. volume path: " + volumePath;
s_logger.error(msg);
throw new Exception(msg);
}
// 4 MB is the minimum requirement for VM memory in VMware
String vmxFormattedVirtualHardwareVersion = VirtualMachineMO.getVmxFormattedVirtualHardwareVersion(vmMo.getVirtualHardwareVersion());
vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first()), vmxFormattedVirtualHardwareVersion);
clonedVm = vmMo.getRunningHost().findVmOnHyperHost(workerVmName);
if (clonedVm == null) {
String msg = "Unable to create dummy VM to export volume. volume path: " + volumePath;
s_logger.error(msg);
throw new Exception(msg);
}
clonedVm.exportVm(secondaryMountPoint + "/" + installPath, templateUniqueName, true, false);
long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length();
OVAProcessor processor = new OVAProcessor();
Map<String, Object> params = new HashMap<String, Object>();
params.put(StorageLayer.InstanceConfigKey, _storage);
processor.configure("OVA Processor", params);
long virtualSize = processor.getTemplateVirtualSize(installFullPath, templateUniqueName);
postCreatePrivateTemplate(installFullPath, templateId, templateUniqueName, physicalSize, virtualSize);
return new Ternary<String, Long, Long>(installPath + "/" + templateUniqueName + ".ova", physicalSize, virtualSize);
} finally {
if (clonedVm != null) {
clonedVm.detachAllDisksAndDestroy();
}
vmMo.removeSnapshot(templateUniqueName, false);
}
}
private Ternary<String, Long, Long> createTemplateFromSnapshot(long accountId, long templateId, String templateUniqueName, String secStorageUrl, long volumeId,
String backedUpSnapshotUuid, String nfsVersion) throws Exception {
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl, nfsVersion);
String installPath = getTemplateRelativeDirInSecStorage(accountId, templateId);
String installFullPath = secondaryMountPoint + "/" + installPath;
String installFullOVAName = installFullPath + "/" + templateUniqueName + ".ova"; //Note: volss for tmpl
String snapshotRoot = secondaryMountPoint + "/" + getSnapshotRelativeDirInSecStorage(accountId, volumeId);
String snapshotFullOVAName = snapshotRoot + "/" + backedUpSnapshotUuid + ".ova";
String snapshotFullOvfName = snapshotRoot + "/" + backedUpSnapshotUuid + ".ovf";
String result;
Script command;
String templateVMDKName = "";
//String snapshotFullVMDKName = snapshotRoot + "/";
// the backedUpSnapshotUuid field currently has the format: uuid/uuid. so we need to extract the uuid out
String backupSSUuid = backedUpSnapshotUuid.substring(0, backedUpSnapshotUuid.indexOf('/'));
String snapshotFullVMDKName = snapshotRoot + "/" + backupSSUuid + "/";
synchronized (installPath.intern()) {
command = new Script(false, "mkdir", _timeout, s_logger);
command.add("-p");
command.add(installFullPath);
result = command.execute();
if (result != null) {
String msg = "unable to prepare template directory: " + installPath + ", storage: " + secStorageUrl + ", error msg: " + result;
s_logger.error(msg);
throw new Exception(msg);
}
}
try {
if (new File(snapshotFullOVAName).exists()) {
command = new Script(false, "cp", _timeout, s_logger);
command.add(snapshotFullOVAName);
command.add(installFullOVAName);
result = command.execute();
if (result != null) {
String msg = "unable to copy snapshot " + snapshotFullOVAName + " to " + installFullPath;
s_logger.error(msg);
throw new Exception(msg);
}
// untar OVA file at template directory
command = new Script("tar", 0, s_logger);
command.add("--no-same-owner");
command.add("-xf", installFullOVAName);
command.setWorkDir(installFullPath);
s_logger.info("Executing command: " + command.toString());
result = command.execute();
if (result != null) {
String msg = "unable to untar snapshot " + snapshotFullOVAName + " to " + installFullPath;
s_logger.error(msg);
throw new Exception(msg);
}
} else { // there is no ova file, only ovf originally;
if (new File(snapshotFullOvfName).exists()) {
command = new Script(false, "cp", _timeout, s_logger);
command.add(snapshotFullOvfName);
//command.add(installFullOvfName);
command.add(installFullPath);
result = command.execute();
if (result != null) {
String msg = "unable to copy snapshot " + snapshotFullOvfName + " to " + installFullPath;
s_logger.error(msg);
throw new Exception(msg);
}
s_logger.info("vmdkfile parent dir: " + snapshotFullVMDKName);
File snapshotdir = new File(snapshotFullVMDKName);
// File snapshotdir = new File(snapshotRoot);
File[] ssfiles = snapshotdir.listFiles();
// List<String> filenames = new ArrayList<String>();
for (int i = 0; i < ssfiles.length; i++) {
String vmdkfile = ssfiles[i].getName();
s_logger.info("vmdk file name: " + vmdkfile);
if (vmdkfile.toLowerCase().startsWith(backupSSUuid) && vmdkfile.toLowerCase().endsWith(".vmdk")) {
snapshotFullVMDKName += vmdkfile;
templateVMDKName += vmdkfile;
break;
}
}
if (snapshotFullVMDKName != null) {
command = new Script(false, "cp", _timeout, s_logger);
command.add(snapshotFullVMDKName);
command.add(installFullPath);
result = command.execute();
s_logger.info("Copy VMDK file: " + snapshotFullVMDKName);
if (result != null) {
String msg = "unable to copy snapshot vmdk file " + snapshotFullVMDKName + " to " + installFullPath;
s_logger.error(msg);
throw new Exception(msg);
}
}
} else {
String msg = "unable to find any snapshot ova/ovf files" + snapshotFullOVAName + " to " + installFullPath;
s_logger.error(msg);
throw new Exception(msg);
}
}
long physicalSize = new File(installFullPath + "/" + templateVMDKName).length();
OVAProcessor processor = new OVAProcessor();
// long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length();
Map<String, Object> params = new HashMap<String, Object>();
params.put(StorageLayer.InstanceConfigKey, _storage);
processor.configure("OVA Processor", params);
long virtualSize = processor.getTemplateVirtualSize(installFullPath, templateUniqueName);
postCreatePrivateTemplate(installFullPath, templateId, templateUniqueName, physicalSize, virtualSize);
writeMetaOvaForTemplate(installFullPath, backedUpSnapshotUuid + ".ovf", templateVMDKName, templateUniqueName, physicalSize);
return new Ternary<String, Long, Long>(installPath + "/" + templateUniqueName + ".ova", physicalSize, virtualSize);
} catch (Exception e) {
// TODO, clean up left over files
throw e;
}
}
private void postCreatePrivateTemplate(String installFullPath, long templateId, String templateName, long size, long virtualSize) throws Exception {
// TODO a bit ugly here
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(installFullPath + "/template.properties"), "UTF-8"));
out.write("filename=" + templateName + ".ova");
out.newLine();
out.write("description=");
out.newLine();
out.write("checksum=");
out.newLine();
out.write("hvm=false");
out.newLine();
out.write("size=" + size);
out.newLine();
//out.write("ova=true");
out.write("ova=false"); //volss: the real ova file is not created
out.newLine();
out.write("id=" + templateId);
out.newLine();
out.write("public=false");
out.newLine();
out.write("ova.filename=" + templateName + ".ova");
out.newLine();
out.write("uniquename=" + templateName);
out.newLine();
out.write("ova.virtualsize=" + virtualSize);
out.newLine();
out.write("virtualsize=" + virtualSize);
out.newLine();
out.write("ova.size=" + size);
out.newLine();
} finally {
if (out != null) {
out.close();
}
}
}
private void writeMetaOvaForTemplate(String installFullPath, String ovfFilename, String vmdkFilename, String templateName, long diskSize) throws Exception {
// TODO a bit ugly here
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(installFullPath + "/" + templateName + ".ova.meta"), "UTF-8"));
out.write("ova.filename=" + templateName + ".ova");
out.newLine();
out.write("version=1.0");
out.newLine();
out.write("ovf=" + ovfFilename);
out.newLine();
out.write("numDisks=1");
out.newLine();
out.write("disk1.name=" + vmdkFilename);
out.newLine();
out.write("disk1.size=" + diskSize);
out.newLine();
} finally {
if (out != null) {
out.close();
}
}
}
private String createVolumeFromSnapshot(VmwareHypervisorHost hyperHost, DatastoreMO primaryDsMo, String newVolumeName, long accountId, long volumeId, String secStorageUrl,
String snapshotBackupUuid, String nfsVersion) throws Exception {
restoreVolumeFromSecStorage(hyperHost, primaryDsMo, newVolumeName, secStorageUrl, getSnapshotRelativeDirInSecStorage(accountId, volumeId), snapshotBackupUuid, nfsVersion);
return null;
}
private void restoreVolumeFromSecStorage(VmwareHypervisorHost hyperHost, DatastoreMO primaryDsMo, String newVolumeName, String secStorageUrl, String secStorageDir,
String backupName, String nfsVersion) throws Exception {
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl, nfsVersion);
String srcOVAFileName = secondaryMountPoint + "/" + secStorageDir + "/" + backupName + "." + ImageFormat.OVA.getFileExtension();
String snapshotDir = "";
if (backupName.contains("/")) {
snapshotDir = backupName.split("/")[0];
}
File ovafile = new File(srcOVAFileName);
String srcOVFFileName = secondaryMountPoint + "/" + secStorageDir + "/" + backupName + ".ovf";
File ovfFile = new File(srcOVFFileName);
// String srcFileName = getOVFFilePath(srcOVAFileName);
if (!ovfFile.exists()) {
srcOVFFileName = getOVFFilePath(srcOVAFileName);
if (srcOVFFileName == null && ovafile.exists()) { // volss: ova file exists; o/w can't do tar
Script command = new Script("tar", 0, s_logger);
command.add("--no-same-owner");
command.add("-xf", srcOVAFileName);
command.setWorkDir(secondaryMountPoint + "/" + secStorageDir + "/" + snapshotDir);
s_logger.info("Executing command: " + command.toString());
String result = command.execute();
if (result != null) {
String msg = "Unable to unpack snapshot OVA file at: " + srcOVAFileName;
s_logger.error(msg);
throw new Exception(msg);
}
} else {
String msg = "Unable to find snapshot OVA file at: " + srcOVAFileName;
s_logger.error(msg);
throw new Exception(msg);
}
srcOVFFileName = getOVFFilePath(srcOVAFileName);
}
if (srcOVFFileName == null) {
String msg = "Unable to locate OVF file in template package directory: " + srcOVAFileName;
s_logger.error(msg);
throw new Exception(msg);
}
VirtualMachineMO clonedVm = null;
try {
hyperHost.importVmFromOVF(srcOVFFileName, newVolumeName, primaryDsMo, "thin", null);
clonedVm = hyperHost.findVmOnHyperHost(newVolumeName);
if (clonedVm == null) {
throw new Exception("Unable to create container VM for volume creation");
}
clonedVm.moveAllVmDiskFiles(primaryDsMo, "", false);
clonedVm.detachAllDisks();
} finally {
if (clonedVm != null) {
clonedVm.detachAllDisksAndDestroy();
}
}
}
private String backupSnapshotToSecondaryStorage(VirtualMachineMO vmMo, long accountId, long volumeId, String volumePath, String snapshotUuid, String secStorageUrl,
String prevSnapshotUuid, String prevBackupUuid, String workerVmName, String nfsVersion) throws Exception {
String backupUuid = UUID.randomUUID().toString();
exportVolumeToSecondaryStorage(vmMo, volumePath, secStorageUrl, getSnapshotRelativeDirInSecStorage(accountId, volumeId), backupUuid, workerVmName, nfsVersion, true);
return backupUuid + "/" + backupUuid;
}
private void exportVolumeToSecondaryStorage(VirtualMachineMO vmMo, String volumePath, String secStorageUrl, String secStorageDir, String exportName, String workerVmName,
String nfsVersion, boolean clonedWorkerVMNeeded) throws Exception {
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl, nfsVersion);
String exportPath = secondaryMountPoint + "/" + secStorageDir + "/" + exportName;
synchronized (exportPath.intern()) {
if (!new File(exportPath).exists()) {
Script command = new Script(false, "mkdir", _timeout, s_logger);
command.add("-p");
command.add(exportPath);
String result = command.execute();
if (result != null) {
String errorMessage = String.format("Unable to prepare snapshot backup directory: [%s] due to [%s].", exportPath, result);
s_logger.error(errorMessage);
throw new Exception(errorMessage);
}
}
}
VirtualMachineMO clonedVm = null;
try {
Pair<VirtualDisk, String> volumeDeviceInfo = vmMo.getDiskDevice(volumePath);
if (volumeDeviceInfo == null) {
String msg = "Unable to find related disk device for volume. volume path: " + volumePath;
s_logger.error(msg);
throw new Exception(msg);
}
if (clonedWorkerVMNeeded) {
// 4 MB is the minimum requirement for VM memory in VMware
String vmxFormattedVirtualHardwareVersion = VirtualMachineMO.getVmxFormattedVirtualHardwareVersion(vmMo.getVirtualHardwareVersion());
vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first()), vmxFormattedVirtualHardwareVersion);
clonedVm = vmMo.getRunningHost().findVmOnHyperHost(workerVmName);
if (clonedVm == null) {
String msg = String.format("Unable to create dummy VM to export volume. volume path: [%s].", volumePath);
s_logger.error(msg);
throw new Exception(msg);
}
clonedVm.exportVm(exportPath, exportName, false, false); //Note: volss: not to create ova.
} else {
vmMo.exportVm(exportPath, exportName, false, false);
}
} finally {
if (clonedVm != null) {
clonedVm.detachAllDisksAndDestroy();
}
}
}
private Pair<String, String> copyVolumeToSecStorage(VmwareHostService hostService, VmwareHypervisorHost hyperHost, CopyVolumeCommand cmd, String vmName, long volumeId,
String poolId, String volumePath, String secStorageUrl, String workerVmName, String nfsVersion) throws Exception {
String volumeFolder = String.valueOf(volumeId) + "/";
VirtualMachineMO workerVm = null;
VirtualMachineMO vmMo = null;
String exportName = UUID.randomUUID().toString();
String searchExcludedFolders = cmd.getContextParam("searchexludefolders");
try {
ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, poolId);
if (morDs == null) {
String msg = "Unable to find volumes's storage pool for copy volume operation";
s_logger.error(msg);
throw new Exception(msg);
}
boolean clonedWorkerVMNeeded = true;
vmMo = hyperHost.findVmOnHyperHost(vmName);
if (vmMo == null) {
// create a dummy worker vm for attaching the volume
DatastoreMO dsMo = new DatastoreMO(hyperHost.getContext(), morDs);
workerVm = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, workerVmName, null);
if (workerVm == null) {
String msg = "Unable to create worker VM to execute CopyVolumeCommand";
s_logger.error(msg);
throw new Exception(msg);
}
//attach volume to worker VM
String datastoreVolumePath = getVolumePathInDatastore(dsMo, volumePath + ".vmdk", searchExcludedFolders);
workerVm.attachDisk(new String[] {datastoreVolumePath}, morDs);
vmMo = workerVm;
clonedWorkerVMNeeded = false;
} else {
vmMo.createSnapshot(exportName, "Temporary snapshot for copy-volume command", false, false);
}
exportVolumeToSecondaryStorage(vmMo, volumePath, secStorageUrl, "volumes/" + volumeFolder, exportName, hostService.getWorkerName(hyperHost.getContext(), cmd, 1, null),
nfsVersion, clonedWorkerVMNeeded);
return new Pair<String, String>(volumeFolder, exportName);
} finally {
if (vmMo != null && vmMo.getSnapshotMor(exportName) != null) {
vmMo.removeSnapshot(exportName, false);
}
if (workerVm != null) {
workerVm.detachAllDisksAndDestroy();
}
}
}
private String getVolumePathInDatastore(DatastoreMO dsMo, String volumeFileName, String searchExcludeFolders) throws Exception {
String datastoreVolumePath = dsMo.searchFileInSubFolders(volumeFileName, true, searchExcludeFolders);
if (datastoreVolumePath == null) {
throw new CloudRuntimeException("Unable to find file " + volumeFileName + " in datastore " + dsMo.getName());
}
return datastoreVolumePath;
}
private Pair<String, String> copyVolumeFromSecStorage(VmwareHypervisorHost hyperHost, long volumeId, DatastoreMO dsMo, String secStorageUrl, String exportName,
String nfsVersion) throws Exception {
String volumeFolder = String.valueOf(volumeId) + "/";
String newVolume = UUID.randomUUID().toString().replace("-", "");
restoreVolumeFromSecStorage(hyperHost, dsMo, newVolume, secStorageUrl, "volumes/" + volumeFolder, exportName, nfsVersion);
return new Pair<String, String>(volumeFolder, newVolume);
}
// here we use a method to return the ovf and vmdk file names; Another way to do it:
// create a new class, and like TemplateLocation.java and create templateOvfInfo.java to handle it;
private String createOVAFromMetafile(String metafileName, int archiveTimeout) throws Exception {
File ova_metafile = new File(metafileName);
Properties props = null;
String ovaFileName = "";
s_logger.info("Creating OVA using MetaFile: " + metafileName);
try (FileInputStream strm = new FileInputStream(ova_metafile);) {
s_logger.info("loading properties from ova meta file: " + metafileName);
props = new Properties();
props.load(strm);
ovaFileName = props.getProperty("ova.filename");
s_logger.info("ovafilename: " + ovaFileName);
String ovfFileName = props.getProperty("ovf");
s_logger.info("ovffilename: " + ovfFileName);
int diskNum = Integer.parseInt(props.getProperty("numDisks"));
if (diskNum <= 0) {
String msg = "VMDK disk file number is 0. Error";
s_logger.error(msg);
throw new Exception(msg);
}
String[] disks = new String[diskNum];
for (int i = 0; i < diskNum; i++) {
// String diskNameKey = "disk" + Integer.toString(i+1) + ".name"; // Fang use this
String diskNameKey = "disk1.name";
disks[i] = props.getProperty(diskNameKey);
s_logger.info("diskname " + disks[i]);
}
String exportDir = ova_metafile.getParent();
s_logger.info("exportDir: " + exportDir);
// Important! we need to sync file system before we can safely use tar to work around a linux kernel bug(or feature)
s_logger.info("Sync file system before we package OVA..., before tar ");
s_logger.info("ova: " + ovaFileName + ", ovf:" + ovfFileName + ", vmdk:" + disks[0] + ".");
Script commandSync = new Script(true, "sync", 0, s_logger);
commandSync.execute();
Script command = new Script(false, "tar", archiveTimeout, s_logger);
command.setWorkDir(exportDir); // Fang: pass this in to the method?
command.add("-cf", ovaFileName);
command.add(ovfFileName); // OVF file should be the first file in OVA archive
for (String diskName : disks) {
command.add(diskName);
}
command.execute();
s_logger.info("Package OVA for template in dir: " + exportDir + "cmd: " + command.toString());
// to be safe, physically test existence of the target OVA file
if ((new File(exportDir + File.separator + ovaFileName)).exists()) {
s_logger.info("OVA file: " + ovaFileName + " is created and ready to extract.");
return ovaFileName;
} else {
String msg = exportDir + File.separator + ovaFileName + " is not created as expected";
s_logger.error(msg);
throw new Exception(msg);
}
} catch (Exception e) {
s_logger.error("Exception while creating OVA using Metafile", e);
throw e;
}
}
private String getOVFFilePath(String srcOVAFileName) {
File file = new File(srcOVAFileName);
assert (_storage != null);
String[] files = _storage.listFiles(file.getParent());
if (files != null) {
for (String fileName : files) {
if (fileName.toLowerCase().endsWith(".ovf")) {
File ovfFile = new File(fileName);
return file.getParent() + File.separator + ovfFile.getName();
}
}
}
return null;
}
private static String getTemplateRelativeDirInSecStorage(long accountId, long templateId) {
return "template/tmpl/" + accountId + "/" + templateId;
}
private static String getSnapshotRelativeDirInSecStorage(long accountId, long volumeId) {
return "snapshots/" + accountId + "/" + volumeId;
}
protected boolean isManagedStorageDatastorePath(final String datastorePath) {
// ex. [-iqn.2010-01.com.solidfire:3p53.data-9999.97-0] i-2-9999-VM
return datastorePath != null && datastorePath.startsWith("[-iqn.");
}
protected String getManagedDatastoreName(final String datastorePath) {
// ex. [-iqn.2010-01.com.solidfire:3p53.data-9999.97-0]
return datastorePath == null ? datastorePath : datastorePath.split(" ")[0];
}
private long getVMSnapshotChainSize(VmwareContext context, VmwareHypervisorHost hyperHost, String fileName, ManagedObjectReference morDs,
String exceptFileName, String vmName) throws Exception {
long size = 0;
DatastoreMO dsMo = new DatastoreMO(context, morDs);
HostDatastoreBrowserMO browserMo = dsMo.getHostDatastoreBrowserMO();
String datastorePath = (new DatastoreFile(dsMo.getName(), vmName)).getPath();
if (isManagedStorageDatastorePath(datastorePath)) {
datastorePath = getManagedDatastoreName(datastorePath);
}
HostDatastoreBrowserSearchSpec searchSpec = new HostDatastoreBrowserSearchSpec();
FileQueryFlags fqf = new FileQueryFlags();
fqf.setFileSize(true);
fqf.setFileOwner(true);
fqf.setModification(true);
searchSpec.setDetails(fqf);
searchSpec.setSearchCaseInsensitive(false);
searchSpec.getMatchPattern().add(fileName);
ArrayList<HostDatastoreBrowserSearchResults> results = browserMo.searchDatastoreSubFolders(datastorePath, searchSpec);
for (HostDatastoreBrowserSearchResults result : results) {
if (result != null) {
List<FileInfo> info = result.getFile();
for (FileInfo fi : info) {
if (exceptFileName != null && fi.getPath().contains(exceptFileName)) {
continue;
} else {
size = size + fi.getFileSize();
}
}
}
}
return size;
}
private boolean isVolumeOnDatastoreCluster(VolumeObjectTO volumeObjectTO) {
DataStoreTO dsTO = volumeObjectTO.getDataStore();
if (!(dsTO instanceof PrimaryDataStoreTO)) {
return false;
}
PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO)dsTO;
return Storage.StoragePoolType.DatastoreCluster.equals(primaryDataStoreTO.getPoolType()) ||
Storage.StoragePoolType.DatastoreCluster.equals(primaryDataStoreTO.getParentPoolType());
}
private void syncVolume(VmwareHostService hostService, VirtualMachineMO virtualMachineMO, VmwareContext context,
VmwareHypervisorHost hypervisorHost, VolumeObjectTO volumeTO) throws Exception {
if (hostService.getStorageProcessor() == null) return;
VmwareStorageProcessor storageProcessor = hostService.getStorageProcessor();
DiskTO disk = new DiskTO();
Map<String, String> map = new HashMap<>();
map.put(DiskTO.PROTOCOL_TYPE, Storage.StoragePoolType.DatastoreCluster.toString());
disk.setDetails(map);
disk.setData(volumeTO);
storageProcessor.getSyncedVolume(virtualMachineMO, context, hypervisorHost, disk, volumeTO);
}
@Override
public CreateVMSnapshotAnswer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd) {
List<VolumeObjectTO> volumeTOs = cmd.getVolumeTOs();
String vmName = cmd.getVmName();
String vmSnapshotName = cmd.getTarget().getSnapshotName();
String vmSnapshotDesc = cmd.getTarget().getDescription();
boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory;
boolean quiescevm = cmd.getTarget().getQuiescevm();
VirtualMachineMO vmMo = null;
VmwareContext context = hostService.getServiceContext(cmd);
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
// wait if there are already VM snapshot task running
ManagedObjectReference taskmgr = context.getServiceContent().getTaskManager();
List<ManagedObjectReference> tasks = context.getVimClient().getDynamicProperty(taskmgr, "recentTask");
for (ManagedObjectReference taskMor : tasks) {
TaskInfo info = (TaskInfo)(context.getVimClient().getDynamicProperty(taskMor, "info"));
if (info.getEntityName().equals(cmd.getVmName()) && org.apache.commons.lang3.StringUtils.isNotBlank(info.getName()) && info.getName().equalsIgnoreCase("CreateSnapshot_Task")) {
if (!(info.getState().equals(TaskInfoState.SUCCESS) || info.getState().equals(TaskInfoState.ERROR))) {
s_logger.debug("There is already a VM snapshot task running, wait for it");
context.getVimClient().waitForTask(taskMor);
}
}
}
vmMo = hyperHost.findVmOnHyperHost(vmName);
if (vmMo == null) {
vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
}
if (vmMo == null) {
String msg = "Unable to find VM for CreateVMSnapshotCommand";
s_logger.info(msg);
return new CreateVMSnapshotAnswer(cmd, false, msg);
} else {
if (vmMo.getSnapshotMor(vmSnapshotName) != null) {
s_logger.info("VM snapshot " + vmSnapshotName + " already exists");
} else if (!vmMo.createSnapshot(vmSnapshotName, vmSnapshotDesc, snapshotMemory, quiescevm)) {
return new CreateVMSnapshotAnswer(cmd, false, "Unable to create snapshot due to esxi internal failed");
}
setVolumeToPathAndSize(volumeTOs, vmMo, hostService, context, hyperHost);
return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), volumeTOs);
}
} catch (Exception e) {
String msg = e.getMessage();
s_logger.error("failed to create snapshot for vm:" + vmName + " due to " + msg, e);
try {
if (vmMo.getSnapshotMor(vmSnapshotName) != null) {
vmMo.removeSnapshot(vmSnapshotName, false);
}
} catch (Exception e1) {
s_logger.info("[ignored]" + "error during snapshot remove: " + e1.getLocalizedMessage());
}
return new CreateVMSnapshotAnswer(cmd, false, e.getMessage());
}
}
private Map<String, String> getNewDiskMap(VirtualMachineMO vmMo) throws Exception {
Map<String, String> mapNewDisk = new HashMap<String, String>();
// find VM disk file path after creating snapshot
VirtualDisk[] vdisks = vmMo.getAllDiskDevice();
for (int i = 0; i < vdisks.length; i++) {
List<Pair<String, ManagedObjectReference>> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false);
for (Pair<String, ManagedObjectReference> fileItem : vmdkFiles) {
String fullPath = fileItem.first();
String baseName = null;
String vmdkName = null;
// if this is managed storage
if (isManagedStorageDatastorePath(fullPath)) {
baseName = getManagedDatastoreName(fullPath);
baseName = baseName.substring(1, baseName.length() - 1); // remove '[' and ']'
vmdkName = fullPath; // for managed storage, vmdkName == fullPath
} else {
vmdkName = fullPath.split("] ")[1];
if (vmdkName.endsWith(".vmdk")) {
vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length());
}
String token = "/";
if (vmdkName.contains(token)) {
vmdkName = vmdkName.substring(vmdkName.indexOf(token) + token.length());
}
baseName = VmwareHelper.trimSnapshotDeltaPostfix(vmdkName);
}
mapNewDisk.put(baseName, vmdkName);
}
}
return mapNewDisk;
}
protected void setVolumeToPathAndSize(List<VolumeObjectTO> volumeTOs, VirtualMachineMO vmMo, VmwareHostService hostService, VmwareContext context, VmwareHypervisorHost hyperHost)
throws Exception {
String vmName = vmMo.getVmName();
for (VolumeObjectTO volumeTO : volumeTOs) {
String path = volumeTO.getPath();
String baseName;
String datastoreUuid = volumeTO.getDataStore().getUuid();
if (isVolumeOnDatastoreCluster(volumeTO)) {
syncVolume(hostService, vmMo, context, hyperHost, volumeTO);
path = volumeTO.getPath();
baseName = VmwareHelper.trimSnapshotDeltaPostfix(volumeTO.getPath());
if (StringUtils.isNotEmpty(volumeTO.getDataStoreUuid())) {
datastoreUuid = volumeTO.getDataStoreUuid();
}
} else {
Map<String, String> mapNewDisk = getNewDiskMap(vmMo);
if (isManagedStorageDatastorePath(path)) {
path = getManagedDatastoreName(path);
baseName = path.substring(1, path.length() - 1); // remove '[' and ']'
} else {
baseName = VmwareHelper.trimSnapshotDeltaPostfix(path);
}
path = mapNewDisk.get(baseName);
volumeTO.setPath(path);
}
// get volume's chain size for this VM snapshot; exclude current volume vdisk
ManagedObjectReference morDs = getDatastoreAsManagedObjectReference(baseName, hyperHost, datastoreUuid);
long size = getVMSnapshotChainSize(context, hyperHost, baseName + "-*.vmdk", morDs, path, vmName);
if (volumeTO.getVolumeType() == Volume.Type.ROOT) {
// add memory snapshot size
size += getVMSnapshotChainSize(context, hyperHost, vmName + "-*.vmsn", morDs, null, vmName);
}
volumeTO.setSize(size);
}
}
private ManagedObjectReference getDatastoreAsManagedObjectReference(String baseName, VmwareHypervisorHost hyperHost, String storeUuid) throws Exception {
try {
// if baseName equates to a datastore name, this should be managed storage
ManagedObjectReference morDs = hyperHost.findDatastoreByName(baseName);
if (morDs != null) {
return morDs;
}
} catch (Exception ex) {
s_logger.info("[ignored]" + "error getting managed object refference: " + ex.getLocalizedMessage());
}
// not managed storage, so use the standard way of getting a ManagedObjectReference for a datastore
return HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, storeUuid);
}
@Override
public DeleteVMSnapshotAnswer execute(VmwareHostService hostService, DeleteVMSnapshotCommand cmd) {
List<VolumeObjectTO> listVolumeTo = cmd.getVolumeTOs();
VirtualMachineMO vmMo = null;
VmwareContext context = hostService.getServiceContext(cmd);
String vmName = cmd.getVmName();
String vmSnapshotName = cmd.getTarget().getSnapshotName();
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
vmMo = hyperHost.findVmOnHyperHost(vmName);
if (vmMo == null) {
vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
}
if (vmMo == null) {
String msg = "Unable to find VM for RevertToVMSnapshotCommand";
s_logger.debug(msg);
return new DeleteVMSnapshotAnswer(cmd, false, msg);
} else {
if (vmMo.getSnapshotMor(vmSnapshotName) == null) {
s_logger.debug("can not find the snapshot " + vmSnapshotName + ", assume it is already removed");
} else {
if (!vmMo.removeSnapshot(vmSnapshotName, false)) {
String msg = "delete vm snapshot " + vmSnapshotName + " due to error occurred in vmware";
s_logger.error(msg);
return new DeleteVMSnapshotAnswer(cmd, false, msg);
}
}
s_logger.debug("snapshot: " + vmSnapshotName + " is removed");
// after removed snapshot, the volumes' paths have been changed for the VM, needs to report new paths to manager
setVolumeToPathAndSize(listVolumeTo, vmMo, hostService, context, hyperHost);
return new DeleteVMSnapshotAnswer(cmd, listVolumeTo);
}
} catch (Exception e) {
String msg = e.getMessage();
s_logger.error("failed to delete vm snapshot " + vmSnapshotName + " of vm " + vmName + " due to " + msg, e);
return new DeleteVMSnapshotAnswer(cmd, false, msg);
}
}
@Override
public RevertToVMSnapshotAnswer execute(VmwareHostService hostService, RevertToVMSnapshotCommand cmd) {
String snapshotName = cmd.getTarget().getSnapshotName();
String vmName = cmd.getVmName();
Boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory;
List<VolumeObjectTO> listVolumeTo = cmd.getVolumeTOs();
VirtualMachine.PowerState vmState = VirtualMachine.PowerState.PowerOn;
VirtualMachineMO vmMo = null;
VmwareContext context = hostService.getServiceContext(cmd);
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
// wait if there are already VM revert task running
ManagedObjectReference taskmgr = context.getServiceContent().getTaskManager();
List<ManagedObjectReference> tasks = context.getVimClient().getDynamicProperty(taskmgr, "recentTask");
for (ManagedObjectReference taskMor : tasks) {
TaskInfo info = (TaskInfo)(context.getVimClient().getDynamicProperty(taskMor, "info"));
if (info.getEntityName().equals(cmd.getVmName()) && org.apache.commons.lang3.StringUtils.isNotBlank(info.getName()) && info.getName().equalsIgnoreCase("RevertToSnapshot_Task")) {
s_logger.debug("There is already a VM snapshot task running, wait for it");
context.getVimClient().waitForTask(taskMor);
}
}
HostMO hostMo = (HostMO)hyperHost;
vmMo = hyperHost.findVmOnHyperHost(vmName);
if (vmMo == null) {
vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
}
if (vmMo == null) {
String msg = "Unable to find VM for RevertToVMSnapshotCommand";
s_logger.debug(msg);
return new RevertToVMSnapshotAnswer(cmd, false, msg);
} else {
if (cmd.isReloadVm()) {
vmMo.reload();
}
boolean result = false;
if (snapshotName != null) {
ManagedObjectReference morSnapshot = vmMo.getSnapshotMor(snapshotName);
result = hostMo.revertToSnapshot(morSnapshot);
} else {
return new RevertToVMSnapshotAnswer(cmd, false, "Unable to find the snapshot by name " + snapshotName);
}
if (result) {
setVolumeToPathAndSize(listVolumeTo, vmMo, hostService, context, hyperHost);
if (!snapshotMemory) {
vmState = VirtualMachine.PowerState.PowerOff;
}
return new RevertToVMSnapshotAnswer(cmd, listVolumeTo, vmState);
} else {
return new RevertToVMSnapshotAnswer(cmd, false, "Error while reverting to snapshot due to execute in ESXi");
}
}
} catch (Exception e) {
String msg = "revert vm " + vmName + " to snapshot " + snapshotName + " failed due to " + e.getMessage();
s_logger.error(msg, e);
return new RevertToVMSnapshotAnswer(cmd, false, msg);
}
}
private String deleteVolumeDirOnSecondaryStorage(long volumeId, String secStorageUrl, String nfsVersion) throws Exception {
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl, nfsVersion);
String volumeMountRoot = secondaryMountPoint + "/" + getVolumeRelativeDirInSecStorage(volumeId);
return deleteDir(volumeMountRoot);
}
private String deleteDir(String dir) {
synchronized (dir.intern()) {
Script command = new Script(false, "rm", _timeout, s_logger);
command.add("-rf");
command.add(dir);
return command.execute();
}
}
private static String getVolumeRelativeDirInSecStorage(long volumeId) {
return "volumes/" + volumeId;
}
}