[4.22] Prevent unmanaging or reinstalling a VM if it is part of a CKS cluster (#12800)

This commit is contained in:
Nicolas Vazquez 2026-03-26 09:47:49 -03:00 committed by GitHub
parent 84676afd5c
commit c1af36f8fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 78 additions and 0 deletions

View File

@ -199,4 +199,9 @@ public interface UserVmManager extends UserVmService {
Boolean getDestroyRootVolumeOnVmDestruction(Long domainId);
/**
* @return true if the VM is part of a CKS cluster, false otherwise.
*/
boolean isVMPartOfAnyCKSCluster(VMInstanceVO vm);
}

View File

@ -8785,6 +8785,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
throw new InvalidParameterValueException(String.format("Operation not supported for instance: %s",
vm.getName()));
}
if (isVMPartOfAnyCKSCluster(vm)) {
throw new UnsupportedServiceException("Cannot restore VM with id = " + vm.getUuid() +
" as it belongs to a CKS cluster. Please remove the VM from the CKS cluster before restoring.");
}
_accountMgr.checkAccess(caller, null, true, vm);
VMTemplateVO template;
@ -9986,6 +9990,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return DestroyRootVolumeOnVmDestruction.valueIn(domainId);
}
@Override
public boolean isVMPartOfAnyCKSCluster(VMInstanceVO vm) {
return kubernetesServiceHelpers.get(0).findByVmId(vm.getId()) != null;
}
private void setVncPasswordForKvmIfAvailable(Map<String, String> customParameters, UserVmVO vm) {
if (customParameters.containsKey(VmDetailConstants.KVM_VNC_PASSWORD)
&& StringUtils.isNotEmpty(customParameters.get(VmDetailConstants.KVM_VNC_PASSWORD))) {

View File

@ -2304,6 +2304,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
* Perform validations before attempting to unmanage a VM from CloudStack:
* - VM must not have any associated volume snapshot
* - VM must not have an attached ISO
* - VM must not belong to any CKS cluster
* @throws UnsupportedServiceException in case any of the validations above fail
*/
void performUnmanageVMInstancePrechecks(VMInstanceVO vmVO) {
if (hasVolumeSnapshotsPriorToUnmanageVM(vmVO)) {
@ -2315,6 +2317,11 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
throw new UnsupportedServiceException("Cannot unmanage VM with id = " + vmVO.getUuid() +
" as there is an ISO attached. Please detach ISO before unmanaging.");
}
if (userVmManager.isVMPartOfAnyCKSCluster(vmVO)) {
throw new UnsupportedServiceException("Cannot unmanage VM with id = " + vmVO.getUuid() +
" as it belongs to a CKS cluster. Please remove the VM from the CKS cluster before unmanaging.");
}
}
private boolean hasVolumeSnapshotsPriorToUnmanageVM(VMInstanceVO vmVO) {

View File

@ -59,6 +59,7 @@ import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import com.cloud.kubernetes.cluster.KubernetesServiceHelper;
import com.cloud.storage.dao.SnapshotPolicyDao;
import com.cloud.utils.fsm.NoTransitionException;
import org.apache.cloudstack.acl.ControlledEntity;
@ -1476,6 +1477,9 @@ public class UserVmManagerImplTest {
when(cmd.getVmId()).thenReturn(vmId);
when(cmd.getTemplateId()).thenReturn(2L);
when(userVmDao.findById(vmId)).thenReturn(userVmVoMock);
KubernetesServiceHelper helper = mock(KubernetesServiceHelper.class);
when(helper.findByVmId(anyLong())).thenReturn(null);
userVmManagerImpl.setKubernetesServiceHelpers(Collections.singletonList(helper));
userVmManagerImpl.restoreVM(cmd);
}

View File

@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
@ -39,6 +40,9 @@ import java.util.Map;
import java.util.UUID;
import com.cloud.offering.DiskOffering;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.vm.ImportVMTaskVO;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ResponseGenerator;
@ -241,6 +245,8 @@ public class UnmanagedVMsManagerImplTest {
private StoragePoolHostDao storagePoolHostDao;
@Mock
private ImportVmTasksManager importVmTasksManager;
@Mock
private SnapshotDao snapshotDao;
@Mock
private VMInstanceVO virtualMachine;
@ -568,6 +574,53 @@ public class UnmanagedVMsManagerImplTest {
unmanagedVMsManager.unmanageVMInstance(virtualMachineId, null, false);
}
@Test(expected = UnsupportedServiceException.class)
public void testUnmanageVMInstanceWithVolumeSnapshotsFail() {
when(virtualMachine.getType()).thenReturn(VirtualMachine.Type.User);
when(virtualMachine.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Stopped);
when(virtualMachine.getId()).thenReturn(virtualMachineId);
UserVmVO userVmVO = mock(UserVmVO.class);
when(userVmDao.findById(anyLong())).thenReturn(userVmVO);
when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine);
VolumeVO volumeVO = mock(VolumeVO.class);
long volumeId = 20L;
when(volumeVO.getId()).thenReturn(volumeId);
SnapshotVO snapshotVO = mock(SnapshotVO.class);
when(snapshotVO.getState()).thenReturn(Snapshot.State.BackedUp);
when(snapshotDao.listByVolumeId(volumeId)).thenReturn(Collections.singletonList(snapshotVO));
when(volumeDao.findByInstance(virtualMachineId)).thenReturn(Collections.singletonList(volumeVO));
unmanagedVMsManager.unmanageVMInstance(virtualMachineId, null, false);
}
@Test(expected = UnsupportedServiceException.class)
public void testUnmanageVMInstanceWithAssociatedIsoFail() {
when(virtualMachine.getType()).thenReturn(VirtualMachine.Type.User);
when(virtualMachine.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Stopped);
when(virtualMachine.getId()).thenReturn(virtualMachineId);
UserVmVO userVmVO = mock(UserVmVO.class);
when(userVmVO.getIsoId()).thenReturn(null);
when(userVmDao.findById(anyLong())).thenReturn(userVmVO);
when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine);
when(userVmVO.getIsoId()).thenReturn(1L);
unmanagedVMsManager.unmanageVMInstance(virtualMachineId, null, false);
}
@Test(expected = UnsupportedServiceException.class)
public void testUnmanageVMInstanceBelongingToCksClusterFail() {
when(virtualMachine.getType()).thenReturn(VirtualMachine.Type.User);
when(virtualMachine.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Stopped);
when(virtualMachine.getId()).thenReturn(virtualMachineId);
UserVmVO userVmVO = mock(UserVmVO.class);
when(userVmVO.getIsoId()).thenReturn(null);
when(userVmDao.findById(anyLong())).thenReturn(userVmVO);
when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine);
when(userVmManager.isVMPartOfAnyCKSCluster(virtualMachine)).thenReturn(true);
unmanagedVMsManager.unmanageVMInstance(virtualMachineId, null, false);
}
@Test
public void testListRemoteInstancesTest() {
ListVmsForImportCmd cmd = Mockito.mock(ListVmsForImportCmd.class);