http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8197f1f0/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java new file mode 100644 index 0000000..b525e13 --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java @@ -0,0 +1,880 @@ +// 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; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8197f1f0/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageMount.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageMount.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageMount.java new file mode 100644 index 0000000..9f4de50 --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageMount.java @@ -0,0 +1,17 @@ +// 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; + +public interface VmwareStorageMount { + String getMountPoint(String storageUrl); +} http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8197f1f0/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java new file mode 100755 index 0000000..f82afb8 --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java @@ -0,0 +1,60 @@ +// 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.resource; + +import org.apache.log4j.Logger; + +import com.cloud.hypervisor.vmware.manager.VmwareManager; +import com.cloud.hypervisor.vmware.util.VmwareContext; +import com.cloud.utils.StringUtils; +import com.cloud.utils.component.ComponentLocator; +import com.vmware.apputils.version.ExtendedAppUtil; + +public class VmwareContextFactory { + + private static final Logger s_logger = Logger.getLogger(VmwareContextFactory.class); + + private static volatile int s_seq = 1; + private static VmwareManager s_vmwareMgr; + + static { + // skip certificate check + System.setProperty("axis.socketSecureFactory", "org.apache.axis.components.net.SunFakeTrustSocketFactory"); + + ComponentLocator locator = ComponentLocator.getLocator("management-server"); + s_vmwareMgr = locator.getManager(VmwareManager.class); + } + + public static VmwareContext create(String vCenterAddress, String vCenterUserName, String vCenterPassword) throws Exception { + assert(vCenterAddress != null); + assert(vCenterUserName != null); + assert(vCenterPassword != null); + + String serviceUrl = "https://" + vCenterAddress + "/sdk/vimService"; + String[] params = new String[] {"--url", serviceUrl, "--username", vCenterUserName, "--password", vCenterPassword }; + + if(s_logger.isDebugEnabled()) + s_logger.debug("initialize VmwareContext. url: " + serviceUrl + ", username: " + vCenterUserName + ", password: " + StringUtils.getMaskedPasswordForDisplay(vCenterPassword)); + + ExtendedAppUtil appUtil = ExtendedAppUtil.initialize(vCenterAddress + "-" + s_seq++, params); + + appUtil.connect(); + VmwareContext context = new VmwareContext(appUtil, vCenterAddress); + context.registerStockObject(VmwareManager.CONTEXT_STOCK_NAME, s_vmwareMgr); + + context.registerStockObject("serviceconsole", s_vmwareMgr.getServiceConsolePortGroupName()); + context.registerStockObject("manageportgroup", s_vmwareMgr.getManagementPortGroupName()); + + return context; + } +}
