Skip to content

Commit c1af36f

Browse files
authored
[4.22] Prevent unmanaging or reinstalling a VM if it is part of a CKS cluster (#12800)
1 parent 84676af commit c1af36f

File tree

5 files changed

+78
-0
lines changed

5 files changed

+78
-0
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,9 @@ static Set<String> getStrictHostTags() {
199199

200200
Boolean getDestroyRootVolumeOnVmDestruction(Long domainId);
201201

202+
/**
203+
* @return true if the VM is part of a CKS cluster, false otherwise.
204+
*/
205+
boolean isVMPartOfAnyCKSCluster(VMInstanceVO vm);
206+
202207
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8785,6 +8785,10 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException,
87858785
throw new InvalidParameterValueException(String.format("Operation not supported for instance: %s",
87868786
vm.getName()));
87878787
}
8788+
if (isVMPartOfAnyCKSCluster(vm)) {
8789+
throw new UnsupportedServiceException("Cannot restore VM with id = " + vm.getUuid() +
8790+
" as it belongs to a CKS cluster. Please remove the VM from the CKS cluster before restoring.");
8791+
}
87888792
_accountMgr.checkAccess(caller, null, true, vm);
87898793

87908794
VMTemplateVO template;
@@ -9986,6 +9990,11 @@ public Boolean getDestroyRootVolumeOnVmDestruction(Long domainId) {
99869990
return DestroyRootVolumeOnVmDestruction.valueIn(domainId);
99879991
}
99889992

9993+
@Override
9994+
public boolean isVMPartOfAnyCKSCluster(VMInstanceVO vm) {
9995+
return kubernetesServiceHelpers.get(0).findByVmId(vm.getId()) != null;
9996+
}
9997+
99899998
private void setVncPasswordForKvmIfAvailable(Map<String, String> customParameters, UserVmVO vm) {
99909999
if (customParameters.containsKey(VmDetailConstants.KVM_VNC_PASSWORD)
999110000
&& StringUtils.isNotEmpty(customParameters.get(VmDetailConstants.KVM_VNC_PASSWORD))) {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2304,6 +2304,8 @@ public List<Class<?>> getCommands() {
23042304
* Perform validations before attempting to unmanage a VM from CloudStack:
23052305
* - VM must not have any associated volume snapshot
23062306
* - VM must not have an attached ISO
2307+
* - VM must not belong to any CKS cluster
2308+
* @throws UnsupportedServiceException in case any of the validations above fail
23072309
*/
23082310
void performUnmanageVMInstancePrechecks(VMInstanceVO vmVO) {
23092311
if (hasVolumeSnapshotsPriorToUnmanageVM(vmVO)) {
@@ -2315,6 +2317,11 @@ void performUnmanageVMInstancePrechecks(VMInstanceVO vmVO) {
23152317
throw new UnsupportedServiceException("Cannot unmanage VM with id = " + vmVO.getUuid() +
23162318
" as there is an ISO attached. Please detach ISO before unmanaging.");
23172319
}
2320+
2321+
if (userVmManager.isVMPartOfAnyCKSCluster(vmVO)) {
2322+
throw new UnsupportedServiceException("Cannot unmanage VM with id = " + vmVO.getUuid() +
2323+
" as it belongs to a CKS cluster. Please remove the VM from the CKS cluster before unmanaging.");
2324+
}
23182325
}
23192326

23202327
private boolean hasVolumeSnapshotsPriorToUnmanageVM(VMInstanceVO vmVO) {

server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import java.util.TimeZone;
6060
import java.util.UUID;
6161

62+
import com.cloud.kubernetes.cluster.KubernetesServiceHelper;
6263
import com.cloud.storage.dao.SnapshotPolicyDao;
6364
import com.cloud.utils.fsm.NoTransitionException;
6465
import org.apache.cloudstack.acl.ControlledEntity;
@@ -1476,6 +1477,9 @@ public void testRestoreVMWithVolumeSnapshots() throws ResourceUnavailableExcepti
14761477
when(cmd.getVmId()).thenReturn(vmId);
14771478
when(cmd.getTemplateId()).thenReturn(2L);
14781479
when(userVmDao.findById(vmId)).thenReturn(userVmVoMock);
1480+
KubernetesServiceHelper helper = mock(KubernetesServiceHelper.class);
1481+
when(helper.findByVmId(anyLong())).thenReturn(null);
1482+
userVmManagerImpl.setKubernetesServiceHelpers(Collections.singletonList(helper));
14791483

14801484
userVmManagerImpl.restoreVM(cmd);
14811485
}

server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
import java.net.URI;
3333
import java.util.ArrayList;
34+
import java.util.Collections;
3435
import java.util.Date;
3536
import java.util.HashMap;
3637
import java.util.LinkedHashMap;
@@ -39,6 +40,9 @@
3940
import java.util.UUID;
4041

4142
import com.cloud.offering.DiskOffering;
43+
import com.cloud.storage.Snapshot;
44+
import com.cloud.storage.SnapshotVO;
45+
import com.cloud.storage.dao.SnapshotDao;
4246
import com.cloud.vm.ImportVMTaskVO;
4347
import org.apache.cloudstack.api.ApiConstants;
4448
import org.apache.cloudstack.api.ResponseGenerator;
@@ -241,6 +245,8 @@ public class UnmanagedVMsManagerImplTest {
241245
private StoragePoolHostDao storagePoolHostDao;
242246
@Mock
243247
private ImportVmTasksManager importVmTasksManager;
248+
@Mock
249+
private SnapshotDao snapshotDao;
244250

245251
@Mock
246252
private VMInstanceVO virtualMachine;
@@ -568,6 +574,53 @@ public void unmanageVMInstanceStoppedInstanceTest() {
568574
unmanagedVMsManager.unmanageVMInstance(virtualMachineId, null, false);
569575
}
570576

577+
@Test(expected = UnsupportedServiceException.class)
578+
public void testUnmanageVMInstanceWithVolumeSnapshotsFail() {
579+
when(virtualMachine.getType()).thenReturn(VirtualMachine.Type.User);
580+
when(virtualMachine.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
581+
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Stopped);
582+
when(virtualMachine.getId()).thenReturn(virtualMachineId);
583+
UserVmVO userVmVO = mock(UserVmVO.class);
584+
when(userVmDao.findById(anyLong())).thenReturn(userVmVO);
585+
when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine);
586+
VolumeVO volumeVO = mock(VolumeVO.class);
587+
long volumeId = 20L;
588+
when(volumeVO.getId()).thenReturn(volumeId);
589+
SnapshotVO snapshotVO = mock(SnapshotVO.class);
590+
when(snapshotVO.getState()).thenReturn(Snapshot.State.BackedUp);
591+
when(snapshotDao.listByVolumeId(volumeId)).thenReturn(Collections.singletonList(snapshotVO));
592+
when(volumeDao.findByInstance(virtualMachineId)).thenReturn(Collections.singletonList(volumeVO));
593+
unmanagedVMsManager.unmanageVMInstance(virtualMachineId, null, false);
594+
}
595+
596+
@Test(expected = UnsupportedServiceException.class)
597+
public void testUnmanageVMInstanceWithAssociatedIsoFail() {
598+
when(virtualMachine.getType()).thenReturn(VirtualMachine.Type.User);
599+
when(virtualMachine.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
600+
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Stopped);
601+
when(virtualMachine.getId()).thenReturn(virtualMachineId);
602+
UserVmVO userVmVO = mock(UserVmVO.class);
603+
when(userVmVO.getIsoId()).thenReturn(null);
604+
when(userVmDao.findById(anyLong())).thenReturn(userVmVO);
605+
when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine);
606+
when(userVmVO.getIsoId()).thenReturn(1L);
607+
unmanagedVMsManager.unmanageVMInstance(virtualMachineId, null, false);
608+
}
609+
610+
@Test(expected = UnsupportedServiceException.class)
611+
public void testUnmanageVMInstanceBelongingToCksClusterFail() {
612+
when(virtualMachine.getType()).thenReturn(VirtualMachine.Type.User);
613+
when(virtualMachine.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
614+
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Stopped);
615+
when(virtualMachine.getId()).thenReturn(virtualMachineId);
616+
UserVmVO userVmVO = mock(UserVmVO.class);
617+
when(userVmVO.getIsoId()).thenReturn(null);
618+
when(userVmDao.findById(anyLong())).thenReturn(userVmVO);
619+
when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine);
620+
when(userVmManager.isVMPartOfAnyCKSCluster(virtualMachine)).thenReturn(true);
621+
unmanagedVMsManager.unmanageVMInstance(virtualMachineId, null, false);
622+
}
623+
571624
@Test
572625
public void testListRemoteInstancesTest() {
573626
ListVmsForImportCmd cmd = Mockito.mock(ListVmsForImportCmd.class);

0 commit comments

Comments
 (0)