diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java index 83c7529b22b3..d6b007c7640c 100644 --- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java +++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java @@ -40,6 +40,8 @@ public interface VmDetailConstants { String KVM_VNC_PORT = "kvm.vnc.port"; String KVM_VNC_ADDRESS = "kvm.vnc.address"; + String KVM_VNC_PASSWORD = "kvm.vnc.password"; + // KVM specific, custom virtual GPU hardware String VIDEO_HARDWARE = "video.hardware"; String VIDEO_RAM = "video.ram"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java index 70233317cc51..4fa68a0d2a07 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java @@ -84,7 +84,7 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, - description = "the hypervisor name of the instance") + description = "the name of the instance as it is known to the hypervisor") private String name; @Parameter(name = ApiConstants.DISPLAY_NAME, diff --git a/api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java b/api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java index 2876a0127be5..53aece949649 100644 --- a/api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java +++ b/api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java @@ -17,13 +17,20 @@ package org.apache.cloudstack.vm; +import com.cloud.hypervisor.Hypervisor; import com.cloud.utils.component.PluggableService; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.KVM; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.VMware; public interface UnmanagedVMsManager extends VmImportService, UnmanageVMService, PluggableService, Configurable { ConfigKey UnmanageVMPreserveNic = new ConfigKey<>("Advanced", Boolean.class, "unmanage.vm.preserve.nics", "false", "If set to true, do not remove VM nics (and its MAC addresses) when unmanaging a VM, leaving them allocated but not reserved. " + "If set to false, nics are removed and MAC addresses can be reassigned", true, ConfigKey.Scope.Zone); + + static boolean isSupported(Hypervisor.HypervisorType hypervisorType) { + return hypervisorType == VMware || hypervisorType == KVM; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java index 344565b92a76..094c5c4a923e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java @@ -345,6 +345,14 @@ public boolean parseDomainXML(String domXML) { period = "0"; } + if(bytes == null) { + bytes = "0"; + } + + if(period == null) { + period = "0"; + } + if (StringUtils.isEmpty(backendModel)) { def = new RngDef(path, Integer.parseInt(bytes), Integer.parseInt(period)); } else { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java new file mode 100644 index 000000000000..d59a5e9ff4dc --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java @@ -0,0 +1,223 @@ +// 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.kvm.resource.wrapper; + +import com.cloud.agent.api.GetUnmanagedInstancesAnswer; +import com.cloud.agent.api.GetUnmanagedInstancesCommand; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser; +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import com.cloud.utils.Pair; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImgException; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.vm.UnmanagedInstanceTO; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; +import org.libvirt.Connect; +import org.libvirt.Domain; +import org.libvirt.LibvirtException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@ResourceWrapper(handles=GetUnmanagedInstancesCommand.class) +public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWrapper { + private static final Logger LOGGER = Logger.getLogger(LibvirtPrepareUnmanageVMInstanceCommandWrapper.class); + + @Override + public GetUnmanagedInstancesAnswer execute(GetUnmanagedInstancesCommand command, LibvirtComputingResource libvirtComputingResource) { + LOGGER.info("Fetching unmanaged instance on host"); + + HashMap unmanagedInstances = new HashMap<>(); + try { + final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper(); + final Connect conn = libvirtUtilitiesHelper.getConnection(); + final List domains = getDomains(command, libvirtComputingResource, conn); + + for (Domain domain : domains) { + UnmanagedInstanceTO instance = getUnmanagedInstance(libvirtComputingResource, domain, conn); + unmanagedInstances.put(instance.getName(), instance); + domain.free(); + } + } catch (Exception e) { + LOGGER.error("GetUnmanagedInstancesCommand failed due to " + e.getMessage()); + throw new CloudRuntimeException("GetUnmanagedInstancesCommand failed due to " + e.getMessage()); + } + + return new GetUnmanagedInstancesAnswer(command, "True", unmanagedInstances); + } + + private List getDomains(GetUnmanagedInstancesCommand command, + LibvirtComputingResource libvirtComputingResource, + Connect conn) throws LibvirtException, CloudRuntimeException { + final List domains = new ArrayList<>(); + final String vmNameCmd = command.getInstanceName(); + if (StringUtils.isNotBlank(vmNameCmd)) { + final Domain domain = libvirtComputingResource.getDomain(conn, vmNameCmd); + if (domain == null) { + LOGGER.error("GetUnmanagedInstancesCommand: vm not found " + vmNameCmd); + throw new CloudRuntimeException("GetUnmanagedInstancesCommand: vm not found " + vmNameCmd); + } + + checkIfVmExists(vmNameCmd,domain); + checkIfVmIsManaged(command,vmNameCmd,domain); + + domains.add(domain); + } else { + final List allVmNames = libvirtComputingResource.getAllVmNames(conn); + for (String name : allVmNames) { + if (!command.hasManagedInstance(name)) { + final Domain domain = libvirtComputingResource.getDomain(conn, name); + domains.add(domain); + } + } + } + return domains; + } + + private void checkIfVmExists(String vmNameCmd,final Domain domain) throws LibvirtException { + if (StringUtils.isNotEmpty(vmNameCmd) && + !vmNameCmd.equals(domain.getName())) { + LOGGER.error("GetUnmanagedInstancesCommand: exact vm name not found " + vmNameCmd); + throw new CloudRuntimeException("GetUnmanagedInstancesCommand: exact vm name not found " + vmNameCmd); + } + } + + private void checkIfVmIsManaged(GetUnmanagedInstancesCommand command,String vmNameCmd,final Domain domain) throws LibvirtException { + if (command.hasManagedInstance(domain.getName())) { + LOGGER.error("GetUnmanagedInstancesCommand: vm already managed " + vmNameCmd); + throw new CloudRuntimeException("GetUnmanagedInstancesCommand: vm already managed " + vmNameCmd); + } + } + private UnmanagedInstanceTO getUnmanagedInstance(LibvirtComputingResource libvirtComputingResource, Domain domain, Connect conn) { + try { + final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser(); + parser.parseDomainXML(domain.getXMLDesc(1)); + + final UnmanagedInstanceTO instance = new UnmanagedInstanceTO(); + instance.setName(domain.getName()); + + instance.setCpuCores((int) LibvirtComputingResource.countDomainRunningVcpus(domain)); + instance.setCpuSpeed(parser.getCpuTuneDef().getShares()/instance.getCpuCores()); + + if (parser.getCpuModeDef() != null) { + instance.setCpuCoresPerSocket(parser.getCpuModeDef().getCoresPerSocket()); + } + instance.setPowerState(getPowerState(libvirtComputingResource.getVmState(conn,domain.getName()))); + instance.setMemory((int) LibvirtComputingResource.getDomainMemory(domain) / 1024); + instance.setNics(getUnmanagedInstanceNics(parser.getInterfaces())); + instance.setDisks(getUnmanagedInstanceDisks(parser.getDisks(),libvirtComputingResource)); + instance.setVncPassword(parser.getVncPasswd() + "aaaaaaaaaaaaaa"); // Suffix back extra characters for DB compatibility + + return instance; + } catch (Exception e) { + LOGGER.info("Unable to retrieve unmanaged instance info. " + e.getMessage()); + throw new CloudRuntimeException("Unable to retrieve unmanaged instance info. " + e.getMessage()); + } + } + + private UnmanagedInstanceTO.PowerState getPowerState(VirtualMachine.PowerState vmPowerState) { + switch (vmPowerState) { + case PowerOn: + return UnmanagedInstanceTO.PowerState.PowerOn; + case PowerOff: + return UnmanagedInstanceTO.PowerState.PowerOff; + default: + return UnmanagedInstanceTO.PowerState.PowerUnknown; + + } + } + + private List getUnmanagedInstanceNics(List interfaces) { + final ArrayList nics = new ArrayList<>(interfaces.size()); + int counter = 0; + for (LibvirtVMDef.InterfaceDef interfaceDef : interfaces) { + final UnmanagedInstanceTO.Nic nic = new UnmanagedInstanceTO.Nic(); + nic.setNicId(String.valueOf(counter++)); + nic.setMacAddress(interfaceDef.getMacAddress()); + nic.setAdapterType(interfaceDef.getModel().toString()); + nic.setNetwork(interfaceDef.getDevName()); + nic.setPciSlot(interfaceDef.getSlot().toString()); + nic.setVlan(interfaceDef.getVlanTag()); + nics.add(nic); + } + return nics; + } + + private List getUnmanagedInstanceDisks(List disksInfo, LibvirtComputingResource libvirtComputingResource){ + final ArrayList disks = new ArrayList<>(disksInfo.size()); + int counter = 0; + for (LibvirtVMDef.DiskDef diskDef : disksInfo) { + if (diskDef.getDeviceType() != LibvirtVMDef.DiskDef.DeviceType.DISK) { + continue; + } + + final UnmanagedInstanceTO.Disk disk = new UnmanagedInstanceTO.Disk(); + Long size = null; + String imagePath = null; + try { + QemuImgFile file = new QemuImgFile(diskDef.getSourcePath()); + QemuImg qemu = new QemuImg(0); + Map info = qemu.info(file); + size = Long.parseLong(info.getOrDefault("virtual_size", "0")); + imagePath = info.getOrDefault("image", null); + } catch (QemuImgException | LibvirtException e) { + throw new RuntimeException(e); + } + + disk.setPosition(counter); + disk.setCapacity(size); + disk.setDiskId(String.valueOf(counter++)); + disk.setLabel(diskDef.getDiskLabel()); + disk.setController(diskDef.getBusType().toString()); + + + Pair sourceHostPath = getSourceHostPath(libvirtComputingResource, diskDef.getSourcePath()); + if (sourceHostPath != null) { + disk.setDatastoreHost(sourceHostPath.first()); + disk.setDatastorePath(sourceHostPath.second()); + } else { + disk.setDatastorePath(diskDef.getSourcePath()); + disk.setDatastoreHost(diskDef.getSourceHost()); + } + + disk.setDatastoreType(diskDef.getDiskType().toString()); + disk.setDatastorePort(diskDef.getSourceHostPort()); + disk.setImagePath(imagePath); + disk.setDatastoreName(imagePath.substring(imagePath.lastIndexOf("/"))); + disks.add(disk); + } + return disks; + } + + private Pair getSourceHostPath(LibvirtComputingResource libvirtComputingResource, String diskPath) { + int pathEnd = diskPath.lastIndexOf("/"); + if (pathEnd >= 0) { + diskPath = diskPath.substring(0, pathEnd); + return libvirtComputingResource.getSourceHostPath(diskPath); + } + return null; + } +} diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareUnmanageVMInstanceCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareUnmanageVMInstanceCommandWrapper.java new file mode 100644 index 000000000000..683730890380 --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareUnmanageVMInstanceCommandWrapper.java @@ -0,0 +1,51 @@ +// 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.kvm.resource.wrapper; + +import com.cloud.agent.api.PrepareUnmanageVMInstanceAnswer; +import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import org.apache.log4j.Logger; +import org.libvirt.Connect; +import org.libvirt.Domain; + +@ResourceWrapper(handles=PrepareUnmanageVMInstanceCommand.class) +public final class LibvirtPrepareUnmanageVMInstanceCommandWrapper extends CommandWrapper { + private static final Logger LOGGER = Logger.getLogger(LibvirtPrepareUnmanageVMInstanceCommandWrapper.class); + @Override + public PrepareUnmanageVMInstanceAnswer execute(PrepareUnmanageVMInstanceCommand command, LibvirtComputingResource libvirtComputingResource) { + final String vmName = command.getInstanceName(); + final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper(); + LOGGER.debug(String.format("Verify if KVM instance: [%s] is available before Unmanaging VM.", vmName)); + try { + final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName); + final Domain domain = libvirtComputingResource.getDomain(conn, vmName); + if (domain == null) { + LOGGER.error("Prepare Unmanage VMInstanceCommand: vm not found " + vmName); + new PrepareUnmanageVMInstanceAnswer(command, false, String.format("Cannot find VM with name [%s] in KVM host.", vmName)); + } + } catch (Exception e){ + LOGGER.error("PrepareUnmanagedInstancesCommand failed due to " + e.getMessage()); + return new PrepareUnmanageVMInstanceAnswer(command, false, "Error: " + e.getMessage()); + } + + return new PrepareUnmanageVMInstanceAnswer(command, true, "OK"); + } +} diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 3c0deac28378..db98caa0b590 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -129,6 +129,7 @@ import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.security.ParserUtils; import org.apache.cloudstack.vm.schedule.VMScheduleManager; +import org.apache.cloudstack.vm.UnmanagedVMsManager; import org.apache.commons.codec.binary.Base64; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; @@ -235,7 +236,6 @@ import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; -import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.hypervisor.kvm.dpdk.DpdkHelper; @@ -4472,7 +4472,6 @@ private UserVmVO commitUserVm(final boolean isImport, final DataCenter zone, fin if (customParameters.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { // already verified for positive number rootDiskSize = Long.parseLong(customParameters.get(VmDetailConstants.ROOT_DISK_SIZE)); - VMTemplateVO templateVO = _templateDao.findById(template.getId()); if (templateVO == null) { throw new InvalidParameterValueException("Unable to look up template by id " + template.getId()); @@ -4501,6 +4500,8 @@ private UserVmVO commitUserVm(final boolean isImport, final DataCenter zone, fin } } + setVncPasswordForKvmIfAvailable(customParameters, vm); + vm.setUserVmType(vmType); _vmDao.persist(vm); for (String key : customParameters.keySet()) { @@ -4875,7 +4876,7 @@ public String validateUserData(String userData, HTTPMethod httpmethod) { } @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", async = true) + @ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true) public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException { long vmId = cmd.getEntityId(); if (!cmd.getStartVm()) { @@ -4886,7 +4887,6 @@ public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableExc Long hostId = cmd.getHostId(); Map additionalParams = new HashMap<>(); Map diskOfferingMap = cmd.getDataDiskTemplateToDiskOfferingMap(); - Map details = cmd.getDetails(); if (cmd instanceof DeployVMCmdByAdmin) { DeployVMCmdByAdmin adminCmd = (DeployVMCmdByAdmin)cmd; podId = adminCmd.getPodId(); @@ -8240,8 +8240,9 @@ public boolean unmanageUserVM(Long vmId) { return false; } - if (vm.getHypervisorType() != Hypervisor.HypervisorType.VMware) { - throw new UnsupportedServiceException("Unmanaging a VM is currently allowed for VMware VMs only"); + if (!UnmanagedVMsManager.isSupported(vm.getHypervisorType())) { + throw new UnsupportedServiceException("Unmanaging a VM is currently not supported on hypervisor " + + vm.getHypervisorType().toString()); } List volumes = _volsDao.findByInstance(vm.getId()); @@ -8420,4 +8421,11 @@ private void collectVmDiskAndNetworkStatistics(UserVm vm, State expectedState) { public Boolean getDestroyRootVolumeOnVmDestruction(Long domainId){ return DestroyRootVolumeOnVmDestruction.valueIn(domainId); } + + private void setVncPasswordForKvmIfAvailable(Map customParameters, UserVmVO vm){ + if (customParameters.containsKey(VmDetailConstants.KVM_VNC_PASSWORD) + && StringUtils.isNotEmpty(customParameters.get(VmDetailConstants.KVM_VNC_PASSWORD))) { + vm.setVncPassword(customParameters.get(VmDetailConstants.KVM_VNC_PASSWORD)); + } + } } diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index fc49ad386ed2..d883c0345299 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -118,6 +118,7 @@ import com.cloud.vm.VmDetailConstants; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; import com.google.gson.Gson; import org.apache.cloudstack.acl.ControlledEntity; @@ -188,6 +189,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { @Inject private ResourceLimitService resourceLimitService; @Inject + private UserVmDetailsDao userVmDetailsDao; + @Inject private UserVmManager userVmManager; @Inject private ResponseGenerator responseGenerator; @@ -329,6 +332,7 @@ private List getAdditionalNameFilters(Cluster cluster) { if (cluster == null) { return additionalNameFilter; } + if (cluster.getHypervisorType() == Hypervisor.HypervisorType.VMware) { // VMWare considers some templates as VM and they are not filtered by VirtualMachineMO.isTemplate() List templates = templatePoolDao.listAll(); @@ -413,7 +417,7 @@ private boolean storagePoolSupportsDiskOffering(StoragePool pool, DiskOffering d return volumeApiService.doesTargetStorageSupportDiskOffering(pool, diskOffering.getTags()); } - private ServiceOfferingVO getUnmanagedInstanceServiceOffering(final UnmanagedInstanceTO instance, ServiceOfferingVO serviceOffering, final Account owner, final DataCenter zone, final Map details) + private ServiceOfferingVO getUnmanagedInstanceServiceOffering(final UnmanagedInstanceTO instance, ServiceOfferingVO serviceOffering, final Account owner, final DataCenter zone, final Map details, Hypervisor.HypervisorType hypervisorType) throws ServerApiException, PermissionDeniedException, ResourceAllocationException { if (instance == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Cannot find VM to import."); @@ -457,7 +461,7 @@ private ServiceOfferingVO getUnmanagedInstanceServiceOffering(final UnmanagedIns if (!memory.equals(serviceOffering.getRamSize()) && !instance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Service offering (%s) %dMB memory does not match VM memory %dMB and VM is not in powered off state (Power state: %s)", serviceOffering.getUuid(), serviceOffering.getRamSize(), memory, instance.getPowerState())); } - if (cpuSpeed != null && cpuSpeed > 0 && !cpuSpeed.equals(serviceOffering.getSpeed()) && !instance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) { + if (hypervisorType == Hypervisor.HypervisorType.VMware && cpuSpeed != null && cpuSpeed > 0 && !cpuSpeed.equals(serviceOffering.getSpeed()) && !instance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Service offering (%s) %dMHz CPU speed does not match VM CPU speed %dMHz and VM is not in powered off state (Power state: %s)", serviceOffering.getUuid(), serviceOffering.getSpeed(), cpuSpeed, instance.getPowerState())); } } @@ -523,13 +527,15 @@ private StoragePool getStoragePool(final UnmanagedInstanceTO.Disk disk, final Da return storagePool; } - private Pair> getRootAndDataDisks(List disks, final Map dataDiskOfferingMap) { + private Pair> getRootAndDataDisks( + List disks, + final Map dataDiskOfferingMap) { UnmanagedInstanceTO.Disk rootDisk = null; List dataDisks = new ArrayList<>(); Set callerDiskIds = dataDiskOfferingMap.keySet(); if (callerDiskIds.size() != disks.size() - 1) { - String msg = String.format("VM has total %d disks for which %d disk offering mappings provided. %d disks need a disk offering for import", disks.size(), callerDiskIds.size(), disks.size()-1); + String msg = String.format("VM has total %d disks for which %d disk offering mappings provided. %d disks need a disk offering for import", disks.size(), callerDiskIds.size(), disks.size() - 1); LOGGER.error(String.format("%s. %s parameter can be used to provide disk offerings for the disks", msg, ApiConstants.DATADISK_OFFERING_LIST)); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); } @@ -541,11 +547,16 @@ private Pair> getRootAn rootDisk = disk; } else { dataDisks.add(disk); + DiskOffering diskOffering = diskOfferingDao.findById(dataDiskOfferingMap.getOrDefault(disk.getDiskId(), null)); + if ((disk.getCapacity() == null || disk.getCapacity() <= 0) && diskOffering != null) { + disk.setCapacity(diskOffering.getDiskSize()); + } } } - if (diskIdsWithoutOffering.size() > 1) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM has total %d disks, disk offering mapping not provided for %d disks. Disk IDs that may need a disk offering - %s", disks.size(), diskIdsWithoutOffering.size()-1, String.join(", ", diskIdsWithoutOffering))); + if (diskIdsWithoutOffering.size() > 1 || rootDisk == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM has total %d disks, disk offering mapping not provided for %d disks. Disk IDs that may need a disk offering - %s", disks.size(), diskIdsWithoutOffering.size() - 1, String.join(", ", diskIdsWithoutOffering))); } + return new Pair<>(rootDisk, dataDisks); } @@ -594,7 +605,7 @@ private void checkUnmanagedDiskAndOfferingForImport(String intanceName, List getUnmanagedNicNetworkMap(String instanceName, List nics, final Map callerNicNetworkMap, final Map callerNicIpAddressMap, final DataCenter zone, final String hostName, final Account owner) throws ServerApiException { + private Map getUnmanagedNicNetworkMap(String instanceName, List nics, final Map callerNicNetworkMap, final Map callerNicIpAddressMap, final DataCenter zone, final String hostName, final Account owner, final Hypervisor.HypervisorType hypervisorType) throws ServerApiException { Map nicNetworkMap = new HashMap<>(); String nicAdapter = null; for (UnmanagedInstanceTO.Nic nic : nics) { @@ -675,7 +691,7 @@ private Map getUnmanagedNicNetworkMap(String instanceName, List getUnmanagedNicNetworkMap(String instanceName, List allDetails = new HashMap<>(details); @@ -1040,7 +1055,16 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI if (rootDisk == null || StringUtils.isEmpty(rootDisk.getController())) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed. Unable to retrieve root disk details for VM: %s ", instanceName)); } + if (cluster.getHypervisorType() == Hypervisor.HypervisorType.KVM) { + Long rootDiskOfferingId = validatedServiceOffering.getDiskOfferingId(); + DiskOffering rootDiskOffering = diskOfferingDao.findById(rootDiskOfferingId); + if ((rootDisk.getCapacity() == null || rootDisk.getCapacity() <= 0) && rootDiskOffering != null) { + rootDisk.setCapacity(rootDiskOffering.getDiskSize()); + } + } allDetails.put(VmDetailConstants.ROOT_DISK_CONTROLLER, rootDisk.getController()); + allDetails.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDisk.getCapacity() / Resource.ResourceType.bytesToGiB)); + try { checkUnmanagedDiskAndOfferingForImport(unmanagedInstance.getName(), rootDisk, null, validatedServiceOffering, owner, zone, cluster, migrateAllowed); if (CollectionUtils.isNotEmpty(dataDisks)) { // Data disk(s) present @@ -1054,14 +1078,20 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI } // Check NICs and supplied networks Map nicIpAddressMap = getNicIpAddresses(unmanagedInstance.getNics(), callerNicIpAddressMap); - Map allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner); + Map allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner, host.getHypervisorType()); if (!CollectionUtils.isEmpty(unmanagedInstance.getNics())) { allDetails.put(VmDetailConstants.NIC_ADAPTER, unmanagedInstance.getNics().get(0).getAdapterType()); } + + if (StringUtils.isNotEmpty(unmanagedInstance.getVncPassword())) { + allDetails.put(VmDetailConstants.KVM_VNC_PASSWORD, unmanagedInstance.getVncPassword()); + } + VirtualMachine.PowerState powerState = VirtualMachine.PowerState.PowerOff; if (unmanagedInstance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOn)) { powerState = VirtualMachine.PowerState.PowerOn; } + try { userVm = userVmManager.importVM(zone, host, template, internalCSName, displayName, owner, null, caller, true, null, owner.getAccountId(), userId, @@ -1072,6 +1102,7 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI LOGGER.error(errorMsg, ice); throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, errorMsg); } + if (userVm == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); } @@ -1113,7 +1144,7 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI for (UnmanagedInstanceTO.Nic nic : unmanagedInstance.getNics()) { Network network = networkDao.findById(allNicNetworkMap.get(nic.getNicId())); Network.IpAddresses ipAddresses = nicIpAddressMap.get(nic.getNicId()); - importNic(nic, userVm, network, ipAddresses, nicIndex, nicIndex==0, forced); + importNic(nic, userVm, network, ipAddresses, nicIndex, nicIndex == 0, forced); nicIndex++; } } catch (Exception e) { @@ -1158,8 +1189,9 @@ private Cluster basicAccessChecks(Long clusterId) { if (cluster == null) { throw new InvalidParameterValueException(String.format("Cluster with ID [%d] cannot be found.", clusterId)); } - if (cluster.getHypervisorType() != Hypervisor.HypervisorType.VMware) { - throw new InvalidParameterValueException(String.format("VM import is currently not supported for hypervisor [%s].", cluster.getHypervisorType().toString())); + + if (!UnmanagedVMsManager.isSupported(cluster.getHypervisorType())) { + throw new InvalidParameterValueException(String.format("VM import is currently not supported for hypervisor: %s", cluster.getHypervisorType().toString())); } return cluster; } @@ -1199,7 +1231,6 @@ public ListResponse listUnmanagedInstances(ListUnmana public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) { Long clusterId = cmd.getClusterId(); Cluster cluster = basicAccessChecks(clusterId); - final DataCenter zone = dataCenterDao.findById(cluster.getDataCenterId()); final String instanceName = cmd.getName(); if (StringUtils.isEmpty(instanceName)) { @@ -1217,6 +1248,7 @@ public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) { VMTemplateVO template; final Long templateId = cmd.getTemplateId(); if (templateId == null) { + template = templateDao.findByName(VM_IMPORT_DEFAULT_TEMPLATE_NAME); if (template == null) { template = createDefaultDummyVmImportTemplate(false); @@ -1293,12 +1325,18 @@ public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) { if (unmanagedInstance == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to retrieve details for unmanaged VM: %s", name)); } + + if (template.getName().equals(VM_IMPORT_DEFAULT_TEMPLATE_NAME) && cluster.getHypervisorType().equals(Hypervisor.HypervisorType.KVM)) { + throw new InvalidParameterValueException("Template is needed and unable to use default template for hypervisor " + host.getHypervisorType().toString()); + } + if (template.getName().equals(VM_IMPORT_DEFAULT_TEMPLATE_NAME)) { String osName = unmanagedInstance.getOperatingSystem(); GuestOS guestOS = null; if (StringUtils.isNotEmpty(osName)) { guestOS = guestOSDao.findOneByDisplayName(osName); } + GuestOSHypervisor guestOSHypervisor = null; if (guestOS != null) { guestOSHypervisor = guestOSHypervisorDao.findByOsIdAndHypervisor(guestOS.getId(), host.getHypervisorType().toString(), host.getHypervisorVersion()); @@ -1312,6 +1350,7 @@ public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) { } throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to retrieve guest OS details for unmanaged VM: %s with OS name: %s, OS ID: %s for hypervisor: %s version: %s. templateid parameter can be used to assign template for VM", name, osName, unmanagedInstance.getOperatingSystemId(), host.getHypervisorType().toString(), host.getHypervisorVersion())); } + template.setGuestOSId(guestOSHypervisor.getGuestOsId()); } userVm = importVirtualMachineInternal(unmanagedInstance, instanceName, zone, cluster, host, @@ -1413,8 +1452,9 @@ public boolean unmanageVMInstance(long vmId) { throw new InvalidParameterValueException("Could not find VM to unmanage, it is either removed or not existing VM"); } else if (vmVO.getState() != VirtualMachine.State.Running && vmVO.getState() != VirtualMachine.State.Stopped) { throw new InvalidParameterValueException("VM with id = " + vmVO.getUuid() + " must be running or stopped to be unmanaged"); - } else if (vmVO.getHypervisorType() != Hypervisor.HypervisorType.VMware) { - throw new UnsupportedServiceException("Unmanage VM is currently allowed for VMware VMs only"); + } else if (!UnmanagedVMsManager.isSupported(vmVO.getHypervisorType())) { + throw new UnsupportedServiceException("Unmanage VM is currently not allowed for hypervisor " + + vmVO.getHypervisorType().toString()); } else if (vmVO.getType() != VirtualMachine.Type.User) { throw new UnsupportedServiceException("Unmanage VM is currently allowed for guest VMs only"); } @@ -1631,7 +1671,7 @@ private UserVm importExternalKvmVirtualMachine(final UnmanagedInstanceTO unmanag // Check NICs and supplied networks Map nicIpAddressMap = getNicIpAddresses(unmanagedInstance.getNics(), callerNicIpAddressMap); - Map allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner); + Map allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner, Hypervisor.HypervisorType.KVM); if (!CollectionUtils.isEmpty(unmanagedInstance.getNics())) { allDetails.put(VmDetailConstants.NIC_ADAPTER, unmanagedInstance.getNics().get(0).getAdapterType()); } @@ -1986,6 +2026,6 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] { UnmanageVMPreserveNic }; + return new ConfigKey[]{UnmanageVMPreserveNic}; } } diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index a179266ec635..a9b6f03ecdd6 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -29,13 +29,13 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.event.UsageEventUtils; +import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; -import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; @@ -44,6 +44,8 @@ import java.util.UUID; import com.cloud.exception.UnsupportedServiceException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; @@ -109,6 +111,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnitRunner; +import static org.mockito.Mockito.mock; @RunWith(MockitoJUnitRunner.class) public class UnmanagedVMsManagerImplTest { @@ -121,6 +124,10 @@ public class UnmanagedVMsManagerImplTest { @Mock private ClusterDao clusterDao; @Mock + private ClusterVO clusterVO; + @Mock + private UserVmVO userVm; + @Mock private ResourceManager resourceManager; @Mock private VMTemplatePoolDao templatePoolDao; @@ -218,8 +225,7 @@ public void setUp() throws Exception { instance.setNics(instanceNics); instance.setPowerState(UnmanagedInstanceTO.PowerState.PowerOn); - ClusterVO clusterVO = new ClusterVO(1L, 1L, "Cluster"); - clusterVO.setHypervisorType(Hypervisor.HypervisorType.VMware.toString()); + clusterVO = new ClusterVO(1L, 1L, "Cluster"); when(clusterDao.findById(Mockito.anyLong())).thenReturn(clusterVO); when(configurationDao.getValue(Mockito.anyString())).thenReturn(null); doNothing().when(resourceLimitService).checkResourceLimit(Mockito.any(Account.class), Mockito.any(Resource.ResourceType.class), Mockito.anyLong()); @@ -257,7 +263,7 @@ public void setUp() throws Exception { when(serviceOfferingDao.findById(Mockito.anyLong())).thenReturn(serviceOffering); DiskOfferingVO diskOfferingVO = Mockito.mock(DiskOfferingVO.class); when(diskOfferingDao.findById(Mockito.anyLong())).thenReturn(diskOfferingVO); - UserVmVO userVm = Mockito.mock(UserVmVO.class); + userVm = Mockito.mock(UserVmVO.class); when(userVm.getAccountId()).thenReturn(1L); when(userVm.getDataCenterId()).thenReturn(1L); when(userVm.getHostName()).thenReturn(instance.getName()); @@ -280,7 +286,6 @@ public void setUp() throws Exception { nullable(String.class), nullable(Hypervisor.HypervisorType.class), nullable(Map.class), nullable(VirtualMachine.PowerState.class), nullable(LinkedHashMap.class))).thenReturn(userVm); NetworkVO networkVO = Mockito.mock(NetworkVO.class); when(networkVO.getGuestType()).thenReturn(Network.GuestType.L2); - when(networkVO.getBroadcastUri()).thenReturn(URI.create(String.format("vlan://%d", instanceNic.getVlan()))); when(networkVO.getDataCenterId()).thenReturn(1L); when(networkDao.findById(Mockito.anyLong())).thenReturn(networkVO); List networks = new ArrayList<>(); @@ -297,7 +302,6 @@ public void setUp() throws Exception { userVmResponse.setInstanceName(instance.getName()); userVmResponses.add(userVmResponse); when(responseGenerator.createUserVmResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.anyString(), Mockito.any(UserVm.class))).thenReturn(userVmResponses); - when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine); when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Running); } @@ -310,6 +314,14 @@ public void tearDown() throws Exception { @Test public void listUnmanagedInstancesTest() { + testListUnmanagedInstancesTest(Hypervisor.HypervisorType.VMware); + testListUnmanagedInstancesTest(Hypervisor.HypervisorType.KVM); + } + + private void testListUnmanagedInstancesTest(Hypervisor.HypervisorType hypervisorType) { + clusterVO.setHypervisorType(hypervisorType.toString()); + when(virtualMachine.getHypervisorType()).thenReturn(hypervisorType); + when(userVm.getHypervisorType()).thenReturn(hypervisorType); ListUnmanagedInstancesCmd cmd = Mockito.mock(ListUnmanagedInstancesCmd.class); unmanagedVMsManager.listUnmanagedInstances(cmd); } @@ -318,14 +330,20 @@ public void listUnmanagedInstancesTest() { public void listUnmanagedInstancesInvalidHypervisorTest() { ListUnmanagedInstancesCmd cmd = Mockito.mock(ListUnmanagedInstancesCmd.class); ClusterVO cluster = new ClusterVO(1, 1, "Cluster"); - cluster.setHypervisorType(Hypervisor.HypervisorType.KVM.toString()); + cluster.setHypervisorType(Hypervisor.HypervisorType.XenServer.toString()); when(clusterDao.findById(Mockito.anyLong())).thenReturn(cluster); unmanagedVMsManager.listUnmanagedInstances(cmd); } @Test(expected = PermissionDeniedException.class) public void listUnmanagedInstancesInvalidCallerTest() { + testListUnmanagedInstancesInvalidCallerTest(Hypervisor.HypervisorType.VMware); + testListUnmanagedInstancesInvalidCallerTest(Hypervisor.HypervisorType.KVM); + } + + private void testListUnmanagedInstancesInvalidCallerTest(Hypervisor.HypervisorType hypervisorType) { CallContext.unregister(); + clusterVO.setHypervisorType(hypervisorType.toString()); AccountVO account = new AccountVO("user", 1L, "", Account.Type.NORMAL, "uuid"); UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); CallContext.register(user, account); @@ -334,7 +352,15 @@ public void listUnmanagedInstancesInvalidCallerTest() { } @Test - public void importUnmanagedInstanceTest() { + public void importUnmanagedInstanceTest() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { + testImportUnmanagedInstanceTest(Hypervisor.HypervisorType.VMware); + testImportUnmanagedInstanceTest(Hypervisor.HypervisorType.KVM); + } + + private void testImportUnmanagedInstanceTest(Hypervisor.HypervisorType hypervisorType) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { + clusterVO.setHypervisorType(hypervisorType.toString()); + when(virtualMachine.getHypervisorType()).thenReturn(hypervisorType); + when(userVm.getHypervisorType()).thenReturn(hypervisorType); ImportUnmanagedInstanceCmd importUnmanageInstanceCmd = Mockito.mock(ImportUnmanagedInstanceCmd.class); when(importUnmanageInstanceCmd.getName()).thenReturn("TestInstance"); when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null); @@ -344,7 +370,13 @@ public void importUnmanagedInstanceTest() { } @Test(expected = InvalidParameterValueException.class) - public void importUnmanagedInstanceInvalidHostnameTest() { + public void importUnmanagedInstanceInvalidHostnameTest() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { + testImportUnmanagedInstanceInvalidHostnameTest(Hypervisor.HypervisorType.VMware); + testImportUnmanagedInstanceInvalidHostnameTest(Hypervisor.HypervisorType.KVM); + } + + private void testImportUnmanagedInstanceInvalidHostnameTest(Hypervisor.HypervisorType hypervisorType) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { + clusterVO.setHypervisorType(hypervisorType.toString()); ImportUnmanagedInstanceCmd importUnmanageInstanceCmd = Mockito.mock(ImportUnmanagedInstanceCmd.class); when(importUnmanageInstanceCmd.getName()).thenReturn("TestInstance"); when(importUnmanageInstanceCmd.getName()).thenReturn("some name"); @@ -352,7 +384,13 @@ public void importUnmanagedInstanceInvalidHostnameTest() { } @Test(expected = ServerApiException.class) - public void importUnmanagedInstanceMissingInstanceTest() { + public void importUnmanagedInstanceMissingInstanceTest() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { + testImportUnmanagedInstanceMissingInstanceTest(Hypervisor.HypervisorType.VMware); + testImportUnmanagedInstanceMissingInstanceTest(Hypervisor.HypervisorType.KVM); + } + + private void testImportUnmanagedInstanceMissingInstanceTest(Hypervisor.HypervisorType hypervisorType) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { + clusterVO.setHypervisorType(hypervisorType.toString()); ImportUnmanagedInstanceCmd importUnmanageInstanceCmd = Mockito.mock(ImportUnmanagedInstanceCmd.class); when(importUnmanageInstanceCmd.getName()).thenReturn("SomeInstance"); when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null); @@ -361,34 +399,75 @@ public void importUnmanagedInstanceMissingInstanceTest() { @Test(expected = InvalidParameterValueException.class) public void unmanageVMInstanceMissingInstanceTest() { + testUnmanageVMInstanceMissingInstanceTest(Hypervisor.HypervisorType.VMware); + testUnmanageVMInstanceMissingInstanceTest(Hypervisor.HypervisorType.KVM); + + } + + private void testUnmanageVMInstanceMissingInstanceTest(Hypervisor.HypervisorType hypervisorType) { + clusterVO.setHypervisorType(hypervisorType.toString()); long notExistingId = 10L; unmanagedVMsManager.unmanageVMInstance(notExistingId); } @Test(expected = InvalidParameterValueException.class) public void unmanageVMInstanceDestroyedInstanceTest() { + testUnmanageVMInstanceDestroyedInstanceTest(Hypervisor.HypervisorType.VMware); + testUnmanageVMInstanceDestroyedInstanceTest(Hypervisor.HypervisorType.KVM); + } + + private void testUnmanageVMInstanceDestroyedInstanceTest(Hypervisor.HypervisorType hypervisorType){ + clusterVO.setHypervisorType(hypervisorType.toString()); when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Destroyed); unmanagedVMsManager.unmanageVMInstance(virtualMachineId); } @Test(expected = InvalidParameterValueException.class) public void unmanageVMInstanceExpungedInstanceTest() { + testUnmanageVMInstanceExpungedInstanceTest(Hypervisor.HypervisorType.VMware); + testUnmanageVMInstanceExpungedInstanceTest(Hypervisor.HypervisorType.KVM); + } + + private void testUnmanageVMInstanceExpungedInstanceTest(Hypervisor.HypervisorType hypervisorType){ + clusterVO.setHypervisorType(hypervisorType.toString()); when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Expunging); unmanagedVMsManager.unmanageVMInstance(virtualMachineId); } @Test(expected = UnsupportedServiceException.class) public void unmanageVMInstanceExistingVMSnapshotsTest() { + testUnmanageVMInstanceExistingVMSnapshotsTest(Hypervisor.HypervisorType.VMware); + testUnmanageVMInstanceExistingVMSnapshotsTest(Hypervisor.HypervisorType.KVM); + } + + private void testUnmanageVMInstanceExistingVMSnapshotsTest(Hypervisor.HypervisorType hypervisorType) { + clusterVO.setHypervisorType(hypervisorType.toString()); + when(virtualMachine.getHypervisorType()).thenReturn(hypervisorType); unmanagedVMsManager.unmanageVMInstance(virtualMachineId); } @Test(expected = UnsupportedServiceException.class) public void unmanageVMInstanceExistingVolumeSnapshotsTest() { + testUnmanageVMInstanceExistingVolumeSnapshotsTest(Hypervisor.HypervisorType.VMware); + testUnmanageVMInstanceExistingVolumeSnapshotsTest(Hypervisor.HypervisorType.KVM); + } + + private void testUnmanageVMInstanceExistingVolumeSnapshotsTest(Hypervisor.HypervisorType hypervisorType){ + clusterVO.setHypervisorType(hypervisorType.toString()); + when(virtualMachine.getHypervisorType()).thenReturn(hypervisorType); unmanagedVMsManager.unmanageVMInstance(virtualMachineId); } @Test(expected = UnsupportedServiceException.class) public void unmanageVMInstanceExistingISOAttachedTest() { + testUnmanageVMInstanceExistingISOAttachedTest(Hypervisor.HypervisorType.VMware); + testUnmanageVMInstanceExistingISOAttachedTest(Hypervisor.HypervisorType.KVM); + } + + private void testUnmanageVMInstanceExistingISOAttachedTest(Hypervisor.HypervisorType hypervisorType) { + clusterVO.setHypervisorType(hypervisorType.toString()); + when(virtualMachine.getHypervisorType()).thenReturn(hypervisorType); + UserVmVO userVmVO = mock(UserVmVO.class); unmanagedVMsManager.unmanageVMInstance(virtualMachineId); } } diff --git a/ui/src/views/compute/wizard/MultiNetworkSelection.vue b/ui/src/views/compute/wizard/MultiNetworkSelection.vue index 1bc80822501d..d56a4ecd24d1 100644 --- a/ui/src/views/compute/wizard/MultiNetworkSelection.vue +++ b/ui/src/views/compute/wizard/MultiNetworkSelection.vue @@ -101,6 +101,10 @@ export default { filterMatchKey: { type: String, default: null + }, + hypervisor: { + type: String, + default: null } }, data () { @@ -203,7 +207,10 @@ export default { }, setIpAddressEnabled (nic, network) { this.ipAddressesEnabled[nic.id] = network && network.type !== 'L2' - this.indexNum = (this.indexNum % 2) + 1 + this.values[nic.id] = network ? network.id : '' + if (!this.ipAddresses[nic.id] && this.ipAddressesEnabled[nic.id]) { + this.ipAddresses[nic.id] = 'auto' + } }, setIpAddress (nicId, autoAssign, ipAddress) { this.ipAddresses[nicId] = autoAssign ? 'auto' : ipAddress diff --git a/ui/src/views/tools/ImportUnmanagedInstance.vue b/ui/src/views/tools/ImportUnmanagedInstance.vue index 0e24cf253131..f3499aa885be 100644 --- a/ui/src/views/tools/ImportUnmanagedInstance.vue +++ b/ui/src/views/tools/ImportUnmanagedInstance.vue @@ -120,7 +120,7 @@ :value="templateType" @change="changeTemplateType"> - + {{ $t('label.template.temporary.import') }} @@ -269,8 +269,8 @@ :zoneId="cluster.zoneid" :selectionEnabled="false" :filterUnimplementedNetworks="true" - filterMatchKey="broadcasturi" :hypervisor="this.cluster.hypervisortype" + filterMatchKey="broadcasturi" @select-multi-network="updateMultiNetworkOffering" /> @@ -438,7 +438,7 @@ export default { selectedDomainId: null, templates: [], templateLoading: false, - templateType: 'auto', + templateType: this.defaultTemplateType(), totalComputeOfferings: 0, computeOfferings: [], computeOfferingLoading: false, @@ -802,6 +802,12 @@ export default { updateMultiNetworkOffering (data) { this.nicsNetworksMapping = data }, + defaultTemplateType () { + if (this.cluster.hypervisortype === 'VMWare') { + return 'auto' + } + return 'custom' + }, changeTemplateType (e) { this.templateType = e.target.value if (this.templateType === 'auto') { @@ -1109,7 +1115,7 @@ export default { for (var field of fields) { this.updateFieldValue(field, undefined) } - this.templateType = 'auto' + this.templateType = this.defaultTemplateType() this.updateComputeOffering(undefined) this.switches = {} }, diff --git a/ui/src/views/tools/ManageInstances.vue b/ui/src/views/tools/ManageInstances.vue index ff291300eee3..3faa4de6b23e 100644 --- a/ui/src/views/tools/ManageInstances.vue +++ b/ui/src/views/tools/ManageInstances.vue @@ -418,6 +418,7 @@
@@ -452,16 +453,17 @@ :importsource="selectedSourceAction" :zoneid="this.zoneId" :hypervisor="this.destinationHypervisor" - :exthost="this.values.hostname" - :username="this.values.username" - :password="this.values.password" - :tmppath="this.values.tmppath" - :diskpath="this.values.diskpath" + :exthost="this.values?.hostname || ''" + :username="this.values?.username || ''" + :password="this.values?.password || ''" + :tmppath="this.values?.tmppath || ''" + :diskpath="this.values?.diskpath || ''" :isOpen="showUnmanageForm" :selectedVmwareVcenter="selectedVmwareVcenter" @refresh-data="fetchInstances" @close-action="closeImportUnmanagedInstanceForm" @loading-changed="updateManageInstanceActionLoading" + @track-import-jobid="trackImportJobId" />
@@ -497,7 +499,8 @@ export default { name: 'unmanaged', label: 'Manage/Unmanage existing instances', sourceDestHypervisors: { - vmware: 'vmware' + vmware: 'vmware', + kvm: 'kvm' }, wizardTitle: this.$t('label.desc.importexportinstancewizard'), wizardDescription: this.$t('message.desc.importexportinstancewizard') @@ -1218,6 +1221,9 @@ export default { }, updateManageInstanceActionLoading (value) { this.importUnmanagedInstanceLoading = value + if (!value) { + this.fetchInstances() + } }, onManageInstanceAction () { this.selectedUnmanagedInstance = {} @@ -1284,6 +1290,21 @@ export default { } }) }, + trackImportJobId (details) { + const jobId = details[0] + const name = details[1] + this.$pollJob({ + jobId, + title: this.$t('label.import.instance'), + description: this.$t('label.import.instance'), + loadingMessage: `${this.$t('label.import.instance')} ${name} ${this.$t('label.in.progress')}`, + catchMessage: this.$t('error.fetching.async.job.result'), + successMessage: this.$t('message.success.import.instance') + ' ' + name, + successMethod: (result) => { + this.fetchInstances() + } + }) + }, unmanageInstances () { for (var index of this.managedInstancesSelectedRowKeys) { const vm = this.managedInstances[index]