Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
d879013
[WIP] Add command wrappers for supporting manage-unmanage instances i…
itsayushpandey Jul 2, 2023
f02ea08
Fix VNC console for imported VM
itsayushpandey Aug 28, 2023
f7232e2
Add changes for UI network selection in KVM case
itsayushpandey Aug 28, 2023
20c602f
Add back deleted file
itsayushpandey Aug 28, 2023
77e7c13
Add back changes in json file
itsayushpandey Aug 28, 2023
7a36d86
fix breaking tests and minor cleanups
itsayushpandey Aug 29, 2023
cabc789
fix lint error in JS
itsayushpandey Aug 29, 2023
9656336
CR Comments
itsayushpandey Aug 30, 2023
df29386
Skip VLan checks for KVM during import
itsayushpandey Aug 31, 2023
56623e3
Bug fixes and CR
itsayushpandey Sep 3, 2023
eec5d4d
Fix build failure
itsayushpandey Sep 3, 2023
bf0b00a
Readding hypervisor in MultiNetworkSelection
itsayushpandey Sep 15, 2023
300ebd8
Add CR Comment suggestions
itsayushpandey Sep 29, 2023
087522c
Fix package string
itsayushpandey Sep 29, 2023
86a1684
Skip VLan checks for KVM during import
itsayushpandey Sep 29, 2023
b424d16
Fix build failure
itsayushpandey Sep 29, 2023
6aeb08f
hostname check for KVM
itsayushpandey Sep 29, 2023
ba7c8b5
Added VM checks for KVM Hypervisor
itsayushpandey Oct 4, 2023
4a13471
Merge branch 'main' into ayushpandey/gsoc2023
itsayushpandey Oct 19, 2023
54e572d
KVM Ingestion: Initial commit
kishankavala Aug 30, 2023
e79f008
Add CopyRemoteVolume command
kishankavala Aug 31, 2023
3e90409
Run qemu-img remotely and scp file
kishankavala Sep 1, 2023
be8733d
Fix copy volume
kishankavala Sep 2, 2023
f4a3c46
UI changes to include multiple KVM migration options
kishankavala Sep 14, 2023
ea6698b
Local and Shared disk import
kishankavala Sep 18, 2023
8962b40
Cleanup Import Remote Instance
kishankavala Sep 19, 2023
85bc10a
Make IportSource enum case insensitive
kishankavala Sep 19, 2023
82d065a
Fix unit test and lint errors
kishankavala Sep 19, 2023
e8cf5db
UI changes for local and shared storage import
kishankavala Sep 23, 2023
46ad0c2
Fix build after rebasing with main
harikrishna-patnala Oct 10, 2023
ff6b49b
fix external instance import
kishankavala Oct 24, 2023
fe17abf
fix unit test failure
kishankavala Oct 24, 2023
cca5b8d
fix UI build error
kishankavala Oct 24, 2023
9a947a8
handle SPICEVMC in domain xml of external instances
kishankavala Oct 26, 2023
144d1bd
fix destination path for shared storage
kishankavala Oct 27, 2023
8d97a22
remove unexpected character
kishankavala Oct 27, 2023
62f305c
Exclude CPU speed check for KVM
nvazquez Oct 30, 2023
d6b87df
fix internal vm name and display name
kishankavala Oct 30, 2023
5854c82
merge UI changes with import from Vmware feature
kishankavala Nov 1, 2023
45da23d
Added check for storage pool. Change ext host url param to host
kishankavala Nov 2, 2023
ce36976
filter storage pools by cluster scope
kishankavala Nov 3, 2023
3678d2b
Add network selection to import from disk
kishankavala Nov 3, 2023
1fc0bda
change kvm import template name
kishankavala Nov 3, 2023
458164c
remove unused columsn while listing external instances
kishankavala Nov 3, 2023
5a96d3e
fix multi disk import
kishankavala Nov 3, 2023
e08fedd
Merge branch 'kvm_ingestion' into gsoc23-to-kvm-ingestion
nvazquez Nov 6, 2023
ebf638c
Merge branch 'kvm_ingestion' into gsoc23-to-kvm-ingestion
nvazquez Nov 6, 2023
88b413f
Fix UI conflict
nvazquez Nov 6, 2023
f12244a
Merge branch 'kvm_ingestion' into gsoc23-to-kvm-ingestion
nvazquez Nov 7, 2023
2a9ac59
Add missing kvm unmanaged instance
nvazquez Nov 7, 2023
aaf9623
Remove new lines and lines diff changes
nvazquez Nov 7, 2023
346d28e
Restore network selection changes
nvazquez Nov 7, 2023
8e71c63
Merge branch 'kvm_ingestion' into gsoc23-to-kvm-ingestion
nvazquez Nov 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/src/main/java/com/cloud/vm/VmDetailConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Boolean> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<GetUnmanagedInstancesCommand, GetUnmanagedInstancesAnswer, LibvirtComputingResource> {
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<String, UnmanagedInstanceTO> unmanagedInstances = new HashMap<>();
try {
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
final Connect conn = libvirtUtilitiesHelper.getConnection();
final List<Domain> 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<Domain> getDomains(GetUnmanagedInstancesCommand command,
LibvirtComputingResource libvirtComputingResource,
Connect conn) throws LibvirtException, CloudRuntimeException {
final List<Domain> 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<String> 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<UnmanagedInstanceTO.Nic> getUnmanagedInstanceNics(List<LibvirtVMDef.InterfaceDef> interfaces) {
final ArrayList<UnmanagedInstanceTO.Nic> 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<UnmanagedInstanceTO.Disk> getUnmanagedInstanceDisks(List<LibvirtVMDef.DiskDef> disksInfo, LibvirtComputingResource libvirtComputingResource){
final ArrayList<UnmanagedInstanceTO.Disk> 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<String, String> 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<String, String> 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<String, String> getSourceHostPath(LibvirtComputingResource libvirtComputingResource, String diskPath) {
int pathEnd = diskPath.lastIndexOf("/");
if (pathEnd >= 0) {
diskPath = diskPath.substring(0, pathEnd);
return libvirtComputingResource.getSourceHostPath(diskPath);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -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<PrepareUnmanageVMInstanceCommand, PrepareUnmanageVMInstanceAnswer, LibvirtComputingResource> {
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");
}
}
Loading