Skip to content

Commit ad0ab57

Browse files
[WIP] Add command wrappers for supporting manage-unmanage instances in KVM
1 parent 5b9a989 commit ad0ab57

7 files changed

Lines changed: 225 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
@@ -37,6 +37,8 @@
3737
import org.xml.sax.SAXException;
3838

3939
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
40+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.CpuModeDef;
41+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.CpuTuneDef;
4042
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
4143
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
4244
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.NicModel;
@@ -56,6 +58,10 @@ public class LibvirtDomainXMLParser {
5658
private Integer vncPort;
5759
private String desc;
5860

61+
private CpuTuneDef cpuTuneDef;
62+
63+
private CpuModeDef cpuModeDef;
64+
5965
public boolean parseDomainXML(String domXML) {
6066
DocumentBuilder builder;
6167
try {
@@ -321,6 +327,55 @@ public boolean parseDomainXML(String domXML) {
321327
watchDogDefs.add(def);
322328
}
323329

330+
NodeList cpuTunesList = rootElement.getElementsByTagName("cputune");
331+
if (cpuTunesList.getLength() > 0) {
332+
cpuTuneDef = new CpuTuneDef();
333+
final Element cpuTuneDefElement = (Element) cpuTunesList.item(0);
334+
final String cpuShares = cpuTuneDefElement.getAttribute("shares");
335+
if (StringUtils.isNotBlank(cpuShares)) {
336+
cpuTuneDef.setShares((Integer.parseInt(cpuShares)));
337+
}
338+
339+
final String quota = cpuTuneDefElement.getAttribute("quota");
340+
if (StringUtils.isNotBlank(quota)) {
341+
cpuTuneDef.setQuota((Integer.parseInt(quota)));
342+
}
343+
344+
final String period = cpuTuneDefElement.getAttribute("period");
345+
if (StringUtils.isNotBlank(period)) {
346+
cpuTuneDef.setQuota((Integer.parseInt(period)));
347+
}
348+
}
349+
350+
NodeList cpuModeList = rootElement.getElementsByTagName("cpu");
351+
if (cpuModeList.getLength() > 0){
352+
cpuModeDef = new CpuModeDef();
353+
final Element cpuModeDefElement = (Element) cpuModeList.item(0);
354+
final String cpuModel = cpuModeDefElement.getAttribute("model");
355+
if (StringUtils.isNotBlank(cpuModel)){
356+
cpuModeDef.setModel(cpuModel);
357+
}
358+
NodeList cpuFeatures = cpuModeDefElement.getElementsByTagName("features");
359+
if (cpuFeatures.getLength() > 0) {
360+
final ArrayList<String> features = new ArrayList<>(cpuFeatures.getLength());
361+
for (int i = 0; i < cpuFeatures.getLength(); i++) {
362+
final Element feature = (Element)cpuFeatures.item(i);
363+
final String policy = feature.getAttribute("policy");
364+
String featureName = feature.getAttribute("name");
365+
if ("disable".equals(policy)) {
366+
featureName = "-" + featureName;
367+
}
368+
features.add(featureName);
369+
}
370+
cpuModeDef.setFeatures(features);
371+
}
372+
final String sockets = getAttrValue("topology", "sockets", cpuModeDefElement);
373+
final String cores = getAttrValue("topology", "cores", cpuModeDefElement);
374+
if (StringUtils.isNotBlank(sockets) && StringUtils.isNotBlank(cores)) {
375+
cpuModeDef.setTopology(Integer.parseInt(cores), Integer.parseInt(sockets));
376+
}
377+
}
378+
324379
return true;
325380
} catch (ParserConfigurationException e) {
326381
s_logger.debug(e.toString());
@@ -381,4 +436,12 @@ public List<WatchDogDef> getWatchDogs() {
381436
public String getDescription() {
382437
return desc;
383438
}
439+
440+
public CpuTuneDef getCpuTuneDef() {
441+
return cpuTuneDef;
442+
}
443+
444+
public CpuModeDef getCpuModeDef() {
445+
return cpuModeDef;
446+
}
384447
}

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
@@ -576,7 +576,6 @@ public String toString() {
576576
enum DiskType {
577577
FILE("file"), BLOCK("block"), DIRECTROY("dir"), NETWORK("network");
578578
String _diskType;
579-
580579
DiskType(String type) {
581580
_diskType = type;
582581
}
@@ -1584,6 +1583,10 @@ public String toString() {
15841583
modeBuilder.append("</cpu>");
15851584
return modeBuilder.toString();
15861585
}
1586+
1587+
public int getCoresPerSocket() {
1588+
return _coresPerSocket;
1589+
}
15871590
}
15881591

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

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

72907291
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
@@ -7940,8 +7940,8 @@ public boolean unmanageUserVM(Long vmId) {
79407940
return false;
79417941
}
79427942

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

79477947
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)