blob: 410fc7c2a7ceb45a9483ce7b36a4e5a6dd873146 [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.guru;
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.inject.Inject;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.command.DownloadCommand;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.agent.api.BackupSnapshotCommand;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
import com.cloud.agent.api.MigrateVmToPoolCommand;
import com.cloud.agent.api.UnregisterNicCommand;
import com.cloud.agent.api.UnregisterVMCommand;
import com.cloud.agent.api.storage.CopyVolumeCommand;
import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand;
import com.cloud.agent.api.storage.CreateVolumeOVACommand;
import com.cloud.agent.api.storage.PrepareOVAPackingCommand;
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.StorageFilerTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.agent.api.to.VolumeTO;
import com.cloud.cluster.ClusterManager;
import com.cloud.configuration.Resource;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDetailsDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.hypervisor.HypervisorGuru;
import com.cloud.hypervisor.HypervisorGuruBase;
import com.cloud.hypervisor.vmware.VmwareDatacenterVO;
import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO;
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao;
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao;
import com.cloud.hypervisor.vmware.manager.VmwareManager;
import com.cloud.hypervisor.vmware.mo.DatacenterMO;
import com.cloud.hypervisor.vmware.mo.NetworkMO;
import com.cloud.hypervisor.vmware.mo.VirtualDiskManagerMO;
import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.resource.VmwareContextFactory;
import com.cloud.hypervisor.vmware.util.VmwareContext;
import com.cloud.network.Network;
import com.cloud.network.Networks;
import com.cloud.network.Networks.BroadcastDomainType;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.PhysicalNetworkDao;
import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao;
import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO;
import com.cloud.network.dao.PhysicalNetworkVO;
import com.cloud.secstorage.CommandExecLogDao;
import com.cloud.secstorage.CommandExecLogVO;
import com.cloud.serializer.GsonHelper;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.Storage;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.VMTemplateStorageResourceAssoc;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VMTemplatePoolDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.secondary.SecondaryStorageVmManager;
import com.cloud.user.ResourceLimitService;
import com.cloud.utils.Pair;
import com.cloud.utils.UuidUtils;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.NicProfile;
import com.cloud.vm.NicVO;
import com.cloud.vm.SecondaryStorageVmVO;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.Type;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
import com.google.gson.Gson;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.VirtualDevice;
import com.vmware.vim25.VirtualDeviceBackingInfo;
import com.vmware.vim25.VirtualDeviceConnectInfo;
import com.vmware.vim25.VirtualDisk;
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
import com.vmware.vim25.VirtualEthernetCard;
import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo;
import com.vmware.vim25.VirtualMachineConfigSummary;
import com.vmware.vim25.VirtualMachineRuntimeInfo;
public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Configurable {
private static final Logger s_logger = Logger.getLogger(VMwareGuru.class);
private static final Gson GSON = GsonHelper.getGson();
@Inject VmwareVmImplementer vmwareVmImplementer;
@Inject GuestOSDao _guestOsDao;
@Inject HostDetailsDao _hostDetailsDao;
@Inject ClusterDetailsDao _clusterDetailsDao;
@Inject CommandExecLogDao _cmdExecLogDao;
@Inject VmwareManager _vmwareMgr;
@Inject VMInstanceDao vmDao;
@Inject SecondaryStorageVmManager _secStorageMgr;
@Inject PhysicalNetworkTrafficTypeDao _physicalNetworkTrafficTypeDao;
@Inject VirtualMachineManager vmManager;
@Inject ClusterManager _clusterMgr;
@Inject VolumeDao _volumeDao;
@Inject ResourceLimitService _resourceLimitService;
@Inject PrimaryDataStoreDao _storagePoolDao;
@Inject VolumeDataFactory _volFactory;
@Inject VmwareDatacenterDao vmwareDatacenterDao;
@Inject VmwareDatacenterZoneMapDao vmwareDatacenterZoneMapDao;
@Inject VMTemplatePoolDao templateStoragePoolDao;
@Inject VMTemplateDao vmTemplateDao;
@Inject UserVmDao userVmDao;
@Inject DiskOfferingDao diskOfferingDao;
@Inject PhysicalNetworkDao physicalNetworkDao;
@Inject StoragePoolHostDao storagePoolHostDao;
protected VMwareGuru() {
super();
}
public static final ConfigKey<Boolean> VmwareReserveCpu = new ConfigKey<Boolean>(Boolean.class, "vmware.reserve.cpu", "Advanced", "false",
"Specify whether or not to reserve CPU when deploying an instance.", true, ConfigKey.Scope.Cluster, null);
public static final ConfigKey<Boolean> VmwareReserveMemory = new ConfigKey<Boolean>(Boolean.class, "vmware.reserve.mem", "Advanced", "false",
"Specify whether or not to reserve memory when deploying an instance.", true, ConfigKey.Scope.Cluster, null);
public static final ConfigKey<Boolean> VmwareEnableNestedVirtualization = new ConfigKey<Boolean>(Boolean.class, "vmware.nested.virtualization", "Advanced", "false",
"When set to true this will enable nested virtualization when this is supported by the hypervisor", true, ConfigKey.Scope.Global, null);
public static final ConfigKey<Boolean> VmwareEnableNestedVirtualizationPerVM = new ConfigKey<Boolean>(Boolean.class, "vmware.nested.virtualization.perVM", "Advanced", "false",
"When set to true this will enable nested virtualization per vm", true, ConfigKey.Scope.Global, null);
@Override public HypervisorType getHypervisorType() {
return HypervisorType.VMware;
}
@Override public VirtualMachineTO implement(VirtualMachineProfile vm) {
vmwareVmImplementer.setGlobalNestedVirtualisationEnabled(VmwareEnableNestedVirtualization.value());
vmwareVmImplementer.setGlobalNestedVPerVMEnabled(VmwareEnableNestedVirtualizationPerVM.value());
return vmwareVmImplementer.implement(vm, toVirtualMachineTO(vm), vmManager.findClusterAndHostIdForVm(vm.getId()).first());
}
@Override @DB public Pair<Boolean, Long> getCommandHostDelegation(long hostId, Command cmd) {
if (s_logger.isTraceEnabled()) {
s_logger.trace(String.format("Finding delegation for command of type %s to host %d.", cmd.getClass(), hostId));
}
boolean needDelegation = false;
if (cmd instanceof StorageSubSystemCommand) {
StorageSubSystemCommand c = (StorageSubSystemCommand)cmd;
c.setExecuteInSequence(StorageManager.shouldExecuteInSequenceOnVmware());
}
if (cmd instanceof DownloadCommand) {
cmd.setContextParam(VmwareManager.s_vmwareOVAPackageTimeout.key(), String.valueOf(VmwareManager.s_vmwareOVAPackageTimeout.value()));
}
//NOTE: the hostid can be a hypervisor host, or a ssvm agent. For copycommand, if it's for volume upload, the hypervisor
//type is empty, so we need to check the format of volume at first.
if (cmd instanceof CopyCommand) {
CopyCommand cpyCommand = (CopyCommand)cmd;
DataTO srcData = cpyCommand.getSrcTO();
DataStoreTO srcStoreTO = srcData.getDataStore();
DataTO destData = cpyCommand.getDestTO();
DataStoreTO destStoreTO = destData.getDataStore();
boolean inSeq = true;
if (parallelExecutionAllowed(srcData, destData, srcStoreTO, destStoreTO)) {
inSeq = false;
}
cpyCommand.setExecuteInSequence(inSeq);
if (srcData.getObjectType() == DataObjectType.VOLUME) {
VolumeObjectTO volumeObjectTO = (VolumeObjectTO)srcData;
if (Storage.ImageFormat.OVA == volumeObjectTO.getFormat()) {
needDelegation = true;
}
}
if (!needDelegation && !(HypervisorType.VMware == srcData.getHypervisorType() || HypervisorType.VMware == destData.getHypervisorType())) {
return new Pair<Boolean, Long>(Boolean.FALSE, new Long(hostId));
}
if (destData.getObjectType() == DataObjectType.VOLUME && destStoreTO.getRole() == DataStoreRole.Primary && srcData.getObjectType() == DataObjectType.TEMPLATE
&& srcStoreTO.getRole() == DataStoreRole.Primary) {
needDelegation = false;
} else {
needDelegation = true;
}
} else if (cmd instanceof CreateEntityDownloadURLCommand) {
DataTO srcData = ((CreateEntityDownloadURLCommand)cmd).getData();
if ((HypervisorType.VMware == srcData.getHypervisorType())) {
needDelegation = true;
}
if (srcData.getObjectType() == DataObjectType.VOLUME) {
VolumeObjectTO volumeObjectTO = (VolumeObjectTO)srcData;
if (Storage.ImageFormat.OVA == volumeObjectTO.getFormat()) {
needDelegation = true;
}
}
}
if (s_logger.isTraceEnabled()) {
s_logger.trace(String.format("Command of type %s is going to be executed in sequence? %b", cmd.getClass(), cmd.executeInSequence()));
s_logger.trace(String.format("Command of type %s is going to need delegation? %b", cmd.getClass(), needDelegation));
}
if (!needDelegation) {
return new Pair<Boolean, Long>(Boolean.FALSE, new Long(hostId));
}
HostVO host = hostDao.findById(hostId);
long dcId = host.getDataCenterId();
Pair<HostVO, SecondaryStorageVmVO> cmdTarget = _secStorageMgr.assignSecStorageVm(dcId, cmd);
if (cmdTarget != null) {
// TODO, we need to make sure agent is actually connected too
cmd.setContextParam("hypervisor", HypervisorType.VMware.toString());
if (host.getType() == Host.Type.Routing) {
Map<String, String> hostDetails = _hostDetailsDao.findDetails(hostId);
cmd.setContextParam("guid", resolveNameInGuid(hostDetails.get("guid")));
cmd.setContextParam("username", hostDetails.get("username"));
cmd.setContextParam("password", hostDetails.get("password"));
cmd.setContextParam("serviceconsole", _vmwareMgr.getServiceConsolePortGroupName());
cmd.setContextParam("manageportgroup", _vmwareMgr.getManagementPortGroupName());
}
CommandExecLogVO execLog = new CommandExecLogVO(cmdTarget.first().getId(), cmdTarget.second().getId(), cmd.getClass().getSimpleName(), 1);
_cmdExecLogDao.persist(execLog);
cmd.setContextParam("execid", String.valueOf(execLog.getId()));
cmd.setContextParam("noderuninfo", String.format("%d-%d", _clusterMgr.getManagementNodeId(), _clusterMgr.getCurrentRunId()));
cmd.setContextParam("vCenterSessionTimeout", String.valueOf(_vmwareMgr.getVcenterSessionTimeout()));
cmd.setContextParam(VmwareManager.s_vmwareOVAPackageTimeout.key(), String.valueOf(VmwareManager.s_vmwareOVAPackageTimeout.value()));
if (cmd instanceof BackupSnapshotCommand || cmd instanceof CreatePrivateTemplateFromVolumeCommand || cmd instanceof CreatePrivateTemplateFromSnapshotCommand || cmd instanceof CopyVolumeCommand || cmd instanceof CopyCommand || cmd instanceof CreateVolumeOVACommand || cmd instanceof PrepareOVAPackingCommand
|| cmd instanceof CreateVolumeFromSnapshotCommand) {
String workerName = _vmwareMgr.composeWorkerName();
long checkPointId = 1;
// FIXME: Fix long checkPointId = _checkPointMgr.pushCheckPoint(new VmwareCleanupMaid(hostDetails.get("guid"), workerName));
cmd.setContextParam("worker", workerName);
cmd.setContextParam("checkpoint", String.valueOf(checkPointId));
// some commands use 2 workers
String workerName2 = _vmwareMgr.composeWorkerName();
long checkPointId2 = 1;
// FIXME: Fix long checkPointId2 = _checkPointMgr.pushCheckPoint(new VmwareCleanupMaid(hostDetails.get("guid"), workerName2));
cmd.setContextParam("worker2", workerName2);
cmd.setContextParam("checkpoint2", String.valueOf(checkPointId2));
cmd.setContextParam("searchexludefolders", _vmwareMgr.s_vmwareSearchExcludeFolder.value());
}
return new Pair<Boolean, Long>(Boolean.TRUE, cmdTarget.first().getId());
}
return new Pair<Boolean, Long>(Boolean.FALSE, new Long(hostId));
}
private boolean parallelExecutionAllowed(DataTO srcData, DataTO destData, DataStoreTO srcStoreTO, DataStoreTO destStoreTO) {
Long srcId = srcStoreTO == null || srcStoreTO.getUuid() == null ? null : _storagePoolDao.findByUuid(srcStoreTO.getUuid()).getId();
Long dstId = destStoreTO == null || destStoreTO.getUuid() == null ? null : _storagePoolDao.findByUuid(destStoreTO.getUuid()).getId();
return (srcData.getObjectType() == DataObjectType.SNAPSHOT)
|| (destData.getObjectType() == DataObjectType.SNAPSHOT)
|| (destStoreTO != null && destStoreTO.getRole() == DataStoreRole.Image)
|| (destStoreTO != null && destStoreTO.getRole() == DataStoreRole.ImageCache)
|| !StorageManager.shouldExecuteInSequenceOnVmware(srcId, dstId);
}
@Override
public boolean trackVmHostChange() {
return true;
}
private static String resolveNameInGuid(String guid) {
String tokens[] = guid.split("@");
assert (tokens.length == 2);
String vCenterIp = NetUtils.resolveToIp(tokens[1]);
if (vCenterIp == null) {
s_logger.error("Fatal : unable to resolve vCenter address " + tokens[1] + ", please check your DNS configuration");
return guid;
}
if (vCenterIp.equals(tokens[1]))
return guid;
return tokens[0] + "@" + vCenterIp;
}
@Override public List<Command> finalizeExpungeNics(VirtualMachine vm, List<NicProfile> nics) {
List<Command> commands = new ArrayList<Command>();
List<NicVO> nicVOs = nicDao.listByVmId(vm.getId());
for (NicVO nic : nicVOs) {
NetworkVO network = networkDao.findById(nic.getNetworkId());
if (network.getBroadcastDomainType() == BroadcastDomainType.Lswitch) {
s_logger.debug("Nic " + nic.toString() + " is connected to an lswitch, cleanup required");
NetworkVO networkVO = networkDao.findById(nic.getNetworkId());
// We need the traffic label to figure out which vSwitch has the
// portgroup
PhysicalNetworkTrafficTypeVO trafficTypeVO = _physicalNetworkTrafficTypeDao.findBy(networkVO.getPhysicalNetworkId(), networkVO.getTrafficType());
UnregisterNicCommand unregisterNicCommand = new UnregisterNicCommand(vm.getInstanceName(), trafficTypeVO.getVmwareNetworkLabel(), UUID.fromString(nic.getUuid()));
commands.add(unregisterNicCommand);
}
}
return commands;
}
@Override public String getConfigComponentName() {
return VMwareGuru.class.getSimpleName();
}
@Override public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {VmwareReserveCpu, VmwareReserveMemory, VmwareEnableNestedVirtualization, VmwareEnableNestedVirtualizationPerVM};
}
@Override public List<Command> finalizeExpungeVolumes(VirtualMachine vm) {
List<Command> commands = new ArrayList<Command>();
List<VolumeVO> volumes = _volumeDao.findByInstance(vm.getId());
if (volumes != null) {
for (VolumeVO volume : volumes) {
StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId());
// storagePool should be null if we are expunging a volume that was never
// attached to a VM that was started (the "trick" for storagePool to be null
// is that none of the VMs this volume may have been attached to were ever started,
// so the volume was never assigned to a storage pool)
if (storagePool != null && storagePool.isManaged() && volume.getVolumeType() == Volume.Type.ROOT) {
VolumeInfo volumeInfo = _volFactory.getVolume(volume.getId());
PrimaryDataStore primaryDataStore = (PrimaryDataStore)volumeInfo.getDataStore();
Map<String, String> details = primaryDataStore.getDetails();
if (details == null) {
details = new HashMap<String, String>();
primaryDataStore.setDetails(details);
}
details.put(DiskTO.MANAGED, Boolean.TRUE.toString());
DeleteCommand cmd = new DeleteCommand(volumeInfo.getTO());
commands.add(cmd);
break;
}
}
}
return commands;
}
@Override public Map<String, String> getClusterSettings(long vmId) {
Map<String, String> details = new HashMap<String, String>();
Long clusterId = vmManager.findClusterAndHostIdForVm(vmId).first();
if (clusterId != null) {
details.put(VmwareReserveCpu.key(), VmwareReserveCpu.valueIn(clusterId).toString());
details.put(VmwareReserveMemory.key(), VmwareReserveMemory.valueIn(clusterId).toString());
}
return details;
}
/**
* Get vmware datacenter mapped to the zoneId
*/
private VmwareDatacenterVO getVmwareDatacenter(long zoneId) {
VmwareDatacenterZoneMapVO zoneMap = vmwareDatacenterZoneMapDao.findByZoneId(zoneId);
long vmwareDcId = zoneMap.getVmwareDcId();
return vmwareDatacenterDao.findById(vmwareDcId);
}
/**
* Get Vmware datacenter MO
*/
private DatacenterMO getDatacenterMO(long zoneId) throws Exception {
VmwareDatacenterVO vmwareDatacenter = getVmwareDatacenter(zoneId);
VmwareContext context = VmwareContextFactory.getContext(vmwareDatacenter.getVcenterHost(), vmwareDatacenter.getUser(), vmwareDatacenter.getPassword());
DatacenterMO dcMo = new DatacenterMO(context, vmwareDatacenter.getVmwareDatacenterName());
ManagedObjectReference dcMor = dcMo.getMor();
if (dcMor == null) {
String msg = "Error while getting Vmware datacenter " + vmwareDatacenter.getVmwareDatacenterName();
s_logger.error(msg);
throw new InvalidParameterValueException(msg);
}
return dcMo;
}
/**
* Get guest OS ID for VM being imported.
* If it cannot be found it is mapped to: "Other (64-bit)" ID
*/
private Long getImportingVMGuestOs(VirtualMachineConfigSummary configSummary) {
String guestFullName = configSummary.getGuestFullName();
GuestOSVO os = _guestOsDao.findOneByDisplayName(guestFullName);
return os != null ? os.getId() : _guestOsDao.findOneByDisplayName("Other (64-bit)").getId();
}
/**
* Create and persist service offering
*/
private ServiceOfferingVO createServiceOfferingForVMImporting(Integer cpus, Integer memory, Integer maxCpuUsage) {
String name = "Imported-" + cpus + "-" + memory;
DiskOfferingVO diskOfferingVO = new DiskOfferingVO(name, name, Storage.ProvisioningType.THIN, false, null, false, false, true);
diskOfferingVO = diskOfferingDao.persistDefaultDiskOffering(diskOfferingVO);
ServiceOfferingVO vo = new ServiceOfferingVO(name, cpus, memory, maxCpuUsage, null, null, false, name, false, Type.User,
false);
vo.setDiskOfferingId(diskOfferingVO.getId());
return serviceOfferingDao.persist(vo);
}
/**
* Get service offering ID for VM being imported.
* If it cannot be found it creates one and returns its ID
*/
private Long getImportingVMServiceOffering(VirtualMachineConfigSummary configSummary, VirtualMachineRuntimeInfo runtimeInfo) {
Integer numCpu = configSummary.getNumCpu();
Integer memorySizeMB = configSummary.getMemorySizeMB();
Integer maxCpuUsage = runtimeInfo.getMaxCpuUsage();
List<ServiceOfferingVO> offerings = serviceOfferingDao.listPublicByCpuAndMemory(numCpu, memorySizeMB);
return CollectionUtils.isEmpty(offerings) ? createServiceOfferingForVMImporting(numCpu, memorySizeMB, maxCpuUsage).getId() : offerings.get(0).getId();
}
/**
* Check if disk is ROOT disk
*/
private boolean isRootDisk(VirtualDisk disk, Map<VirtualDisk, VolumeVO> disksMapping, Backup backup) {
if (!disksMapping.containsKey(disk)) {
return false;
}
VolumeVO volumeVO = disksMapping.get(disk);
if (volumeVO == null) {
final VMInstanceVO vm = virtualMachineDao.findByIdIncludingRemoved(backup.getVmId());
if (vm == null) {
throw new CloudRuntimeException("Failed to find the volumes details from the VM backup");
}
List<Backup.VolumeInfo> backedUpVolumes = vm.getBackupVolumeList();
for (Backup.VolumeInfo backedUpVolume : backedUpVolumes) {
if (backedUpVolume.getSize().equals(disk.getCapacityInBytes())) {
return backedUpVolume.getType().equals(Volume.Type.ROOT);
}
}
} else {
return volumeVO.getVolumeType().equals(Volume.Type.ROOT);
}
throw new CloudRuntimeException("Could not determinate ROOT disk for VM to import");
}
/**
* Check backing info
*/
private void checkBackingInfo(VirtualDeviceBackingInfo backingInfo) {
if (!(backingInfo instanceof VirtualDiskFlatVer2BackingInfo)) {
String errorMessage = String.format("Unsupported backing info. Expected: [%s], but received: [%s].", VirtualDiskFlatVer2BackingInfo.class.getSimpleName(), backingInfo.getClass().getSimpleName());
s_logger.error(errorMessage);
throw new CloudRuntimeException(errorMessage);
}
}
/**
* Get pool ID from datastore UUID
*/
private Long getPoolIdFromDatastoreUuid(String datastoreUuid) {
String poolUuid = UuidUtils.normalize(datastoreUuid);
StoragePoolVO pool = _storagePoolDao.findByUuid(poolUuid);
if (pool == null) {
throw new CloudRuntimeException("Couldn't find storage pool " + poolUuid);
}
return pool.getId();
}
/**
* Get pool ID for disk
*/
private Long getPoolId(VirtualDisk disk) {
VirtualDeviceBackingInfo backing = disk.getBacking();
checkBackingInfo(backing);
VirtualDiskFlatVer2BackingInfo info = (VirtualDiskFlatVer2BackingInfo)backing;
String[] fileNameParts = info.getFileName().split(" ");
String datastoreUuid = StringUtils.substringBetween(fileNameParts[0], "[", "]");
return getPoolIdFromDatastoreUuid(datastoreUuid);
}
/**
* Get volume name from filename
*/
private String getVolumeNameFromFileName(String fileName) {
String[] fileNameParts = fileName.split(" ");
String volumePath = fileNameParts[1];
return volumePath.split("/")[1].replaceFirst(".vmdk", "");
}
/**
* Get root disk template path
*/
private String getRootDiskTemplatePath(VirtualDisk rootDisk) {
VirtualDeviceBackingInfo backing = rootDisk.getBacking();
checkBackingInfo(backing);
VirtualDiskFlatVer2BackingInfo info = (VirtualDiskFlatVer2BackingInfo)backing;
VirtualDiskFlatVer2BackingInfo parent = info.getParent();
return (parent != null) ? getVolumeNameFromFileName(parent.getFileName()) : getVolumeNameFromFileName(info.getFileName());
}
/**
* Get template MO
*/
private VirtualMachineMO getTemplate(DatacenterMO dcMo, String templatePath) throws Exception {
VirtualMachineMO template = dcMo.findVm(templatePath);
if (!template.isTemplate()) {
throw new CloudRuntimeException(templatePath + " is not a template");
}
return template;
}
/**
* Get template pool ID
*/
private Long getTemplatePoolId(VirtualMachineMO template) throws Exception {
VirtualMachineConfigSummary configSummary = template.getConfigSummary();
String vmPathName = configSummary.getVmPathName();
String[] pathParts = vmPathName.split(" ");
String dataStoreUuid = pathParts[0].replace("[", "").replace("]", "");
return getPoolIdFromDatastoreUuid(dataStoreUuid);
}
/**
* Get template size
*/
private Long getTemplateSize(VirtualMachineMO template, String vmInternalName, Map<VirtualDisk, VolumeVO> disksMapping, Backup backup) throws Exception {
List<VirtualDisk> disks = template.getVirtualDisks();
if (CollectionUtils.isEmpty(disks)) {
throw new CloudRuntimeException("Couldn't find VM template size");
}
return disks.get(0).getCapacityInBytes();
}
/**
* Create a VM Template record on DB
*/
private VMTemplateVO createVMTemplateRecord(String vmInternalName, long guestOsId, long accountId) {
Long nextTemplateId = vmTemplateDao.getNextInSequence(Long.class, "id");
VMTemplateVO templateVO = new VMTemplateVO(nextTemplateId, "Imported-from-" + vmInternalName, Storage.ImageFormat.OVA, false, false, false, Storage.TemplateType.USER, null,
false, 64, accountId, null, "Template imported from VM " + vmInternalName, false, guestOsId, false, HypervisorType.VMware, null, null, false, false, false, false);
return vmTemplateDao.persist(templateVO);
}
/**
* Retrieve the template ID matching the template on templatePath. There are 2 cases:
* - There are no references on DB for primary storage -> create a template DB record and return its ID
* - There are references on DB for primary storage -> return template ID for any of those references
*/
private long getTemplateId(String templatePath, String vmInternalName, Long guestOsId, long accountId) {
List<VMTemplateStoragePoolVO> poolRefs = templateStoragePoolDao.listByTemplatePath(templatePath);
return CollectionUtils.isNotEmpty(poolRefs) ? poolRefs.get(0).getTemplateId() : createVMTemplateRecord(vmInternalName, guestOsId, accountId).getId();
}
/**
* Update template reference on primary storage, if needed
*/
private void updateTemplateRef(long templateId, Long poolId, String templatePath, Long templateSize) {
VMTemplateStoragePoolVO templateRef = templateStoragePoolDao.findByPoolPath(poolId, templatePath);
if (templateRef == null) {
templateRef = new VMTemplateStoragePoolVO(poolId, templateId, null, 100, VMTemplateStorageResourceAssoc.Status.DOWNLOADED, templatePath, null, null, templatePath,
templateSize, null);
templateRef.setState(ObjectInDataStoreStateMachine.State.Ready);
templateStoragePoolDao.persist(templateRef);
}
}
/**
* Get template ID for VM being imported. If it is not found, it is created
*/
private Long getImportingVMTemplate(List<VirtualDisk> virtualDisks, DatacenterMO dcMo, String vmInternalName, Long guestOsId, long accountId, Map<VirtualDisk, VolumeVO> disksMapping, Backup backup) throws Exception {
for (VirtualDisk disk : virtualDisks) {
if (isRootDisk(disk, disksMapping, backup)) {
VolumeVO volumeVO = disksMapping.get(disk);
if (volumeVO == null) {
String templatePath = getRootDiskTemplatePath(disk);
VirtualMachineMO template = getTemplate(dcMo, templatePath);
Long poolId = getTemplatePoolId(template);
Long templateSize = getTemplateSize(template, vmInternalName, disksMapping, backup);
long templateId = getTemplateId(templatePath, vmInternalName, guestOsId, accountId);
updateTemplateRef(templateId, poolId, templatePath, templateSize);
return templateId;
} else {
return volumeVO.getTemplateId();
}
}
}
throw new CloudRuntimeException("Could not find ROOT disk for VM " + vmInternalName);
}
/**
* If VM does not exist: create and persist VM
* If VM exists: update VM
*/
private VMInstanceVO getVM(String vmInternalName, long templateId, long guestOsId, long serviceOfferingId, long zoneId, long accountId, long userId, long domainId) {
s_logger.debug(String.format("Trying to get VM with specs: [vmInternalName: %s, templateId: %s, guestOsId: %s, serviceOfferingId: %s].", vmInternalName,
templateId, guestOsId, serviceOfferingId));
VMInstanceVO vm = virtualMachineDao.findVMByInstanceNameIncludingRemoved(vmInternalName);
if (vm != null) {
s_logger.debug(String.format("Found an existing VM [id: %s, removed: %s] with internalName: [%s].", vm.getUuid(), vm.getRemoved() != null ? "yes" : "no", vmInternalName));
vm.setState(VirtualMachine.State.Stopped);
vm.setPowerState(VirtualMachine.PowerState.PowerOff);
virtualMachineDao.update(vm.getId(), vm);
if (vm.getRemoved() != null) {
virtualMachineDao.unremove(vm.getId());
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, vm.getDataCenterId(), vm.getId(), vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(),
vm.getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplayVm());
}
return virtualMachineDao.findById(vm.getId());
} else {
long id = userVmDao.getNextInSequence(Long.class, "id");
s_logger.debug(String.format("Can't find an existing VM with internalName: [%s]. Creating a new VM with: [id: %s, name: %s, templateId: %s, guestOsId: %s, serviceOfferingId: %s].",
vmInternalName, id, vmInternalName, templateId, guestOsId, serviceOfferingId));
UserVmVO vmInstanceVO = new UserVmVO(id, vmInternalName, vmInternalName, templateId, HypervisorType.VMware, guestOsId, false, false, domainId, accountId, userId,
serviceOfferingId, null, null, null, vmInternalName);
vmInstanceVO.setDataCenterId(zoneId);
return userVmDao.persist(vmInstanceVO);
}
}
/**
* Create and persist volume
*/
private VolumeVO createVolumeRecord(Volume.Type type, String volumeName, long zoneId, long domainId, long accountId, long diskOfferingId, Storage.ProvisioningType provisioningType,
Long size, long instanceId, Long poolId, long templateId, Integer unitNumber, VirtualMachineDiskInfo diskInfo) {
VolumeVO volumeVO = new VolumeVO(type, volumeName, zoneId, domainId, accountId, diskOfferingId, provisioningType, size, null, null, null);
volumeVO.setFormat(Storage.ImageFormat.OVA);
volumeVO.setPath(volumeName);
volumeVO.setState(Volume.State.Ready);
volumeVO.setInstanceId(instanceId);
volumeVO.setPoolId(poolId);
volumeVO.setTemplateId(templateId);
volumeVO.setAttached(new Date());
volumeVO.setRemoved(null);
volumeVO.setChainInfo(GSON.toJson(diskInfo));
if (unitNumber != null) {
volumeVO.setDeviceId(unitNumber.longValue());
}
return _volumeDao.persist(volumeVO);
}
/**
* Get volume name from VM disk
*/
private String getVolumeName(VirtualDisk disk, VirtualMachineMO vmToImport) throws Exception {
return vmToImport.getVmdkFileBaseName(disk);
}
/**
* Get provisioning type for VM disk info
*/
private Storage.ProvisioningType getProvisioningType(VirtualDiskFlatVer2BackingInfo backing) {
Boolean thinProvisioned = backing.isThinProvisioned();
if (BooleanUtils.isTrue(thinProvisioned)) {
return Storage.ProvisioningType.THIN;
}
return Storage.ProvisioningType.SPARSE;
}
/**
* Get disk offering ID for volume being imported. If it is not found it is mapped to "Custom" ID
*/
private long getDiskOfferingId(long size, Storage.ProvisioningType provisioningType) {
List<DiskOfferingVO> offerings = diskOfferingDao.listAllBySizeAndProvisioningType(size, provisioningType);
return CollectionUtils.isNotEmpty(offerings) ? offerings.get(0).getId() : diskOfferingDao.findByUniqueName("Cloud.Com-Custom").getId();
}
protected VolumeVO updateVolume(VirtualDisk disk, Map<VirtualDisk, VolumeVO> disksMapping, VirtualMachineMO vmToImport, Long poolId, VirtualMachine vm) throws Exception {
VolumeVO volume = disksMapping.get(disk);
String volumeName = getVolumeName(disk, vmToImport);
volume.setPath(volumeName);
volume.setPoolId(poolId);
VirtualMachineDiskInfo diskInfo = getDiskInfo(vmToImport, poolId, volumeName);
volume.setChainInfo(GSON.toJson(diskInfo));
volume.setInstanceId(vm.getId());
volume.setState(Volume.State.Ready);
volume.setAttached(new Date());
_volumeDao.update(volume.getId(), volume);
if (volume.getRemoved() != null) {
s_logger.debug(String.format("Marking volume [uuid: %s] of restored VM [%s] as non removed.", volume.getUuid(),
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(vm, "uuid", "instanceName")));
_volumeDao.unremove(volume.getId());
if (vm.getType() == Type.User) {
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), volume.getDiskOfferingId(), null, volume.getSize(),
Volume.class.getName(), volume.getUuid(), volume.isDisplayVolume());
_resourceLimitService.incrementResourceCount(vm.getAccountId(), Resource.ResourceType.volume, volume.isDisplayVolume());
_resourceLimitService.incrementResourceCount(vm.getAccountId(), Resource.ResourceType.primary_storage, volume.isDisplayVolume(), volume.getSize());
}
}
return volume;
}
/**
* Get volumes for VM being imported
*/
private void syncVMVolumes(VMInstanceVO vmInstanceVO, List<VirtualDisk> virtualDisks, Map<VirtualDisk, VolumeVO> disksMapping, VirtualMachineMO vmToImport, Backup backup)
throws Exception {
long zoneId = vmInstanceVO.getDataCenterId();
long accountId = vmInstanceVO.getAccountId();
long domainId = vmInstanceVO.getDomainId();
long templateId = vmInstanceVO.getTemplateId();
long instanceId = vmInstanceVO.getId();
String operation = "";
for (VirtualDisk disk : virtualDisks) {
Long poolId = getPoolId(disk);
Volume volume = null;
if (disksMapping.containsKey(disk) && disksMapping.get(disk) != null) {
volume = updateVolume(disk, disksMapping, vmToImport, poolId, vmInstanceVO);
operation = "updated";
} else {
volume = createVolume(disk, vmToImport, domainId, zoneId, accountId, instanceId, poolId, templateId, backup, true);
operation = "created";
}
s_logger.debug(String.format("Sync volumes to %s in backup restore operation: %s volume [id: %s].", vmInstanceVO, operation, volume.getUuid()));
}
}
private VirtualMachineDiskInfo getDiskInfo(VirtualMachineMO vmMo, Long poolId, String volumeName) throws Exception {
VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder();
String poolName = _storagePoolDao.findById(poolId).getUuid().replace("-", "");
return diskInfoBuilder.getDiskInfoByBackingFileBaseName(volumeName, poolName);
}
private VolumeVO createVolume(VirtualDisk disk, VirtualMachineMO vmToImport, long domainId, long zoneId, long accountId, long instanceId, Long poolId, long templateId, Backup backup, boolean isImport) throws Exception {
VMInstanceVO vm = virtualMachineDao.findByIdIncludingRemoved(backup.getVmId());
if (vm == null) {
throw new CloudRuntimeException("Failed to find the backup volume information from the VM backup");
}
List<Backup.VolumeInfo> backedUpVolumes = vm.getBackupVolumeList();
Volume.Type type = Volume.Type.DATADISK;
Long size = disk.getCapacityInBytes();
if (isImport) {
for (Backup.VolumeInfo volumeInfo : backedUpVolumes) {
if (volumeInfo.getSize().equals(disk.getCapacityInBytes())) {
type = volumeInfo.getType();
}
}
}
VirtualDeviceBackingInfo backing = disk.getBacking();
checkBackingInfo(backing);
VirtualDiskFlatVer2BackingInfo info = (VirtualDiskFlatVer2BackingInfo)backing;
String volumeName = getVolumeName(disk, vmToImport);
Storage.ProvisioningType provisioningType = getProvisioningType(info);
long diskOfferingId = getDiskOfferingId(size, provisioningType);
Integer unitNumber = disk.getUnitNumber();
VirtualMachineDiskInfo diskInfo = getDiskInfo(vmToImport, poolId, volumeName);
return createVolumeRecord(type, volumeName, zoneId, domainId, accountId, diskOfferingId, provisioningType, size, instanceId, poolId, templateId, unitNumber, diskInfo);
}
protected String createVolumeInfoFromVolumes(List<VolumeVO> vmVolumes) {
try {
List<Backup.VolumeInfo> list = new ArrayList<>();
for (VolumeVO vol : vmVolumes) {
list.add(new Backup.VolumeInfo(vol.getUuid(), vol.getPath(), vol.getVolumeType(), vol.getSize()));
}
return GSON.toJson(list.toArray(), Backup.VolumeInfo[].class);
} catch (Exception e) {
if (CollectionUtils.isEmpty(vmVolumes) || vmVolumes.get(0).getInstanceId() == null) {
s_logger.error(String.format("Failed to create VolumeInfo of VM [id: null] volumes due to: [%s].", e.getMessage()), e);
} else {
s_logger.error(String.format("Failed to create VolumeInfo of VM [id: %s] volumes due to: [%s].", vmVolumes.get(0).getInstanceId(), e.getMessage()), e);
}
throw e;
}
}
/**
* Get physical network ID from zoneId and Vmware label
*/
private long getPhysicalNetworkId(Long zoneId, String tag) {
List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZone(zoneId);
for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
PhysicalNetworkTrafficTypeVO vo = _physicalNetworkTrafficTypeDao.findBy(physicalNetwork.getId(), TrafficType.Guest);
if (vo == null) {
continue;
}
String vmwareNetworkLabel = vo.getVmwareNetworkLabel();
if (!vmwareNetworkLabel.startsWith(tag)) {
throw new CloudRuntimeException("Vmware network label does not start with: " + tag);
}
return physicalNetwork.getId();
}
throw new CloudRuntimeException("Could not find guest physical network matching tag: " + tag + " on zone " + zoneId);
}
/**
* Create and persist network
*/
private NetworkVO createNetworkRecord(Long zoneId, String tag, String vlan, long accountId, long domainId) {
Long physicalNetworkId = getPhysicalNetworkId(zoneId, tag);
final long id = networkDao.getNextInSequence(Long.class, "id");
NetworkVO networkVO = new NetworkVO(id, TrafficType.Guest, Networks.Mode.Dhcp, BroadcastDomainType.Vlan, 9L, domainId, accountId, id, "Imported-network-" + id,
"Imported-network-" + id, null, Network.GuestType.Isolated, zoneId, physicalNetworkId, ControlledEntity.ACLType.Account, false, null, false);
networkVO.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlan));
networkVO.setGuruName("ExternalGuestNetworkGuru");
networkVO.setState(Network.State.Implemented);
return networkDao.persist(networkVO);
}
/**
* Get network from VM network name
*/
private NetworkVO getGuestNetworkFromNetworkMorName(String name, long accountId, Long zoneId, long domainId) {
String prefix = "cloud.guest.";
String nameWithoutPrefix = name.replace(prefix, "");
String[] parts = nameWithoutPrefix.split("\\.");
String vlan = parts[0];
String tag = parts[parts.length - 1];
String[] tagSplit = tag.split("-");
tag = tagSplit[tagSplit.length - 1];
s_logger.debug(String.format("Trying to find network with vlan: [%s].", vlan));
NetworkVO networkVO = networkDao.findByVlan(vlan);
if (networkVO == null) {
networkVO = createNetworkRecord(zoneId, tag, vlan, accountId, domainId);
s_logger.debug(String.format("Created new network record [id: %s] with details [zoneId: %s, tag: %s, vlan: %s, accountId: %s and domainId: %s].",
networkVO.getUuid(), zoneId, tag, vlan, accountId, domainId));
}
return networkVO;
}
/**
* Get map between VM networks and its IDs on CloudStack
*/
private Map<String, NetworkVO> getNetworksMapping(String[] vmNetworkNames, long accountId, long zoneId, long domainId) {
Map<String, NetworkVO> mapping = new HashMap<>();
for (String networkName : vmNetworkNames) {
NetworkVO networkVO = getGuestNetworkFromNetworkMorName(networkName, accountId, zoneId, domainId);
s_logger.debug(String.format("Mapping network name [%s] to networkVO [id: %s].", networkName, networkVO.getUuid()));
mapping.put(networkName, networkVO);
}
return mapping;
}
/**
* Get network MO from VM NIC
*/
private NetworkMO getNetworkMO(VirtualEthernetCard nic, VmwareContext context) {
VirtualDeviceConnectInfo connectable = nic.getConnectable();
VirtualEthernetCardNetworkBackingInfo info = (VirtualEthernetCardNetworkBackingInfo)nic.getBacking();
ManagedObjectReference networkMor = info.getNetwork();
if (networkMor == null) {
throw new CloudRuntimeException("Could not find network for NIC on: " + nic.getMacAddress());
}
return new NetworkMO(context, networkMor);
}
private Pair<String, String> getNicMacAddressAndNetworkName(VirtualDevice nicDevice, VmwareContext context) throws Exception {
VirtualEthernetCard nic = (VirtualEthernetCard)nicDevice;
String macAddress = nic.getMacAddress();
NetworkMO networkMO = getNetworkMO(nic, context);
String networkName = networkMO.getName();
return new Pair<>(macAddress, networkName);
}
private void syncVMNics(VirtualDevice[] nicDevices, DatacenterMO dcMo, Map<String, NetworkVO> networksMapping, VMInstanceVO vm) throws Exception {
VmwareContext context = dcMo.getContext();
List<NicVO> allNics = nicDao.listByVmId(vm.getId());
for (VirtualDevice nicDevice : nicDevices) {
Pair<String, String> pair = getNicMacAddressAndNetworkName(nicDevice, context);
String macAddress = pair.first();
String networkName = pair.second();
NetworkVO networkVO = networksMapping.get(networkName);
NicVO nicVO = nicDao.findByNetworkIdAndMacAddressIncludingRemoved(networkVO.getId(), macAddress);
if (nicVO != null) {
s_logger.warn(String.format("Find NIC in DB with networkId [%s] and MAC Address [%s], so this NIC will be removed from list of unmapped NICs of VM [id: %s, name: %s].",
networkVO.getId(), macAddress, vm.getUuid(), vm.getInstanceName()));
allNics.remove(nicVO);
if (nicVO.getRemoved() != null) {
nicDao.unremove(nicVO.getId());
}
}
}
for (final NicVO unMappedNic : allNics) {
s_logger.debug(String.format("Removing NIC [%s] from backup restored %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(unMappedNic, "uuid", "macAddress"), vm));
vmManager.removeNicFromVm(vm, unMappedNic);
}
}
private Map<VirtualDisk, VolumeVO> getDisksMapping(Backup backup, List<VirtualDisk> virtualDisks) {
final VMInstanceVO vm = virtualMachineDao.findByIdIncludingRemoved(backup.getVmId());
if (vm == null) {
throw new CloudRuntimeException("Failed to find the volumes details from the VM backup");
}
List<Backup.VolumeInfo> backedUpVolumes = vm.getBackupVolumeList();
Map<String, Boolean> usedVols = new HashMap<>();
Map<VirtualDisk, VolumeVO> map = new HashMap<>();
for (Backup.VolumeInfo backedUpVol : backedUpVolumes) {
VolumeVO volumeExtra = _volumeDao.findByUuid(backedUpVol.getUuid());
if (volumeExtra != null) {
s_logger.debug(String.format("Marking volume [id: %s] of VM [%s] as removed for the backup process.", backedUpVol.getUuid(), ReflectionToStringBuilderUtils.reflectOnlySelectedFields(vm, "uuid", "instanceName")));
_volumeDao.remove(volumeExtra.getId());
if (vm.getType() == Type.User) {
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DETACH, volumeExtra.getAccountId(), volumeExtra.getDataCenterId(), volumeExtra.getId(),
volumeExtra.getName(), volumeExtra.getDiskOfferingId(), null, volumeExtra.getSize(), Volume.class.getName(),
volumeExtra.getUuid(), volumeExtra.isDisplayVolume());
_resourceLimitService.decrementResourceCount(vm.getAccountId(), Resource.ResourceType.volume, volumeExtra.isDisplayVolume());
_resourceLimitService.decrementResourceCount(vm.getAccountId(), Resource.ResourceType.primary_storage, volumeExtra.isDisplayVolume(), volumeExtra.getSize());
}
}
for (VirtualDisk disk : virtualDisks) {
if (!map.containsKey(disk) && backedUpVol.getSize().equals(disk.getCapacityInBytes()) && !usedVols.containsKey(backedUpVol.getUuid())) {
String volId = backedUpVol.getUuid();
VolumeVO vol = _volumeDao.findByUuidIncludingRemoved(volId);
usedVols.put(backedUpVol.getUuid(), true);
map.put(disk, vol);
s_logger.debug("VM restore mapping for disk " + disk.getBacking() + " (capacity: " + toHumanReadableSize(disk.getCapacityInBytes()) + ") with volume ID" + vol.getId());
}
}
}
return map;
}
/**
* Find VM on datacenter
*/
private VirtualMachineMO findVM(DatacenterMO dcMo, String path) throws Exception {
VirtualMachineMO vm = dcMo.findVm(path);
if (vm == null) {
throw new CloudRuntimeException("Error finding VM: " + path);
}
return vm;
}
/**
* Find restored volume based on volume info
*/
private VirtualDisk findRestoredVolume(Backup.VolumeInfo volumeInfo, VirtualMachineMO vm) throws Exception {
List<VirtualDisk> virtualDisks = vm.getVirtualDisks();
for (VirtualDisk disk : virtualDisks) {
if (disk.getCapacityInBytes().equals(volumeInfo.getSize())) {
return disk;
}
}
throw new CloudRuntimeException("Volume to restore could not be found");
}
/**
* Get volume full path
*/
private String getVolumeFullPath(VirtualDisk disk) {
VirtualDeviceBackingInfo backing = disk.getBacking();
checkBackingInfo(backing);
VirtualDiskFlatVer2BackingInfo info = (VirtualDiskFlatVer2BackingInfo)backing;
return info.getFileName();
}
/**
* Get dest volume full path
*/
private String getDestVolumeFullPath(VirtualMachineMO vmMo) throws Exception {
VirtualDisk vmDisk = vmMo.getVirtualDisks().get(0);
String vmDiskPath = vmMo.getVmdkFileBaseName(vmDisk);
String vmDiskFullPath = getVolumeFullPath(vmMo.getVirtualDisks().get(0));
String uuid = UUID.randomUUID().toString().replace("-", "");
return vmDiskFullPath.replace(vmDiskPath, uuid);
}
/**
* Get dest datastore mor
*/
private ManagedObjectReference getDestStoreMor(VirtualMachineMO vmMo) throws Exception {
VirtualDisk vmDisk = vmMo.getVirtualDisks().get(0);
VirtualDeviceBackingInfo backing = vmDisk.getBacking();
checkBackingInfo(backing);
VirtualDiskFlatVer2BackingInfo info = (VirtualDiskFlatVer2BackingInfo)backing;
return info.getDatastore();
}
@Override
public VirtualMachine importVirtualMachineFromBackup(long zoneId, long domainId, long accountId, long userId, String vmInternalName, Backup backup) throws Exception {
s_logger.debug(String.format("Trying to import VM [vmInternalName: %s] from Backup [%s].", vmInternalName,
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(backup, "id", "uuid", "vmId", "externalId", "backupType")));
DatacenterMO dcMo = getDatacenterMO(zoneId);
VirtualMachineMO vmToImport = dcMo.findVm(vmInternalName);
if (vmToImport == null) {
throw new CloudRuntimeException("Error finding VM: " + vmInternalName);
}
VirtualMachineConfigSummary configSummary = vmToImport.getConfigSummary();
VirtualMachineRuntimeInfo runtimeInfo = vmToImport.getRuntimeInfo();
List<VirtualDisk> virtualDisks = vmToImport.getVirtualDisks();
String[] vmNetworkNames = vmToImport.getNetworks();
VirtualDevice[] nicDevices = vmToImport.getNicDevices();
Map<VirtualDisk, VolumeVO> disksMapping = getDisksMapping(backup, virtualDisks);
Map<String, NetworkVO> networksMapping = getNetworksMapping(vmNetworkNames, accountId, zoneId, domainId);
long guestOsId = getImportingVMGuestOs(configSummary);
long serviceOfferingId = getImportingVMServiceOffering(configSummary, runtimeInfo);
long templateId = getImportingVMTemplate(virtualDisks, dcMo, vmInternalName, guestOsId, accountId, disksMapping, backup);
VMInstanceVO vm = getVM(vmInternalName, templateId, guestOsId, serviceOfferingId, zoneId, accountId, userId, domainId);
syncVMVolumes(vm, virtualDisks, disksMapping, vmToImport, backup);
syncVMNics(nicDevices, dcMo, networksMapping, vm);
return vm;
}
@Override
public boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backup.VolumeInfo volumeInfo, VirtualMachine vm, long poolId, Backup backup)
throws Exception {
DatacenterMO dcMo = getDatacenterMO(zoneId);
VirtualMachineMO vmRestored = findVM(dcMo, location);
VirtualMachineMO vmMo = findVM(dcMo, vm.getInstanceName());
VirtualDisk restoredDisk = findRestoredVolume(volumeInfo, vmRestored);
String diskPath = vmRestored.getVmdkFileBaseName(restoredDisk);
s_logger.debug("Restored disk size=" + toHumanReadableSize(restoredDisk.getCapacityInKB() * Resource.ResourceType.bytesToKiB) + " path=" + diskPath);
// Detach restored VM disks
vmRestored.detachDisk(String.format("%s/%s.vmdk", location, diskPath), false);
String srcPath = getVolumeFullPath(restoredDisk);
String destPath = getDestVolumeFullPath(vmMo);
VirtualDiskManagerMO virtualDiskManagerMO = new VirtualDiskManagerMO(dcMo.getContext());
// Copy volume to the VM folder
s_logger.debug(String.format("Moving volume from %s to %s", srcPath, destPath));
virtualDiskManagerMO.moveVirtualDisk(srcPath, dcMo.getMor(), destPath, dcMo.getMor(), true);
try {
// Attach volume to VM
vmMo.attachDisk(new String[] {destPath}, getDestStoreMor(vmMo));
} catch (Exception e) {
s_logger.error("Failed to attach the restored volume: " + diskPath, e);
return false;
} finally {
// Destroy restored VM
vmRestored.destroy();
}
s_logger.debug(String.format("Attaching disk %s to vm %s", destPath, vm.getId()));
VirtualDisk attachedDisk = getAttachedDisk(vmMo, destPath);
if (attachedDisk == null) {
s_logger.error("Failed to get the attached the (restored) volume " + destPath);
return false;
}
s_logger.debug(String.format("Creating volume entry for disk %s attached to vm %s", destPath, vm.getId()));
createVolume(attachedDisk, vmMo, vm.getDomainId(), vm.getDataCenterId(), vm.getAccountId(), vm.getId(), poolId, vm.getTemplateId(), backup, false);
if (vm.getBackupOfferingId() == null) {
return true;
}
VMInstanceVO vmVO = (VMInstanceVO)vm;
vmVO.setBackupVolumes(createVolumeInfoFromVolumes(_volumeDao.findByInstance(vm.getId())));
vmDao.update(vmVO.getId(), vmVO);
return true;
}
private VirtualDisk getAttachedDisk(VirtualMachineMO vmMo, String diskFullPath) throws Exception {
for (VirtualDisk disk : vmMo.getVirtualDisks()) {
if (getVolumeFullPath(disk).equals(diskFullPath)) {
return disk;
}
}
return null;
}
private boolean isInterClusterMigration(Long srcClusterId, Long destClusterId) {
return srcClusterId != null && destClusterId != null && ! srcClusterId.equals(destClusterId);
}
private String getHostGuidForLocalStorage(StoragePool pool) {
List<StoragePoolHostVO> storagePoolHostVOs = storagePoolHostDao.listByPoolId(pool.getId());
StoragePoolHostVO storagePoolHostVO = storagePoolHostVOs.get(0);
HostVO hostVO = hostDao.findById(storagePoolHostVO.getHostId());
return hostVO.getGuid();
}
private String getHostGuidInTargetCluster(boolean isInterClusterMigration, Long destClusterId) {
String hostGuidInTargetCluster = null;
if (isInterClusterMigration) {
Host hostInTargetCluster = null;
// Without host vMotion might fail between non-shared storages with error similar to,
// https://kb.vmware.com/s/article/1003795
// As this is offline migration VM won't be started on this host
List<HostVO> hosts = hostDao.findHypervisorHostInCluster(destClusterId);
if (CollectionUtils.isNotEmpty(hosts)) {
hostInTargetCluster = hosts.get(0);
}
if (hostInTargetCluster == null) {
throw new CloudRuntimeException("Migration failed, unable to find suitable target host for VM placement while migrating between storage pools of different clusters without shared storages");
}
hostGuidInTargetCluster = hostInTargetCluster.getGuid();
}
return hostGuidInTargetCluster;
}
@Override
public List<Command> finalizeMigrate(VirtualMachine vm, Map<Volume, StoragePool> volumeToPool) {
List<Command> commands = new ArrayList<>();
// OfflineVmwareMigration: specialised migration command
List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerTo = new ArrayList<>();
Long poolClusterId = null;
StoragePool targetLocalPoolForVM = null;
for (Map.Entry<Volume, StoragePool> entry : volumeToPool.entrySet()) {
Volume volume = entry.getKey();
StoragePool pool = entry.getValue();
VolumeTO volumeTo = new VolumeTO(volume, _storagePoolDao.findById(pool.getId()));
StorageFilerTO filerTo = new StorageFilerTO(pool);
if (pool.getClusterId() != null) {
poolClusterId = pool.getClusterId();
}
if (volume.getVolumeType().equals(Volume.Type.ROOT) && pool.isLocal()) {
targetLocalPoolForVM = pool;
}
volumeToFilerTo.add(new Pair<>(volumeTo, filerTo));
}
final Long destClusterId = poolClusterId;
final Long srcClusterId = vmManager.findClusterAndHostIdForVm(vm, true).first();
final boolean isInterClusterMigration = isInterClusterMigration(destClusterId, srcClusterId);
String targetHostGuid = getTargetHostGuid(targetLocalPoolForVM, destClusterId, isInterClusterMigration);
MigrateVmToPoolCommand migrateVmToPoolCommand = new MigrateVmToPoolCommand(vm.getInstanceName(),
volumeToFilerTo, targetHostGuid, true);
commands.add(migrateVmToPoolCommand);
// OfflineVmwareMigration: cleanup if needed
if (isInterClusterMigration) {
final String srcDcName = _clusterDetailsDao.getVmwareDcName(srcClusterId);
final String destDcName = _clusterDetailsDao.getVmwareDcName(destClusterId);
if (srcDcName != null && destDcName != null && !srcDcName.equals(destDcName)) {
final UnregisterVMCommand unregisterVMCommand = new UnregisterVMCommand(vm.getInstanceName(), true);
unregisterVMCommand.setCleanupVmFiles(true);
commands.add(unregisterVMCommand);
}
}
return commands;
}
private String getTargetHostGuid(StoragePool targetLocalPoolForVM, Long destClusterId, boolean isInterClusterMigration) {
String targetHostGuid = null;
if (targetLocalPoolForVM != null) {
// Get the target host for local storage migration
targetHostGuid = getHostGuidForLocalStorage(targetLocalPoolForVM);
} else {
targetHostGuid = getHostGuidInTargetCluster(isInterClusterMigration, destClusterId);
}
return targetHostGuid;
}
@Override
protected VirtualMachineTO toVirtualMachineTO(VirtualMachineProfile vmProfile) {
return super.toVirtualMachineTO(vmProfile);
}
}