Skip to content

Commit a16fa51

Browse files
committed
add hostid in unmanageVirtualMachine cmd and response
1 parent 0a693a2 commit a16fa51

File tree

10 files changed

+115
-60
lines changed

10 files changed

+115
-60
lines changed

api/src/main/java/com/cloud/vm/UserVmService.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import com.cloud.template.VirtualMachineTemplate;
6565
import com.cloud.user.Account;
6666
import com.cloud.uservm.UserVm;
67+
import com.cloud.utils.Pair;
6768
import com.cloud.utils.exception.ExecutionException;
6869

6970
public interface UserVmService {
@@ -514,9 +515,10 @@ UserVm importVM(final DataCenter zone, final Host host, final VirtualMachineTemp
514515

515516
/**
516517
* Unmanage a guest VM from CloudStack
517-
* @return true if the VM is successfully unmanaged, false if not.
518+
*
519+
* @return (true if successful, false if not, hostUuid) if the VM is successfully unmanaged.
518520
*/
519-
boolean unmanageUserVM(Long vmId);
521+
Pair<Boolean, String> unmanageUserVM(Long vmId, Long targetHostId);
520522

521523
UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws InsufficientCapacityException, ResourceAllocationException, ResourceUnavailableException;
522524

api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UnmanageVMInstanceCmd.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.cloud.exception.ResourceUnavailableException;
2828
import com.cloud.user.Account;
2929
import com.cloud.uservm.UserVm;
30+
import com.cloud.utils.Pair;
3031
import com.cloud.vm.VirtualMachine;
3132
import org.apache.cloudstack.acl.RoleType;
3233
import org.apache.cloudstack.api.APICommand;
@@ -36,6 +37,7 @@
3637
import org.apache.cloudstack.api.BaseAsyncCmd;
3738
import org.apache.cloudstack.api.Parameter;
3839
import org.apache.cloudstack.api.ServerApiException;
40+
import org.apache.cloudstack.api.response.HostResponse;
3941
import org.apache.cloudstack.api.response.UnmanageVMInstanceResponse;
4042
import org.apache.cloudstack.api.response.UserVmResponse;
4143
import org.apache.cloudstack.context.CallContext;
@@ -65,6 +67,12 @@ public class UnmanageVMInstanceCmd extends BaseAsyncCmd {
6567
description = "The ID of the virtual machine to unmanage")
6668
private Long vmId;
6769

70+
@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID,
71+
entityType = HostResponse.class, required = false,
72+
description = "ID of the host where domain XML is stored for stopped Instance",
73+
since = "4.22.0")
74+
private Long hostId;
75+
6876
/////////////////////////////////////////////////////
6977
/////////////////// Accessors ///////////////////////
7078
/////////////////////////////////////////////////////
@@ -83,6 +91,14 @@ public String getEventDescription() {
8391
return "unmanaging VM. VM ID = " + vmId;
8492
}
8593

94+
public Long getHostId() {
95+
return hostId;
96+
}
97+
98+
public void setHostId(Long hostId) {
99+
this.hostId = hostId;
100+
}
101+
86102
/////////////////////////////////////////////////////
87103
/////////////// API Implementation///////////////////
88104
/////////////////////////////////////////////////////
@@ -93,9 +109,10 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE
93109
UnmanageVMInstanceResponse response = new UnmanageVMInstanceResponse();
94110
try {
95111
CallContext.current().setEventDetails("VM ID = " + vmId);
96-
boolean result = unmanagedVMsManager.unmanageVMInstance(vmId);
97-
response.setSuccess(result);
98-
if (result) {
112+
Pair<Boolean, String> result = unmanagedVMsManager.unmanageVMInstance(vmId, hostId);
113+
if (result.first()) {
114+
response.setSuccess(true);
115+
response.setHostId(result.second());
99116
response.setDetails("VM unmanaged successfully");
100117
}
101118
} catch (Exception e) {
@@ -124,5 +141,4 @@ public ApiCommandResourceType getApiResourceType() {
124141
public Long getApiResourceId() {
125142
return vmId;
126143
}
127-
128144
}

api/src/main/java/org/apache/cloudstack/api/response/UnmanageVMInstanceResponse.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ public class UnmanageVMInstanceResponse extends BaseResponse {
3232
@Param(description = "details of the unmanage VM operation")
3333
private String details;
3434

35+
@SerializedName(ApiConstants.HOST_ID)
36+
@Param(description = "The ID of the host used for unmanaged Instance")
37+
private String hostId;
38+
3539
public UnmanageVMInstanceResponse() {
3640
}
3741

@@ -55,4 +59,12 @@ public String getDetails() {
5559
public void setDetails(String details) {
5660
this.details = details;
5761
}
62+
63+
public String getHostId() {
64+
return hostId;
65+
}
66+
67+
public void setHostId(String hostId) {
68+
this.hostId = hostId;
69+
}
5870
}

api/src/main/java/org/apache/cloudstack/vm/UnmanageVMService.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717

1818
package org.apache.cloudstack.vm;
1919

20+
import com.cloud.utils.Pair;
21+
2022
public interface UnmanageVMService {
2123

2224
/**
2325
* Unmanage a guest VM from CloudStack
24-
* @return true if the VM is successfully unmanaged, false if not.
26+
*
27+
* @return (true if successful, false if not, hostUuid) if the VM is successfully unmanaged.
2528
*/
26-
boolean unmanageVMInstance(long vmId);
29+
Pair<Boolean, String> unmanageVMInstance(long vmId, Long paramHostId);
2730
}

engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ static String getHypervisorHostname(String name) {
274274
* - Remove the references of the VM and its volumes, nics, IPs from database
275275
* - Keep the VM as it is on the hypervisor
276276
*/
277-
boolean unmanage(String vmUuid);
277+
Pair<Boolean, String> unmanage(String vmUuid, Long paramHostId);
278278

279279
UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long rootDiskOfferingId, boolean expunge, Map<String, String> details) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException;
280280

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2004,7 +2004,7 @@ public boolean getExecuteInSequence(final HypervisorType hypervisorType) {
20042004
}
20052005

20062006
@Override
2007-
public boolean unmanage(String vmUuid) {
2007+
public Pair<Boolean, String> unmanage(String vmUuid, Long paramHostId) {
20082008
VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
20092009
if (vm == null || vm.getRemoved() != null) {
20102010
throw new CloudRuntimeException("Could not find VM with id = " + vmUuid);
@@ -2017,8 +2017,9 @@ public boolean unmanage(String vmUuid) {
20172017
throw new ConcurrentOperationException(msg);
20182018
}
20192019

2020+
Long agentHostId = vm.getHostId();
20202021
if (HypervisorType.KVM.equals(vm.getHypervisorType())) {
2021-
persistDomainForKVM(vm);
2022+
agentHostId = persistDomainForKVM(vm, paramHostId);
20222023
}
20232024
Boolean result = Transaction.execute(new TransactionCallback<Boolean>() {
20242025
@Override
@@ -2043,43 +2044,49 @@ public Boolean doInTransaction(TransactionStatus status) {
20432044
return true;
20442045
}
20452046
});
2046-
2047-
return BooleanUtils.isTrue(result);
2047+
HostVO host = ApiDBUtils.findHostById(agentHostId);
2048+
if (host == null) {
2049+
throw new CloudRuntimeException("Unable to retrieve host with ID: " + agentHostId);
2050+
}
2051+
logger.debug("Selected host UUID: {} to unmanage Instance: {}.", host.getUuid(), vm.getName());
2052+
return new Pair<>(result, host.getUuid());
20482053
}
20492054

2050-
void persistDomainForKVM(VMInstanceVO vm) {
2051-
Long hostId = vm.getHostId();
2055+
Long persistDomainForKVM(VMInstanceVO vm, Long paramHostId) {
2056+
Long agentHostId = vm.getHostId();
20522057
String vmName = vm.getName();
20532058
UnmanageInstanceCommand unmanageInstanceCommand;
20542059
if (State.Stopped.equals(vm.getState())) {
2055-
Pair<Long, Long> clusterAndHostId = findClusterAndHostIdForVm(vm, false);
2056-
hostId = clusterAndHostId.second();
2057-
if (hostId == null) {
2058-
String errorMsg = "No available host to persist domain XML for Instance: " + vmName;
2059-
logger.debug(errorMsg);
2060-
throw new CloudRuntimeException(errorMsg);
2060+
if (paramHostId == null) {
2061+
Pair<Long, Long> clusterAndHostId = findClusterAndHostIdForVm(vm, false);
2062+
agentHostId = clusterAndHostId.second();
2063+
if (agentHostId == null) {
2064+
String errorMsg = "No available host to persist domain XML for Instance: " + vmName;
2065+
logger.debug(errorMsg);
2066+
throw new CloudRuntimeException(errorMsg);
2067+
}
2068+
} else {
2069+
agentHostId = paramHostId;
20612070
}
2062-
unmanageInstanceCommand = new UnmanageInstanceCommand(prepVmSpecForUnmanageCmd(vm.getId(), hostId)); // reconstruct vmSpec for stopped instance
2071+
unmanageInstanceCommand = new UnmanageInstanceCommand(prepVmSpecForUnmanageCmd(vm.getId(), agentHostId)); // reconstruct vmSpec for stopped instance
20632072
} else {
20642073
unmanageInstanceCommand = new UnmanageInstanceCommand(vmName);
20652074
}
2066-
logger.debug("Selected host with ID: {} to persist domain XML for Instance: {}.", hostId, vmName);
2075+
2076+
logger.debug("Selected host ID: {} to persist domain XML for Instance: {}.", agentHostId, vmName);
20672077
try {
2068-
Answer answer = _agentMgr.send(hostId, unmanageInstanceCommand);
2078+
Answer answer = _agentMgr.send(agentHostId, unmanageInstanceCommand);
20692079
if (!answer.getResult()) {
2070-
String errorMsg = "Failed to persist domain XML for instance: " + vmName;
2080+
String errorMsg = "Failed to persist domain XML for Instance: " + vmName + " on host ID: " + agentHostId;
20712081
logger.debug(errorMsg);
20722082
throw new CloudRuntimeException(errorMsg);
20732083
}
2074-
} catch (AgentUnavailableException e) {
2075-
String errorMsg = "Failed to send command, agent unavailable";
2076-
logger.error(errorMsg, e);
2077-
throw new CloudRuntimeException(errorMsg);
2078-
} catch (OperationTimedoutException e) {
2079-
String errorMsg = "Failed to send command, operation timed out";
2084+
} catch (AgentUnavailableException | OperationTimedoutException e) {
2085+
String errorMsg = "Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + agentHostId;
20802086
logger.error(errorMsg, e);
20812087
throw new CloudRuntimeException(errorMsg);
20822088
}
2089+
return agentHostId;
20832090
}
20842091

20852092
/**

engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,7 +1755,7 @@ public void testPersistDomainForKvmForRunningVmSuccess() throws AgentUnavailable
17551755
when(vmInstanceMock.getHostId()).thenReturn(hostMockId);
17561756
UnmanageInstanceAnswer successAnswer = new UnmanageInstanceAnswer(null, true, "success");
17571757
when(agentManagerMock.send(anyLong(), any(Command.class))).thenReturn(successAnswer);
1758-
virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock);
1758+
virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null);
17591759
ArgumentCaptor<Long> hostIdCaptor = ArgumentCaptor.forClass(Long.class);
17601760
ArgumentCaptor<UnmanageInstanceCommand> commandCaptor = ArgumentCaptor.forClass(UnmanageInstanceCommand.class);
17611761
verify(agentManagerMock).send(hostIdCaptor.capture(), commandCaptor.capture());
@@ -1771,7 +1771,7 @@ public void testPersistDomainForKvmForStoppedVmSuccess() throws AgentUnavailable
17711771
UnmanageInstanceAnswer successAnswer = new UnmanageInstanceAnswer(null, true, "success");
17721772
when(agentManagerMock.send(anyLong(), any(UnmanageInstanceCommand.class))).thenReturn(successAnswer);
17731773
when(virtualMachineManagerImpl.findClusterAndHostIdForVm(vmInstanceMock, false)).thenReturn(new Pair<>(clusterMockId, hostMockId));
1774-
virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock);
1774+
virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null);
17751775
ArgumentCaptor<Long> hostIdCaptor = ArgumentCaptor.forClass(Long.class);
17761776
ArgumentCaptor<UnmanageInstanceCommand> commandCaptor = ArgumentCaptor.forClass(UnmanageInstanceCommand.class);
17771777
verify(agentManagerMock).send(hostIdCaptor.capture(), commandCaptor.capture());
@@ -1790,7 +1790,7 @@ public void testPersistDomainForKvmForStoppedVmNoHost() {
17901790
VirtualMachineTO vmTO = new VirtualMachineTO() {};
17911791
vmTO.setName(vmName);
17921792
when(virtualMachineManagerImpl.findClusterAndHostIdForVm(vmInstanceMock, false)).thenReturn(new Pair<>(clusterMockId, null));
1793-
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock));
1793+
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null));
17941794
assertEquals("No available host to persist domain XML for Instance: " + vmName, exception.getMessage());
17951795
}
17961796

@@ -1800,25 +1800,25 @@ public void testPersistDomainForKvmForRunningVmAgentFailure() throws AgentUnavai
18001800
when(vmInstanceMock.getHostId()).thenReturn(hostMockId);
18011801
UnmanageInstanceAnswer failureAnswer = new UnmanageInstanceAnswer(null, false, "failure");
18021802
when(agentManagerMock.send(anyLong(), any(UnmanageInstanceCommand.class))).thenReturn(failureAnswer);
1803-
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock));
1804-
assertEquals("Failed to persist domain XML for instance: " + vmName, exception.getMessage());
1803+
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null));
1804+
assertEquals("Failed to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage());
18051805
}
18061806

18071807
@Test
18081808
public void testPersistDomainForKvmAgentUnavailable() throws AgentUnavailableException, OperationTimedoutException {
18091809
when(vmInstanceMock.getState()).thenReturn(VirtualMachine.State.Running);
18101810
when(vmInstanceMock.getHostId()).thenReturn(hostMockId);
18111811
doThrow(new AgentUnavailableException("Agent down", hostMockId)).when(agentManagerMock).send(anyLong(), any(UnmanageInstanceCommand.class));
1812-
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock));
1813-
assertEquals("Failed to send command, agent unavailable", exception.getMessage());
1812+
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null));
1813+
assertEquals("Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage());
18141814
}
18151815

18161816
@Test
18171817
public void testPersistDomainForKvmOperationTimedOut() throws AgentUnavailableException, OperationTimedoutException {
18181818
when(vmInstanceMock.getState()).thenReturn(VirtualMachine.State.Running);
18191819
when(vmInstanceMock.getHostId()).thenReturn(hostMockId);
18201820
doThrow(new OperationTimedoutException(null, hostMockId, 123L, 60, false)).when(agentManagerMock).send(anyLong(), any(UnmanageInstanceCommand.class));
1821-
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock));
1822-
assertEquals("Failed to send command, operation timed out", exception.getMessage());
1821+
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null));
1822+
assertEquals("Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage());
18231823
}
18241824
}

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9399,18 +9399,19 @@ public UserVm importVM(final DataCenter zone, final Host host, final VirtualMach
93999399
}
94009400

94019401
@Override
9402-
public boolean unmanageUserVM(Long vmId) {
9402+
public Pair<Boolean, String> unmanageUserVM(Long vmId, Long paramHostId) {
94039403
UserVmVO vm = _vmDao.findById(vmId);
94049404
if (vm == null || vm.getRemoved() != null) {
94059405
throw new InvalidParameterValueException("Unable to find a VM with ID = " + vmId);
94069406
}
94079407

94089408
vm = _vmDao.acquireInLockTable(vm.getId());
9409-
boolean result;
9409+
94109410
try {
94119411
if (vm.getState() != State.Running && vm.getState() != State.Stopped) {
9412-
logger.debug("VM {} is not running or stopped, cannot be unmanaged", vm);
9413-
return false;
9412+
String errorMsg = "Instance: " + vm.getName() + " is not running or stopped, cannot be unmanaged";
9413+
logger.debug(errorMsg);
9414+
throw new CloudRuntimeException(errorMsg);
94149415
}
94159416

94169417
if (!UnmanagedVMsManager.isSupported(vm.getHypervisorType())) {
@@ -9422,22 +9423,21 @@ public boolean unmanageUserVM(Long vmId) {
94229423
checkUnmanagingVMOngoingVolumeSnapshots(vm);
94239424
checkUnmanagingVMVolumes(vm, volumes);
94249425

9425-
result = _itMgr.unmanage(vm.getUuid());
9426-
if (result) {
9426+
Pair<Boolean, String> result = _itMgr.unmanage(vm.getUuid(), paramHostId);
9427+
if (result.first()) {
94279428
cleanupUnmanageVMResources(vm);
94289429
unmanageVMFromDB(vm.getId());
94299430
publishUnmanageVMUsageEvents(vm, volumes);
94309431
} else {
94319432
throw new CloudRuntimeException("Error while unmanaging VM: " + vm.getUuid());
94329433
}
9434+
return result;
94339435
} catch (Exception e) {
94349436
logger.error("Could not unmanage VM {}", vm, e);
94359437
throw new CloudRuntimeException(e);
94369438
} finally {
94379439
_vmDao.releaseFromLockTable(vm.getId());
94389440
}
9439-
9440-
return true;
94419441
}
94429442

94439443
private void updateDetailsWithRootDiskAttributes(Map<String, String> details, VmDiskInfo rootVmDiskInfo) {

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2265,7 +2265,7 @@ private Long findSuitableHostId(VMInstanceVO vmVO) {
22652265

22662266
@Override
22672267
@ActionEvent(eventType = EventTypes.EVENT_VM_UNMANAGE, eventDescription = "unmanaging VM", async = true)
2268-
public boolean unmanageVMInstance(long vmId) {
2268+
public Pair<Boolean, String> unmanageVMInstance(long vmId, Long paramHostId) {
22692269
VMInstanceVO vmVO = vmDao.findById(vmId);
22702270
if (vmVO == null || vmVO.getRemoved() != null) {
22712271
throw new InvalidParameterValueException("Could not find VM to unmanage, it is either removed or not existing VM");
@@ -2276,6 +2276,9 @@ public boolean unmanageVMInstance(long vmId) {
22762276
vmVO.getHypervisorType().toString());
22772277
} else if (vmVO.getType() != VirtualMachine.Type.User) {
22782278
throw new UnsupportedServiceException("Unmanage VM is currently allowed for guest VMs only");
2279+
} else if (paramHostId != null &&
2280+
(vmVO.getHypervisorType() != Hypervisor.HypervisorType.KVM || vmVO.getState() != VirtualMachine.State.Stopped)) {
2281+
throw new UnsupportedServiceException("Param hostid is only supported for KVM hypervisor for stopped Instances.");
22792282
}
22802283

22812284
if (vmVO.getType().equals(VirtualMachine.Type.User)) {
@@ -2287,15 +2290,15 @@ public boolean unmanageVMInstance(long vmId) {
22872290

22882291
performUnmanageVMInstancePrechecks(vmVO);
22892292

2290-
boolean isVMStopped = VirtualMachine.State.Stopped.equals(vmVO.getState());
2291-
Long hostId = isVMStopped ? vmVO.getLastHostId() : findSuitableHostId(vmVO);
2293+
boolean isKvmVmStopped = VirtualMachine.State.Stopped.equals(vmVO.getState()) && vmVO.getHypervisorType() == Hypervisor.HypervisorType.KVM;
2294+
Long hostId = isKvmVmStopped ? vmVO.getLastHostId() : findSuitableHostId(vmVO);
22922295
String instanceName = vmVO.getInstanceName();
22932296

2294-
if (!isVMStopped && !existsVMToUnmanage(instanceName, hostId)) {
2297+
if (!isKvmVmStopped && !existsVMToUnmanage(instanceName, hostId)) {
22952298
throw new CloudRuntimeException(String.format("VM %s is not found in the hypervisor", vmVO));
22962299
}
22972300

2298-
return userVmManager.unmanageUserVM(vmId);
2301+
return userVmManager.unmanageUserVM(vmId, paramHostId);
22992302
}
23002303

23012304
/**

0 commit comments

Comments
 (0)