diff --git a/api/src/main/java/com/cloud/template/TemplateApiService.java b/api/src/main/java/com/cloud/template/TemplateApiService.java index b62628560ae..ea818a55a0c 100644 --- a/api/src/main/java/com/cloud/template/TemplateApiService.java +++ b/api/src/main/java/com/cloud/template/TemplateApiService.java @@ -56,9 +56,9 @@ public interface TemplateApiService { VirtualMachineTemplate prepareTemplate(long templateId, long zoneId, Long storageId); - boolean detachIso(long vmId); + boolean detachIso(long vmId, boolean forced); - boolean attachIso(long isoId, long vmId); + boolean attachIso(long isoId, long vmId, boolean forced); /** * Deletes a template diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java index 245840004fe..74a98beef9d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java @@ -54,6 +54,10 @@ public class AttachIsoCmd extends BaseAsyncCmd implements UserCmd { required = true, description = "the ID of the virtual machine") protected Long virtualMachineId; + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, + description = "If true, ejects existing ISO before attaching on VMware. Default: false", since = "4.15.1") + protected Boolean forced; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -66,6 +70,10 @@ public class AttachIsoCmd extends BaseAsyncCmd implements UserCmd { return virtualMachineId; } + public Boolean isForced() { + return forced != null; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -98,7 +106,7 @@ public class AttachIsoCmd extends BaseAsyncCmd implements UserCmd { @Override public void execute() { CallContext.current().setEventDetails("Vm Id: " + getVirtualMachineId() + " ISO ID: " + getId()); - boolean result = _templateService.attachIso(id, virtualMachineId); + boolean result = _templateService.attachIso(id, virtualMachineId, isForced()); if (result) { UserVm userVm = _responseGenerator.findUserVmById(virtualMachineId); if (userVm != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java index ae86e2fdd7f..4f4a0197ac7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java @@ -44,10 +44,14 @@ public class DetachIsoCmd extends BaseAsyncCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType = UserVmResponse.class, - required=true, description="The ID of the virtual machine") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, + required = true, description = "The ID of the virtual machine") protected Long virtualMachineId; + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, + description = "If true, ejects the ISO before detaching on VMware. Default: false", since = "4.15.1") + protected Boolean forced; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -56,6 +60,10 @@ public class DetachIsoCmd extends BaseAsyncCmd implements UserCmd { return virtualMachineId; } + public Boolean isForced() { + return forced != null; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -87,7 +95,7 @@ public class DetachIsoCmd extends BaseAsyncCmd implements UserCmd { @Override public void execute() { - boolean result = _templateService.detachIso(virtualMachineId); + boolean result = _templateService.detachIso(virtualMachineId, isForced()); if (result) { UserVm userVm = _entityMgr.findById(UserVm.class, virtualMachineId); UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", userVm).get(0); diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/AttachCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/AttachCommand.java index d15a4e42da3..ae6ea1fa649 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/AttachCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/AttachCommand.java @@ -27,6 +27,7 @@ public final class AttachCommand extends StorageSubSystemCommand { private DiskTO disk; private String vmName; private boolean inSeq = false; + private boolean forced = false; private Map controllerInfo; public AttachCommand(final DiskTO disk, final String vmName) { @@ -69,6 +70,14 @@ public final class AttachCommand extends StorageSubSystemCommand { this.vmName = vmName; } + public boolean isForced() { + return forced; + } + + public void setForced(boolean forced) { + this.forced = forced; + } + @Override public void setExecuteInSequence(final boolean inSeq) { this.inSeq = inSeq; diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/DettachCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/DettachCommand.java index 1d805b582e9..eeeadaac42b 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/DettachCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/DettachCommand.java @@ -31,6 +31,7 @@ public class DettachCommand extends StorageSubSystemCommand { private String _storageHost; private int _storagePort; private Map params; + private boolean forced; public DettachCommand(final DiskTO disk, final String vmName) { super(); @@ -106,6 +107,14 @@ public class DettachCommand extends StorageSubSystemCommand { this.params = params; } + public boolean isForced() { + return forced; + } + + public void setForced(boolean forced) { + this.forced = forced; + } + @Override public void setExecuteInSequence(final boolean inSeq) { diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java index dd451f59465..7fad871462d 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java @@ -200,7 +200,9 @@ public class DataMigrationUtility { snapshotVO != null && snapshotVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator && snapshot.getParentSnapshotId() == 0 ) { SnapshotInfo snap = snapshotFactory.getSnapshot(snapshotVO.getSnapshotId(), DataStoreRole.Image); - files.add(snap); + if (snap != null) { + files.add(snap); + } } } diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java index f8be1adee56..42578850d54 100644 --- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java +++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java @@ -87,6 +87,9 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory { @Override public SnapshotInfo getSnapshot(long snapshotId, DataStoreRole role) { SnapshotVO snapshot = snapshotDao.findById(snapshotId); + if (snapshot == null) { + return null; + } SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshotId, role); if (snapshotStore == null) { snapshotStore = snapshotStoreDao.findByVolume(snapshot.getVolumeId(), role); diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java index f107343f0de..1d343ab22d2 100644 --- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java +++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java @@ -142,7 +142,10 @@ public class SnapshotObject implements SnapshotInfo { List children = new ArrayList<>(); if (vos != null) { for (SnapshotDataStoreVO vo : vos) { - children.add(snapshotFactory.getSnapshot(vo.getSnapshotId(), DataStoreRole.Image)); + SnapshotInfo info = snapshotFactory.getSnapshot(vo.getSnapshotId(), DataStoreRole.Image); + if (info != null) { + children.add(info); + } } } return children; diff --git a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java index 54ce065f9cd..34f58933cd4 100644 --- a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java +++ b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java @@ -1097,6 +1097,10 @@ public class AsyncJobManagerImpl extends ManagerBase implements AsyncJobManager, final List snapshotList = _snapshotDetailsDao.findDetails(AsyncJob.Constants.MS_ID, Long.toString(msid), false); for (final SnapshotDetailsVO snapshotDetailsVO : snapshotList) { SnapshotInfo snapshot = snapshotFactory.getSnapshot(snapshotDetailsVO.getResourceId(), DataStoreRole.Primary); + if (snapshot == null) { + _snapshotDetailsDao.remove(snapshotDetailsVO.getId()); + continue; + } snapshotSrv.processEventOnSnapshotObject(snapshot, Snapshot.Event.OperationFailed); _snapshotDetailsDao.removeDetail(snapshotDetailsVO.getResourceId(), AsyncJob.Constants.MS_ID); } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 333697c3420..2035cc736ad 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -2038,35 +2038,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (volIso != null) { for (DiskTO vol : disks) { if (vol.getType() == Volume.Type.ISO) { - - TemplateObjectTO iso = (TemplateObjectTO) vol.getData(); - - if (iso.getPath() != null && !iso.getPath().isEmpty()) { - DataStoreTO imageStore = iso.getDataStore(); - if (!(imageStore instanceof NfsTO)) { - s_logger.debug("unsupported protocol"); - throw new Exception("unsupported protocol"); - } - NfsTO nfsImageStore = (NfsTO) imageStore; - String isoPath = nfsImageStore.getUrl() + File.separator + iso.getPath(); - Pair isoDatastoreInfo = getIsoDatastoreInfo(hyperHost, isoPath); - assert (isoDatastoreInfo != null); - assert (isoDatastoreInfo.second() != null); - - deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); - Pair isoInfo = - VmwareHelper.prepareIsoDevice(vmMo, isoDatastoreInfo.first(), isoDatastoreInfo.second(), true, true, ideUnitNumber++, i + 1); - deviceConfigSpecArray[i].setDevice(isoInfo.first()); - if (isoInfo.second()) { - if (s_logger.isDebugEnabled()) - s_logger.debug("Prepare ISO volume at new device " + _gson.toJson(isoInfo.first())); - deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); - } else { - if (s_logger.isDebugEnabled()) - s_logger.debug("Prepare ISO volume at existing device " + _gson.toJson(isoInfo.first())); - deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); - } - } + configureIso(hyperHost, vmMo, vol, deviceConfigSpecArray, ideUnitNumber++, i); i++; } } @@ -2094,8 +2066,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa // // Setup ROOT/DATA disk devices // + if (multipleIsosAtached(sortedDisks) && deployAsIs) { + sortedDisks = getDisks(sortedDisks); + } + for (DiskTO vol : sortedDisks) { if (vol.getType() == Volume.Type.ISO) { + if (deployAsIs) { + configureIso(hyperHost, vmMo, vol, deviceConfigSpecArray, ideUnitNumber++, i); + i++; + } continue; } @@ -2476,6 +2456,46 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } + private boolean multipleIsosAtached(DiskTO[] sortedDisks) { + return Arrays.stream(sortedDisks).filter(disk -> disk.getType() == Volume.Type.ISO).count() > 1; + } + + private DiskTO[] getDisks(DiskTO[] sortedDisks) { + return Arrays.stream(sortedDisks).filter(vol -> ((vol.getPath() != null && + vol.getPath().contains("configdrive"))) || (vol.getType() != Volume.Type.ISO)).toArray(DiskTO[]::new); + } + private void configureIso(VmwareHypervisorHost hyperHost, VirtualMachineMO vmMo, DiskTO vol, + VirtualDeviceConfigSpec[] deviceConfigSpecArray, int ideUnitNumber, int i) throws Exception { + TemplateObjectTO iso = (TemplateObjectTO) vol.getData(); + + if (iso.getPath() != null && !iso.getPath().isEmpty()) { + DataStoreTO imageStore = iso.getDataStore(); + if (!(imageStore instanceof NfsTO)) { + s_logger.debug("unsupported protocol"); + throw new Exception("unsupported protocol"); + } + NfsTO nfsImageStore = (NfsTO) imageStore; + String isoPath = nfsImageStore.getUrl() + File.separator + iso.getPath(); + Pair isoDatastoreInfo = getIsoDatastoreInfo(hyperHost, isoPath); + assert (isoDatastoreInfo != null); + assert (isoDatastoreInfo.second() != null); + + deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); + Pair isoInfo = + VmwareHelper.prepareIsoDevice(vmMo, isoDatastoreInfo.first(), isoDatastoreInfo.second(), true, true, ideUnitNumber, i + 1); + deviceConfigSpecArray[i].setDevice(isoInfo.first()); + if (isoInfo.second()) { + if (s_logger.isDebugEnabled()) + s_logger.debug("Prepare ISO volume at new device " + _gson.toJson(isoInfo.first())); + deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); + } else { + if (s_logger.isDebugEnabled()) + s_logger.debug("Prepare ISO volume at existing device " + _gson.toJson(isoInfo.first())); + deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); + } + } + } + private String mapAdapterType(String adapterStringFromOVF) { if (StringUtils.isBlank(adapterStringFromOVF) || adapterStringFromOVF.equalsIgnoreCase(VirtualEthernetCardType.E1000.toString())) { return VirtualEthernetCardType.E1000.toString(); @@ -5147,7 +5167,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa "Failed to unmount vmware-tools installer ISO as the corresponding CDROM device is locked by VM. Please unmount the CDROM device inside the VM and ret-try."); } } catch (Throwable e) { - vmMo.detachIso(null); + vmMo.detachIso(null, cmd.isForce()); } } @@ -5176,7 +5196,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa String isoDatastorePath = String.format("[%s] %s%s", storeName, isoStorePathFromRoot, isoFileName); if (cmd.isAttach()) { - vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false, cmd.getDeviceKey()); + vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false, cmd.getDeviceKey(), cmd.isForce()); return new AttachIsoAnswer(cmd); } else { int key = vmMo.detachIso(isoDatastorePath, cmd.isForce()); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java index a2aee6b53e1..b06c1bef69c 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -2045,7 +2045,7 @@ public class VmwareStorageProcessor implements StorageProcessor { @Override public Answer attachIso(AttachCommand cmd) { - return this.attachIso(cmd.getDisk(), true, cmd.getVmName()); + return this.attachIso(cmd.getDisk(), true, cmd.getVmName(), cmd.isForced()); } @Override @@ -2411,7 +2411,7 @@ public class VmwareStorageProcessor implements StorageProcessor { return morDatastore; } - private Answer attachIso(DiskTO disk, boolean isAttach, String vmName) { + private Answer attachIso(DiskTO disk, boolean isAttach, String vmName, boolean force) { try { VmwareContext context = hostService.getServiceContext(null); VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, null); @@ -2441,7 +2441,7 @@ public class VmwareStorageProcessor implements StorageProcessor { return new AttachAnswer("Failed to unmount vmware-tools installer ISO as the corresponding CDROM device is locked by VM. Please unmount the CDROM device inside the VM and ret-try."); } } catch(Throwable e){ - vmMo.detachIso(null); + vmMo.detachIso(null, force); } } @@ -2469,9 +2469,9 @@ public class VmwareStorageProcessor implements StorageProcessor { String isoDatastorePath = String.format("[%s] %s/%s", storeName, isoStorePathFromRoot, isoFileName); if (isAttach) { - vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false); + vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false, force); } else { - vmMo.detachIso(isoDatastorePath); + vmMo.detachIso(isoDatastorePath, force); } return new AttachAnswer(disk); @@ -2497,7 +2497,7 @@ public class VmwareStorageProcessor implements StorageProcessor { @Override public Answer dettachIso(DettachCommand cmd) { - return this.attachIso(cmd.getDisk(), false, cmd.getVmName()); + return this.attachIso(cmd.getDisk(), false, cmd.getVmName(), cmd.isForced()); } @Override diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java index e5c811878ce..1ca58a0dfa6 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java @@ -319,7 +319,7 @@ public class KubernetesClusterActionWorker { } for (UserVm vm : clusterVMs) { try { - templateService.attachIso(iso.getId(), vm.getId()); + templateService.attachIso(iso.getId(), vm.getId(), true); if (LOGGER.isInfoEnabled()) { LOGGER.info(String.format("Attached binaries ISO for VM : %s in cluster: %s", vm.getDisplayName(), kubernetesCluster.getName())); } @@ -337,7 +337,7 @@ public class KubernetesClusterActionWorker { for (UserVm vm : clusterVMs) { boolean result = false; try { - result = templateService.detachIso(vm.getId()); + result = templateService.detachIso(vm.getId(), true); } catch (CloudRuntimeException ex) { LOGGER.warn(String.format("Failed to detach binaries ISO from VM : %s in the Kubernetes cluster : %s ", vm.getDisplayName(), kubernetesCluster.getName()), ex); } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index 324143cffcf..17def806c33 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -328,7 +328,9 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu for (int i = offset + 1; i <= nodeCount; i++) { UserVm vm = createKubernetesNode(publicIpAddress, i); addKubernetesClusterVm(kubernetesCluster.getId(), vm.getId()); - resizeNodeVolume(vm); + if (kubernetesCluster.getNodeRootDiskSize() > 0) { + resizeNodeVolume(vm); + } startKubernetesVM(vm); vm = userVmDao.findById(vm.getId()); if (vm == null) { diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java index 5855319ae67..4a96b9ed2ba 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java @@ -277,7 +277,9 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif UserVm k8sMasterVM = null; k8sMasterVM = createKubernetesMaster(network, publicIpAddress); addKubernetesClusterVm(kubernetesCluster.getId(), k8sMasterVM.getId()); - resizeNodeVolume(k8sMasterVM); + if (kubernetesCluster.getNodeRootDiskSize() > 0) { + resizeNodeVolume(k8sMasterVM); + } startKubernetesVM(k8sMasterVM); k8sMasterVM = userVmDao.findById(k8sMasterVM.getId()); if (k8sMasterVM == null) { @@ -297,7 +299,9 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif UserVm vm = null; vm = createKubernetesAdditionalMaster(publicIpAddress, i); addKubernetesClusterVm(kubernetesCluster.getId(), vm.getId()); - resizeNodeVolume(vm); + if (kubernetesCluster.getNodeRootDiskSize() > 0) { + resizeNodeVolume(vm); + } startKubernetesVM(vm); vm = userVmDao.findById(vm.getId()); if (vm == null) { diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java index b22d3b4ca3c..299ce68909a 100644 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java @@ -24,7 +24,6 @@ import org.apache.cloudstack.framework.config.Configurable; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.exception.ResourceAllocationException; -import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Volume; @@ -85,7 +84,5 @@ public interface SnapshotManager extends Configurable { SnapshotVO getParentSnapshot(VolumeInfo volume); - Snapshot backupSnapshot(Long snapshotId); - SnapshotInfo takeSnapshot(VolumeInfo volume) throws ResourceAllocationException; } diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 553d9b3cfb9..06da5d1f002 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -420,16 +420,6 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement return snapshotOnSecondary; } - @Override - public Snapshot backupSnapshot(Long snapshotId) { - SnapshotInfo snapshot = snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Image); - if (snapshot != null) { - throw new CloudRuntimeException("Already in the backup snapshot:" + snapshotId); - } - - return snapshotSrv.backupSnapshot(snapshot); - } - @Override public Snapshot backupSnapshotFromVmSnapshot(Long snapshotId, Long vmId, Long volumeId, Long vmSnapshotId) { VMInstanceVO vm = _vmDao.findById(vmId); diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index deb5feb2e81..956c456c2ff 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -1150,7 +1150,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, @Override @ActionEvent(eventType = EventTypes.EVENT_ISO_DETACH, eventDescription = "detaching ISO", async = true) - public boolean detachIso(long vmId) { + public boolean detachIso(long vmId, boolean forced) { Account caller = CallContext.current().getCallingAccount(); Long userId = CallContext.current().getCallingUserId(); @@ -1178,7 +1178,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, throw new InvalidParameterValueException("Please specify a VM that is either Stopped or Running."); } - boolean result = attachISOToVM(vmId, userId, isoId, false); // attach=false + boolean result = attachISOToVM(vmId, userId, isoId, false, forced); // attach=false // => detach if (result) { return result; @@ -1189,7 +1189,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, @Override @ActionEvent(eventType = EventTypes.EVENT_ISO_ATTACH, eventDescription = "attaching ISO", async = true) - public boolean attachIso(long isoId, long vmId) { + public boolean attachIso(long isoId, long vmId, boolean forced) { Account caller = CallContext.current().getCallingAccount(); Long userId = CallContext.current().getCallingUserId(); @@ -1231,7 +1231,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, if ("vmware-tools.iso".equals(iso.getName()) && vm.getHypervisorType() != Hypervisor.HypervisorType.VMware) { throw new InvalidParameterValueException("Cannot attach VMware tools drivers to incompatible hypervisor " + vm.getHypervisorType()); } - boolean result = attachISOToVM(vmId, userId, isoId, true); + boolean result = attachISOToVM(vmId, userId, isoId, true, forced); if (result) { return result; } else { @@ -1270,7 +1270,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } } - private boolean attachISOToVM(long vmId, long isoId, boolean attach) { + private boolean attachISOToVM(long vmId, long isoId, boolean attach, boolean forced) { UserVmVO vm = _userVmDao.findById(vmId); if (vm == null) { @@ -1302,19 +1302,21 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, Command cmd = null; if (attach) { - cmd = new AttachCommand(disk, vmName, vmTO.getDetails()); + cmd = new AttachCommand(disk, vmName); + ((AttachCommand)cmd).setForced(forced); } else { - cmd = new DettachCommand(disk, vmName, vmTO.getDetails()); + cmd = new DettachCommand(disk, vmName); + ((DettachCommand)cmd).setForced(forced); } Answer a = _agentMgr.easySend(vm.getHostId(), cmd); return (a != null && a.getResult()); } - private boolean attachISOToVM(long vmId, long userId, long isoId, boolean attach) { + private boolean attachISOToVM(long vmId, long userId, long isoId, boolean attach, boolean forced) { UserVmVO vm = _userVmDao.findById(vmId); VMTemplateVO iso = _tmpltDao.findById(isoId); - boolean success = attachISOToVM(vmId, isoId, attach); + boolean success = attachISOToVM(vmId, isoId, attach, forced); if (success && attach) { vm.setIsoId(iso.getId()); _userVmDao.update(vmId, vm); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 82a5a7071b2..3dd926470a0 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -5405,7 +5405,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir for (VMTemplateVO tmpl: child_templates){ if (tmpl.getFormat() == Storage.ImageFormat.ISO){ s_logger.info("MDOV trying to attach disk to the VM " + tmpl.getId() + " vmid=" + vm.getId()); - _tmplService.attachIso(tmpl.getId(), vm.getId()); + _tmplService.attachIso(tmpl.getId(), vm.getId(), true); } } diff --git a/test/integration/smoke/test_scale_vm.py b/test/integration/smoke/test_scale_vm.py index c58aabb90e6..2b2ced5ee34 100644 --- a/test/integration/smoke/test_scale_vm.py +++ b/test/integration/smoke/test_scale_vm.py @@ -58,7 +58,7 @@ class TestScaleVm(cloudstackTestCase): cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) cls.services['mode'] = cls.zone.networktype - if cls.hypervisor.lower() == 'simulator': + if cls.hypervisor.lower() in ['simulator', 'vmware']: cls.template = get_template( cls.apiclient, cls.zone.id, diff --git a/ui/src/components/view/ResourceView.vue b/ui/src/components/view/ResourceView.vue index 333f582ad0e..013310feba0 100644 --- a/ui/src/components/view/ResourceView.vue +++ b/ui/src/components/view/ResourceView.vue @@ -83,6 +83,10 @@ export default { component: DetailsTab }] } + }, + historyTab: { + type: String, + default: '' } }, data () { @@ -128,6 +132,7 @@ export default { ) }).join('&') ) + this.$emit('onTabChange', key) }, showTab (tab) { if ('networkServiceFilter' in tab) { @@ -158,7 +163,20 @@ export default { } }, setActiveTab () { - this.activeTab = this.$route.query.tab ? this.$route.query.tab : this.tabs[0].name + if (this.$route.query.tab) { + this.activeTab = this.$route.query.tab + return + } + if (!this.historyTab || !this.$route.meta.tabs || this.$route.meta.tabs.length === 0) { + this.activeTab = this.tabs[0].name + return + } + const tabIdx = this.$route.meta.tabs.findIndex(tab => tab.name === this.historyTab) + if (tabIdx === -1) { + this.activeTab = this.tabs[0].name + } else { + this.activeTab = this.historyTab + } } } } diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index f160f883d56..6ffd5091d90 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -255,7 +255,13 @@ export default { label: 'label.action.detach.iso', message: 'message.detach.iso.confirm', dataView: true, - args: ['virtualmachineid'], + args: (record, store) => { + var args = ['virtualmachineid'] + if (record && record.hypervisor && record.hypervisor === 'VMware') { + args.push('forced') + } + return args + }, show: (record) => { return ['Running', 'Stopped'].includes(record.state) && 'isoid' in record && record.isoid }, mapping: { virtualmachineid: { diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js index ff29b75700a..d764ae40ad6 100644 --- a/ui/src/store/modules/user.js +++ b/ui/src/store/modules/user.js @@ -325,7 +325,16 @@ const user = { }) }) }, - + UpdateConfiguration ({ commit }) { + return new Promise((resolve, reject) => { + api('listLdapConfigurations').then(response => { + const ldapEnable = (response.ldapconfigurationresponse.count > 0) + commit('SET_LDAP', ldapEnable) + }).catch(error => { + reject(error) + }) + }) + }, SetDomainStore ({ commit }, domainStore) { commit('SET_DOMAIN_STORE', domainStore) } diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index dd92d72468b..c51487c3b7a 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -963,6 +963,9 @@ export default { break } } + if (['addLdapConfiguration', 'deleteLdapConfiguration'].includes(action.api)) { + this.$store.dispatch('UpdateConfiguration') + } return false }, execSubmit (e) { diff --git a/ui/src/views/compute/AttachIso.vue b/ui/src/views/compute/AttachIso.vue index efb591f341a..bc12c264f8c 100644 --- a/ui/src/views/compute/AttachIso.vue +++ b/ui/src/views/compute/AttachIso.vue @@ -33,6 +33,9 @@ + + +
{{ this.$t('label.cancel') }} @@ -116,6 +119,11 @@ export default { id: values.id, virtualmachineid: this.resource.id } + + if (values.forced) { + params.forced = values.forced + } + this.loading = true const title = this.$t('label.action.attach.iso') api('attachIso', params).then(json => { diff --git a/ui/src/views/network/PublicIpResource.vue b/ui/src/views/network/PublicIpResource.vue index 2c97f93bde0..e80ed175ea4 100644 --- a/ui/src/views/network/PublicIpResource.vue +++ b/ui/src/views/network/PublicIpResource.vue @@ -33,7 +33,9 @@ v-if="isPublicIpAddress && 'id' in resource" :loading="loading" :resource="resource" - :tabs="tabs" /> + :historyTab="activeTab" + :tabs="tabs" + @onTabChange="(tab) => { this.activeTab = tab }" />
@@ -63,7 +65,8 @@ export default { tabs: [{ name: 'details', component: () => import('@/components/view/DetailsTab.vue') - }] + }], + activeTab: '' } }, mixins: [mixinDevice], diff --git a/ui/src/views/plugins/quota/QuotaSummaryResource.vue b/ui/src/views/plugins/quota/QuotaSummaryResource.vue index 381f94e0928..abc6e2d6f8d 100644 --- a/ui/src/views/plugins/quota/QuotaSummaryResource.vue +++ b/ui/src/views/plugins/quota/QuotaSummaryResource.vue @@ -19,7 +19,9 @@ + :tabs="tabs" + :historyTab="activeTab" + @onTabChange="(tab) => { this.activeTab = tab }"/>