Skip to content

Commit 8f8b214

Browse files
[WIP] Add command wrappers for supporting manage-unmanage instances in KVM
1 parent c6237c4 commit 8f8b214

7 files changed

Lines changed: 229 additions & 10 deletions

File tree

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import org.xml.sax.SAXException;
3939

4040
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
41+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.CpuModeDef;
42+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.CpuTuneDef;
4143
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
4244
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
4345
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.NicModel;
@@ -59,6 +61,10 @@ public class LibvirtDomainXMLParser {
5961
private Integer vncPort;
6062
private String desc;
6163

64+
private CpuTuneDef cpuTuneDef;
65+
66+
private CpuModeDef cpuModeDef;
67+
6268
public boolean parseDomainXML(String domXML) {
6369
DocumentBuilder builder;
6470
try {
@@ -344,6 +350,55 @@ public boolean parseDomainXML(String domXML) {
344350
watchDogDefs.add(def);
345351
}
346352

353+
NodeList cpuTunesList = rootElement.getElementsByTagName("cputune");
354+
if (cpuTunesList.getLength() > 0) {
355+
cpuTuneDef = new CpuTuneDef();
356+
final Element cpuTuneDefElement = (Element) cpuTunesList.item(0);
357+
final String cpuShares = cpuTuneDefElement.getAttribute("shares");
358+
if (StringUtils.isNotBlank(cpuShares)) {
359+
cpuTuneDef.setShares((Integer.parseInt(cpuShares)));
360+
}
361+
362+
final String quota = cpuTuneDefElement.getAttribute("quota");
363+
if (StringUtils.isNotBlank(quota)) {
364+
cpuTuneDef.setQuota((Integer.parseInt(quota)));
365+
}
366+
367+
final String period = cpuTuneDefElement.getAttribute("period");
368+
if (StringUtils.isNotBlank(period)) {
369+
cpuTuneDef.setQuota((Integer.parseInt(period)));
370+
}
371+
}
372+
373+
NodeList cpuModeList = rootElement.getElementsByTagName("cpu");
374+
if (cpuModeList.getLength() > 0){
375+
cpuModeDef = new CpuModeDef();
376+
final Element cpuModeDefElement = (Element) cpuModeList.item(0);
377+
final String cpuModel = cpuModeDefElement.getAttribute("model");
378+
if (StringUtils.isNotBlank(cpuModel)){
379+
cpuModeDef.setModel(cpuModel);
380+
}
381+
NodeList cpuFeatures = cpuModeDefElement.getElementsByTagName("features");
382+
if (cpuFeatures.getLength() > 0) {
383+
final ArrayList<String> features = new ArrayList<>(cpuFeatures.getLength());
384+
for (int i = 0; i < cpuFeatures.getLength(); i++) {
385+
final Element feature = (Element)cpuFeatures.item(i);
386+
final String policy = feature.getAttribute("policy");
387+
String featureName = feature.getAttribute("name");
388+
if ("disable".equals(policy)) {
389+
featureName = "-" + featureName;
390+
}
391+
features.add(featureName);
392+
}
393+
cpuModeDef.setFeatures(features);
394+
}
395+
final String sockets = getAttrValue("topology", "sockets", cpuModeDefElement);
396+
final String cores = getAttrValue("topology", "cores", cpuModeDefElement);
397+
if (StringUtils.isNotBlank(sockets) && StringUtils.isNotBlank(cores)) {
398+
cpuModeDef.setTopology(Integer.parseInt(cores), Integer.parseInt(sockets));
399+
}
400+
}
401+
347402
return true;
348403
} catch (ParserConfigurationException e) {
349404
s_logger.debug(e.toString());
@@ -427,4 +482,12 @@ public List<WatchDogDef> getWatchDogs() {
427482
public String getDescription() {
428483
return desc;
429484
}
485+
486+
public CpuTuneDef getCpuTuneDef() {
487+
return cpuTuneDef;
488+
}
489+
490+
public CpuModeDef getCpuModeDef() {
491+
return cpuModeDef;
492+
}
430493
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,6 @@ public String toString() {
612612
enum DiskType {
613613
FILE("file"), BLOCK("block"), DIRECTROY("dir"), NETWORK("network");
614614
String _diskType;
615-
616615
DiskType(String type) {
617616
_diskType = type;
618617
}
@@ -1737,6 +1736,10 @@ public String toString() {
17371736
modeBuilder.append("</cpu>");
17381737
return modeBuilder.toString();
17391738
}
1739+
1740+
public int getCoresPerSocket() {
1741+
return _coresPerSocket;
1742+
}
17401743
}
17411744

17421745
public static class SerialDef {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.cloud.hypervisor.kvm.resource.wrapper;
2+
3+
import com.cloud.agent.api.GetUnmanagedInstancesAnswer;
4+
import com.cloud.agent.api.GetUnmanagedInstancesCommand;
5+
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
6+
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
7+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
8+
import com.cloud.resource.CommandWrapper;
9+
import com.cloud.resource.ResourceWrapper;
10+
import com.cloud.utils.exception.CloudRuntimeException;
11+
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
12+
import org.apache.commons.lang3.StringUtils;
13+
import org.apache.log4j.Logger;
14+
import org.libvirt.Connect;
15+
import org.libvirt.Domain;
16+
import org.libvirt.SchedParameter;
17+
18+
import java.util.HashMap;
19+
import java.util.List;
20+
21+
@ResourceWrapper(handles= GetUnmanagedInstancesCommand.class)
22+
public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWrapper<GetUnmanagedInstancesCommand, GetUnmanagedInstancesAnswer, LibvirtComputingResource> {
23+
private static final Logger s_logger = Logger.getLogger(LibvirtPrepareUnmanageVMInstanceCommandWrapper.class);
24+
25+
26+
@Override
27+
public GetUnmanagedInstancesAnswer execute(GetUnmanagedInstancesCommand command, LibvirtComputingResource libvirtComputingResource) {
28+
s_logger.info("Need to implement business logic");
29+
30+
HashMap<String, UnmanagedInstanceTO> unmanagedInstances = new HashMap<>();
31+
try {
32+
final String vmName = command.getInstanceName();
33+
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
34+
final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);
35+
final Domain domain = libvirtComputingResource.getDomain(conn, vmName);
36+
37+
// TODO: Ayush: create UnmanagedInstanceTO from domain
38+
// Need to ask if domain can be template or not like in VMWare
39+
40+
if (domain == null) {
41+
s_logger.error("GetUnmanagedInstancesCommand: vm not found " + vmName);
42+
throw new CloudRuntimeException("GetUnmanagedInstancesCommand: vm not found " + vmName);
43+
}
44+
45+
// Filter managed instances
46+
if (command.hasManagedInstance(domain.getName())) {
47+
s_logger.error("GetUnmanagedInstancesCommand: vm already managed " + vmName);
48+
throw new CloudRuntimeException("GetUnmanagedInstancesCommand: vm already managed " + vmName);
49+
}
50+
51+
// Filter instance if answer is requested for a particular instance name
52+
if (StringUtils.isNotEmpty(command.getInstanceName()) &&
53+
!command.getInstanceName().equals(domain.getName())) {
54+
s_logger.error("GetUnmanagedInstancesCommand: exact vm name not found " + vmName);
55+
throw new CloudRuntimeException("GetUnmanagedInstancesCommand: exact vm name not found " + vmName);
56+
}
57+
UnmanagedInstanceTO instance = getUnmanagedInstance(libvirtComputingResource, domain);
58+
unmanagedInstances.put(instance.getName(), instance);
59+
} catch (Exception e) {
60+
s_logger.error("GetUnmanagedInstancesCommand failed due to " + e.getMessage());
61+
throw new CloudRuntimeException("GetUnmanagedInstancesCommand failed due to " + e.getMessage());
62+
}
63+
return new GetUnmanagedInstancesAnswer(command, "True", unmanagedInstances);
64+
}
65+
66+
private UnmanagedInstanceTO getUnmanagedInstance(LibvirtComputingResource libvirtComputingResource, Domain domain) {
67+
try {
68+
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
69+
parser.parseDomainXML(domain.getXMLDesc(0));
70+
71+
final UnmanagedInstanceTO instance = new UnmanagedInstanceTO();
72+
instance.setName(domain.getName());
73+
instance.setCpuCores((int) LibvirtComputingResource.countDomainRunningVcpus(domain));
74+
instance.setCpuCoresPerSocket(parser.getCpuModeDef().getCoresPerSocket());
75+
instance.setCpuSpeed(parser.getCpuTuneDef().getShares());
76+
instance.setMemory((int) LibvirtComputingResource.getDomainMemory(domain));
77+
78+
// TODO: Ayush complete this function.
79+
// instance.setOperatingSystemId(domain.getVmGuestInfo().getGuestId());
80+
// if (StringUtils.isEmpty(instance.getOperatingSystemId())) {
81+
// instance.setOperatingsSystemId(domain.getConfigSummary().getGuestId());
82+
// instance.setOperatingSystemId(domain.getOSType());
83+
// }
84+
// VirtualMachineGuestOsIdentifier osIdentifier = VirtualMachineGuestOsIdentifier.OTHER_GUEST;
85+
// try {
86+
// osIdentifier = VirtualMachineGuestOsIdentifier.fromValue(instance.getOperatingSystemId());
87+
// } catch (IllegalArgumentException iae) {
88+
// if (StringUtils.isNotEmpty(instance.getOperatingSystemId()) && instance.getOperatingSystemId().contains("64")) {
89+
// osIdentifier = VirtualMachineGuestOsIdentifier.OTHER_GUEST_64;
90+
// }
91+
// }
92+
// instance.setOperatingSystem(domain.getGuestInfo().getGuestFullName());
93+
// if (StringUtils.isEmpty(instance.getOperatingSystem())) {
94+
// instance.setOperatingSystem(domain.getConfigSummary().getGuestFullName());
95+
// }
96+
// UnmanagedInstanceTO.PowerState powerState = UnmanagedInstanceTO.PowerState.PowerUnknown;
97+
// if (domain.getPowerState().toString().equalsIgnoreCase("POWERED_ON")) {
98+
// powerState = UnmanagedInstanceTO.PowerState.PowerOn;
99+
// }
100+
// if (domain.getPowerState().toString().equalsIgnoreCase("POWERED_OFF")) {
101+
// powerState = UnmanagedInstanceTO.PowerState.PowerOff;
102+
// }
103+
// instance.setPowerState(powerState);
104+
// instance.setDisks(getUnmanageInstanceDisks(domain));
105+
// instance.setNics(getUnmanageInstanceNics(hyperHost, domain));
106+
return instance;
107+
} catch (Exception e) {
108+
s_logger.info("Unable to retrieve unmanaged instance info. " + e.getMessage());
109+
throw new CloudRuntimeException("Unable to retrieve unmanaged instance info. " + e.getMessage());
110+
}
111+
}
112+
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.cloud.hypervisor.kvm.resource.wrapper;
2+
3+
import com.cloud.agent.api.PrepareUnmanageVMInstanceAnswer;
4+
import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand;
5+
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
6+
import com.cloud.resource.CommandWrapper;
7+
import com.cloud.resource.ResourceWrapper;
8+
import org.apache.log4j.Logger;
9+
import org.libvirt.Connect;
10+
import org.libvirt.Domain;
11+
12+
@ResourceWrapper(handles = PrepareUnmanageVMInstanceCommand.class)
13+
public final class LibvirtPrepareUnmanageVMInstanceCommandWrapper extends CommandWrapper<PrepareUnmanageVMInstanceCommand, PrepareUnmanageVMInstanceAnswer, LibvirtComputingResource> {
14+
private static final Logger s_logger = Logger.getLogger(LibvirtPrepareUnmanageVMInstanceCommandWrapper.class);
15+
@Override
16+
public PrepareUnmanageVMInstanceAnswer execute(PrepareUnmanageVMInstanceCommand command, LibvirtComputingResource libvirtComputingResource) {
17+
final String vmName = command.getInstanceName();
18+
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
19+
s_logger.debug(String.format("Verify if KVM instance: [%s] is available before Unmanaging VM.", vmName));
20+
try {
21+
final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);
22+
final Domain domain = libvirtComputingResource.getDomain(conn, vmName);
23+
if (domain == null) {
24+
s_logger.error("Prepare Unmanage VMInstanceCommand: vm not found " + vmName);
25+
new PrepareUnmanageVMInstanceAnswer(command, false, String.format("Cannot find VM with name [%s] in KVM host.", vmName));
26+
}
27+
} catch (Exception e){
28+
s_logger.error("PrepareUnmanagedInstancesCommand failed due to " + e.getMessage());
29+
return new PrepareUnmanageVMInstanceAnswer(command, false, "Error: " + e.getMessage());
30+
}
31+
32+
return new PrepareUnmanageVMInstanceAnswer(command, true, "OK");
33+
}
34+
}

plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7370,6 +7370,7 @@ private Answer execute(GetUnmanagedInstancesCommand cmd) {
73707370
VmwareHypervisorHost hyperHost = getHyperHost(context);
73717371

73727372
String vmName = cmd.getInstanceName();
7373+
// TODO: Ayush, ask if VMWare can have more than 1 VM for given hypervisor VMName. IS it also possible on KVM
73737374
List<VirtualMachineMO> vmMos = hyperHost.listVmsOnHyperHostWithHypervisorName(vmName);
73747375

73757376
for (VirtualMachineMO vmMo : vmMos) {

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8112,8 +8112,8 @@ public boolean unmanageUserVM(Long vmId) {
81128112
return false;
81138113
}
81148114

8115-
if (vm.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
8116-
throw new UnsupportedServiceException("Unmanaging a VM is currently allowed for VMware VMs only");
8115+
if (vm.getHypervisorType() != Hypervisor.HypervisorType.VMware && vm.getHypervisorType() != Hypervisor.HypervisorType.KVM) {
8116+
throw new UnsupportedServiceException("Unmanaging a VM is currently allowed for VMware and KVM VMs only");
81178117
}
81188118

81198119
List<VolumeVO> volumes = _volsDao.findByInstance(vm.getId());

server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ private List<String> getAdditionalNameFilters(Cluster cluster) {
302302
if (cluster == null) {
303303
return additionalNameFilter;
304304
}
305+
// TODO: Ayush - invesgigate KVM specific changes
305306
if (cluster.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
306307
// VMWare considers some templates as VM and they are not filtered by VirtualMachineMO.isTemplate()
307308
List<VMTemplateStoragePoolVO> templates = templatePoolDao.listAll();
@@ -1049,7 +1050,8 @@ public ListResponse<UnmanagedInstanceResponse> listUnmanagedInstances(ListUnmana
10491050
if (cluster == null) {
10501051
throw new InvalidParameterValueException(String.format("Cluster ID: %d cannot be found", clusterId));
10511052
}
1052-
if (cluster.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
1053+
//TODO: Ayush Need to check
1054+
if (cluster.getHypervisorType() != Hypervisor.HypervisorType.VMware && cluster.getHypervisorType() != Hypervisor.HypervisorType.KVM) {
10531055
throw new InvalidParameterValueException(String.format("VM ingestion is currently not supported for hypervisor: %s", cluster.getHypervisorType().toString()));
10541056
}
10551057
String keyword = cmd.getKeyword();
@@ -1105,7 +1107,8 @@ public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) {
11051107
if (cluster == null) {
11061108
throw new InvalidParameterValueException(String.format("Cluster ID: %d cannot be found", clusterId));
11071109
}
1108-
if (cluster.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
1110+
//TODO: Ayush Need to check here too
1111+
if (cluster.getHypervisorType() != Hypervisor.HypervisorType.VMware && cluster.getHypervisorType() != Hypervisor.HypervisorType.KVM ) {
11091112
throw new InvalidParameterValueException(String.format("VM import is currently not supported for hypervisor: %s", cluster.getHypervisorType().toString()));
11101113
}
11111114
final DataCenter zone = dataCenterDao.findById(cluster.getDataCenterId());
@@ -1168,6 +1171,7 @@ public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) {
11681171
throw new InvalidParameterValueException("Invalid VM hostname. VM hostname can contain ASCII letters 'a' through 'z', the digits '0' through '9', "
11691172
+ "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit");
11701173
}
1174+
//TODO: Ayush Check this too
11711175
if (cluster.getHypervisorType().equals(Hypervisor.HypervisorType.VMware) &&
11721176
Boolean.parseBoolean(configurationDao.getValue(Config.SetVmInternalNameUsingDisplayName.key()))) {
11731177
// If global config vm.instancename.flag is set to true, then CS will set guest VM's name as it appears on the hypervisor, to its hostname.
@@ -1330,8 +1334,8 @@ public boolean unmanageVMInstance(long vmId) {
13301334
throw new InvalidParameterValueException("Could not find VM to unmanage, it is either removed or not existing VM");
13311335
} else if (vmVO.getState() != VirtualMachine.State.Running && vmVO.getState() != VirtualMachine.State.Stopped) {
13321336
throw new InvalidParameterValueException("VM with id = " + vmVO.getUuid() + " must be running or stopped to be unmanaged");
1333-
} else if (vmVO.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
1334-
throw new UnsupportedServiceException("Unmanage VM is currently allowed for VMware VMs only");
1337+
} else if (vmVO.getHypervisorType() != Hypervisor.HypervisorType.VMware && vmVO.getHypervisorType() != Hypervisor.HypervisorType.KVM) {
1338+
throw new UnsupportedServiceException("Unmanage VM is currently allowed for VMware and KVM VMs only");
13351339
} else if (vmVO.getType() != VirtualMachine.Type.User) {
13361340
throw new UnsupportedServiceException("Unmanage VM is currently allowed for guest VMs only");
13371341
}
@@ -1355,9 +1359,10 @@ private boolean existsVMToUnmanage(String instanceName, Long hostId) {
13551359
PrepareUnmanageVMInstanceCommand command = new PrepareUnmanageVMInstanceCommand();
13561360
command.setInstanceName(instanceName);
13571361
Answer ans = agentManager.easySend(hostId, command);
1358-
if (!(ans instanceof PrepareUnmanageVMInstanceAnswer)) {
1359-
throw new CloudRuntimeException("Error communicating with host " + hostId);
1360-
}
1362+
// TODO: Ayush uncomment condition to see changes
1363+
// if (!(ans instanceof PrepareUnmanageVMInstanceAnswer)) {
1364+
// throw new CloudRuntimeException("Error communicating with host " + hostId);
1365+
// }
13611366
PrepareUnmanageVMInstanceAnswer answer = (PrepareUnmanageVMInstanceAnswer) ans;
13621367
if (!answer.getResult()) {
13631368
LOGGER.error("Error verifying VM " + instanceName + " exists on host with ID = " + hostId + ": " + answer.getDetails());

0 commit comments

Comments
 (0)