// Copyright 2012 Citrix Systems, Inc. Licensed under the
// Apache License, Version 2.0 (the "License"); you may not use this
// file except in compliance with the License. Citrix Systems, Inc.
// reserves all rights not expressly granted by 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.
//
// Automatically generated by addcopyright.py at 04/03/2012
package com.cloud.hypervisor.vmware.manager;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
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.CreateVolumeFromSnapshotAnswer;
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
import com.cloud.agent.api.storage.CopyVolumeAnswer;
import com.cloud.agent.api.storage.CopyVolumeCommand;
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.StorageFilerTO;
import com.cloud.hypervisor.vmware.mo.CustomFieldConstants;
import com.cloud.hypervisor.vmware.mo.DatacenterMO;
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
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.ImageFormat;
import com.cloud.storage.StorageLayer;
import com.cloud.storage.template.VmdkProcessor;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.StringUtils;
import com.cloud.utils.Ternary;
import com.cloud.utils.script.Script;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.VirtualDeviceConfigSpec;
import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
import com.vmware.vim25.VirtualDisk;
import com.vmware.vim25.VirtualLsiLogicController;
import com.vmware.vim25.VirtualMachineConfigSpec;
import com.vmware.vim25.VirtualMachineFileInfo;
import com.vmware.vim25.VirtualMachineGuestOsIdentifier;
import com.vmware.vim25.VirtualSCSISharing;
public class VmwareStorageManagerImpl implements VmwareStorageManager {
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 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 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().get_value()).getBytes()).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);
} 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) {
if (e instanceof RemoteException) {
hostService.invalidateServiceContext(context);
}
String msg = "Unable to execute PrimaryStorageDownloadCommand due to exception";
s_logger.error(msg, e);
return new PrimaryStorageDownloadAnswer(msg);
}
}
@Override
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();
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);
// attach a volume to dummay wrapper VM for taking snapshot and exporting the VM for backup
if (!hyperHost.createBlankVm(workerVMName, 1, 512, 0, false, 4, 0, VirtualMachineGuestOsIdentifier._otherGuest.toString(), morDs, false)) {
String msg = "Unable to create worker VM to execute BackupSnapshotCommand";
s_logger.error(msg);
throw new Exception(msg);
}
vmMo = hyperHost.findVmOnHyperHost(workerVMName);
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 = String.format("[%s] %s.vmdk", dsMo.getName(), volumePath);
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));
success = (snapshotBackupUuid != null);
if (success) {
details = "Successfully backedUp the snapshotUuid: " + snapshotUuid + " to secondary storage.";
}
} finally {
if(vmMo != null)
vmMo.removeAllSnapshots();
try {
if (workerVm != null) {
// detach volume and destroy worker vm
workerVm.detachAllDisks();
workerVm.destroy();
}
} catch (Throwable e) {
s_logger.warn("Failed to destroy worker VM: " + workerVMName);
}
}
} catch (Throwable e) {
if (e instanceof RemoteException) {
hostService.invalidateServiceContext(context);
}
s_logger.error("Unexpecpted exception ", e);
details = "BackupSnapshotCommand exception: " + StringUtils.getExceptionStackInfo(e);
return new BackupSnapshotAnswer(cmd, false, details, 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));
return new CreatePrivateTemplateAnswer(cmd, true, null,
result.first(), result.third(), result.second(),
cmd.getUniqueName(), ImageFormat.OVA);
} catch (Throwable e) {
if (e instanceof RemoteException) {
hostService.invalidateServiceContext(context);
}
s_logger.error("Unexpecpted exception ", e);
details = "CreatePrivateTemplateFromVolumeCommand exception: " + StringUtils.getExceptionStackInfo(e);
return new CreatePrivateTemplateAnswer(cmd, false, details);
}
}
@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);
return new CreatePrivateTemplateAnswer(cmd, true, null,
result.first(), result.third(), result.second(),
uniqeName, ImageFormat.OVA);
} catch (Throwable e) {
if (e instanceof RemoteException) {
hostService.invalidateServiceContext(context);
}
s_logger.error("Unexpecpted exception ", e);
details = "CreatePrivateTemplateFromSnapshotCommand exception: " + StringUtils.getExceptionStackInfo(e);
return new CreatePrivateTemplateAnswer(cmd, false, details);
}
}
@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));
} 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("-", ""));
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);
}
return new CopyVolumeAnswer(cmd, true, null, result.first(), result.second());
} catch (Throwable e) {
if (e instanceof RemoteException) {
hostService.invalidateServiceContext(context);
}
String msg = "Unable to execute CopyVolumeCommand due to exception";
s_logger.error(msg, e);
return new CopyVolumeAnswer(cmd, false, "CopyVolumeCommand failed due to exception: " + StringUtils.getExceptionStackInfo(e), 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().replaceAll("-", "");
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);
if (details == null) {
success = true;
}
} catch (Throwable e) {
if (e instanceof RemoteException) {
hostService.invalidateServiceContext(context);
}
s_logger.error("Unexpecpted exception ", e);
details = "CreateVolumeFromSnapshotCommand exception: " + StringUtils.getExceptionStackInfo(e);
}
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) throws Exception {
s_logger.info("Executing copyTemplateFromSecondaryToPrimary. secondaryStorage: "
+ secondaryStorageUrl + ", templatePathAtSecondaryStorage: " + templatePathAtSecondaryStorage
+ ", templateName: " + templateName);
String secondaryMountPoint = _mountService.getMountPoint(secondaryStorageUrl);
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");
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) throws Exception {
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl);
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, false);
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
vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(),
VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first()));
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();
VmdkProcessor processor = new VmdkProcessor();
Map<String, Object> params = new HashMap<String, Object>();
params.put(StorageLayer.InstanceConfigKey, _storage);
processor.configure("VMDK 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.detachAllDisks();
clonedVm.destroy();
}
vmMo.removeSnapshot(templateUniqueName, false);
}
}
private Ternary<String, Long, Long> createTemplateFromSnapshot(long accountId, long templateId, String templateUniqueName,
String secStorageUrl, long volumeId, String backedUpSnapshotUuid) throws Exception {
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl);
String installPath = getTemplateRelativeDirInSecStorage(accountId, templateId);
String installFullPath = secondaryMountPoint + "/" + installPath;
String installFullName = installFullPath + "/" + templateUniqueName + ".ova";
String snapshotFullName = secondaryMountPoint + "/" + getSnapshotRelativeDirInSecStorage(accountId, volumeId)
+ "/" + backedUpSnapshotUuid + ".ova";
String result;
Script command;
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 {
command = new Script(false, "cp", _timeout, s_logger);
command.add(snapshotFullName);
command.add(installFullName);
result = command.execute();
if(result != null) {
String msg = "unable to copy snapshot " + snapshotFullName + " 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", installFullName);
command.setWorkDir(installFullPath);
s_logger.info("Executing command: " + command.toString());
result = command.execute();
if(result != null) {
String msg = "unable to untar snapshot " + snapshotFullName + " to "
+ installFullPath;
s_logger.error(msg);
throw new Exception(msg);
}
long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length();
VmdkProcessor processor = new VmdkProcessor();
Map<String, Object> params = new HashMap<String, Object>();
params.put(StorageLayer.InstanceConfigKey, _storage);
processor.configure("VMDK 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);
} 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")));
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.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 String createVolumeFromSnapshot(VmwareHypervisorHost hyperHost, DatastoreMO primaryDsMo, String newVolumeName,
long accountId, long volumeId, String secStorageUrl, String snapshotBackupUuid) throws Exception {
restoreVolumeFromSecStorage(hyperHost, primaryDsMo, newVolumeName,
secStorageUrl, getSnapshotRelativeDirInSecStorage(accountId, volumeId), snapshotBackupUuid);
return null;
}
private void restoreVolumeFromSecStorage(VmwareHypervisorHost hyperHost, DatastoreMO primaryDsMo, String newVolumeName,
String secStorageUrl, String secStorageDir, String backupName) throws Exception {
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl);
String srcOVAFileName = secondaryMountPoint + "/" + secStorageDir + "/"
+ backupName + "." + 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 + "/" + secStorageDir);
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);
}
VirtualMachineMO clonedVm = null;
try {
hyperHost.importVmFromOVF(srcFileName, newVolumeName, primaryDsMo, "thin");
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.detachAllDisks();
clonedVm.destroy();
}
}
}
private String backupSnapshotToSecondaryStorage(VirtualMachineMO vmMo, long accountId, long volumeId,
String volumePath, String snapshotUuid, String secStorageUrl,
String prevSnapshotUuid, String prevBackupUuid, String workerVmName) throws Exception {
String backupUuid = UUID.randomUUID().toString();
exportVolumeToSecondaryStroage(vmMo, volumePath, secStorageUrl,
getSnapshotRelativeDirInSecStorage(accountId, volumeId), backupUuid, workerVmName);
return backupUuid;
}
private void exportVolumeToSecondaryStroage(VirtualMachineMO vmMo, String volumePath,
String secStorageUrl, String secStorageDir, String exportName,
String workerVmName) throws Exception {
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl);
String exportPath = secondaryMountPoint + "/" + secStorageDir;
synchronized(exportPath.intern()) {
if(!new File(exportPath).exists()) {
Script command = new Script(false, "mkdir", _timeout, s_logger);
command.add("-p");
command.add(exportPath);
if(command.execute() != null)
throw new Exception("unable to prepare snapshot backup directory");
}
}
VirtualMachineMO clonedVm = null;
try {
Pair<VirtualDisk, String> volumeDeviceInfo = vmMo.getDiskDevice(volumePath, false);
if(volumeDeviceInfo == null) {
String msg = "Unable to find related disk device for volume. volume path: " + volumePath;
s_logger.error(msg);
throw new Exception(msg);
}
// 4 MB is the minimum requirement for VM memory in VMware
vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(),
VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first()));
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(exportPath, exportName, true, true);
} finally {
if(clonedVm != null) {
clonedVm.detachAllDisks();
clonedVm.destroy();
}
}
}
private String deleteSnapshotOnSecondaryStorge(long accountId, long volumeId, String secStorageUrl, String backupUuid) throws Exception {
String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl);
String snapshotMountRoot = secondaryMountPoint + "/" + getSnapshotRelativeDirInSecStorage(accountId, volumeId);
File file = new File(snapshotMountRoot + "/" + backupUuid + ".ova");
if(file.exists()) {
if(file.delete())
return null;
} else {
return "Backup file does not exist. backupUuid: " + backupUuid;
}
return "Failed to delete snapshot backup file, backupUuid: " + backupUuid;
}
private Pair<String, String> copyVolumeToSecStorage(VmwareHostService hostService, VmwareHypervisorHost hyperHost, CopyVolumeCommand cmd,
String vmName, long volumeId, String poolId, String volumePath,
String secStorageUrl, String workerVmName) throws Exception {
String volumeFolder = String.valueOf(volumeId) + "/";
VirtualMachineMO workerVm=null;
VirtualMachineMO vmMo=null;
String exportName = UUID.randomUUID().toString();
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);
}
vmMo = hyperHost.findVmOnHyperHost(vmName);
if (vmMo == null) {
// create a dummy worker vm for attaching the volume
DatastoreMO dsMo = new DatastoreMO(hyperHost.getContext(), morDs);
//restrict VM name to 32 chars, (else snapshot descriptor file name will be truncated to 32 chars of vm name)
VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec();
vmConfig.setName(workerVmName);
vmConfig.setMemoryMB((long) 4);
vmConfig.setNumCPUs(1);
vmConfig.setGuestId(VirtualMachineGuestOsIdentifier._otherGuest.toString());
VirtualMachineFileInfo fileInfo = new VirtualMachineFileInfo();
fileInfo.setVmPathName(String.format("[%s]", dsMo.getName()));
vmConfig.setFiles(fileInfo);
// Scsi controller
VirtualLsiLogicController scsiController = new VirtualLsiLogicController();
scsiController.setSharedBus(VirtualSCSISharing.noSharing);
scsiController.setBusNumber(0);
scsiController.setKey(1);
VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec();
scsiControllerSpec.setDevice(scsiController);
scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.add);
vmConfig.setDeviceChange(new VirtualDeviceConfigSpec[] { scsiControllerSpec });
hyperHost.createVm(vmConfig);
workerVm = hyperHost.findVmOnHyperHost(workerVmName);
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 = String.format("[%s] %s.vmdk", dsMo.getName(), volumePath);
workerVm.attachDisk(new String[] { datastoreVolumePath }, morDs);
vmMo = workerVm;
}
vmMo.createSnapshot(exportName, "Temporary snapshot for copy-volume command", false, false);
exportVolumeToSecondaryStroage(vmMo, volumePath, secStorageUrl, "volumes/" + volumeFolder, exportName,
hostService.getWorkerName(hyperHost.getContext(), cmd, 1));
return new Pair<String, String>(volumeFolder, exportName);
} finally {
vmMo.removeSnapshot(exportName, false);
if (workerVm != null) {
//detach volume and destroy worker vm
workerVm.detachAllDisks();
workerVm.destroy();
}
}
}
private Pair<String, String> copyVolumeFromSecStorage(VmwareHypervisorHost hyperHost, long volumeId,
DatastoreMO dsMo, String secStorageUrl, String exportName) throws Exception {
String volumeFolder = String.valueOf(volumeId) + "/";
String newVolume = UUID.randomUUID().toString().replaceAll("-", "");
restoreVolumeFromSecStorage(hyperHost, dsMo, newVolume, secStorageUrl, "volumes/" + volumeFolder, exportName);
return new Pair<String, String>(volumeFolder, newVolume);
}
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;
}
}