From f49188179c49a35f1f3cb056e0429ce3de79a59a Mon Sep 17 00:00:00 2001 From: abh1sar Date: Thu, 22 Aug 2024 15:31:32 +0530 Subject: [PATCH 1/5] Global setting to allow/disallow users to force stop a vm --- .../command/user/config/ListCapabilitiesCmd.java | 1 + .../api/response/CapabilitiesResponse.java | 8 ++++++++ .../java/com/cloud/server/ManagementServerImpl.java | 2 ++ .../src/main/java/com/cloud/vm/UserVmManager.java | 3 +++ .../main/java/com/cloud/vm/UserVmManagerImpl.java | 13 ++++++++++++- ui/src/config/section/compute.js | 5 ++++- 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java index cf25dfaf5b54..fba0380e94fb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java @@ -55,6 +55,7 @@ public void execute() { response.setAllowUserExpungeRecoverVM((Boolean)capabilities.get("allowUserExpungeRecoverVM")); response.setAllowUserExpungeRecoverVolume((Boolean)capabilities.get("allowUserExpungeRecoverVolume")); response.setAllowUserViewAllDomainAccounts((Boolean)capabilities.get("allowUserViewAllDomainAccounts")); + response.setAllowUserForceStopVM((Boolean)capabilities.get("allowUserForceStopVM")); response.setKubernetesServiceEnabled((Boolean)capabilities.get("kubernetesServiceEnabled")); response.setKubernetesClusterExperimentalFeaturesEnabled((Boolean)capabilities.get("kubernetesClusterExperimentalFeaturesEnabled")); response.setCustomHypervisorDisplayName((String) capabilities.get("customHypervisorDisplayName")); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java index e4224c85e970..0d754bb2e483 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java @@ -92,6 +92,10 @@ public class CapabilitiesResponse extends BaseResponse { @Param(description = "true if users can see all accounts within the same domain, false otherwise") private boolean allowUserViewAllDomainAccounts; + @SerializedName("allowuserforcestopvm") + @Param(description = "true if users are allowed to force stop a vm, false otherwise", since = "4.20.0") + private boolean allowUserForceStopVM; + @SerializedName("kubernetesserviceenabled") @Param(description = "true if Kubernetes Service plugin is enabled, false otherwise") private boolean kubernetesServiceEnabled; @@ -192,6 +196,10 @@ public void setAllowUserViewAllDomainAccounts(boolean allowUserViewAllDomainAcco this.allowUserViewAllDomainAccounts = allowUserViewAllDomainAccounts; } + public void setAllowUserForceStopVM(boolean allowUserForceStopVM) { + this.allowUserForceStopVM = allowUserForceStopVM; + } + public void setKubernetesServiceEnabled(boolean kubernetesServiceEnabled) { this.kubernetesServiceEnabled = kubernetesServiceEnabled; } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 68a3371bda8e..0188db036937 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -4459,6 +4459,7 @@ public Map listCapabilities(final ListCapabilitiesCmd cmd) { final boolean allowUserViewDestroyedVM = (QueryService.AllowUserViewDestroyedVM.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId())); final boolean allowUserExpungeRecoverVM = (UserVmManager.AllowUserExpungeRecoverVm.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId())); final boolean allowUserExpungeRecoverVolume = (VolumeApiServiceImpl.AllowUserExpungeRecoverVolume.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId())); + final boolean allowUserForceStopVM = (UserVmManager.AllowUserForceStopVm.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId())); final boolean allowUserViewAllDomainAccounts = (QueryService.AllowUserViewAllDomainAccounts.valueIn(caller.getDomainId())); @@ -4486,6 +4487,7 @@ public Map listCapabilities(final ListCapabilitiesCmd cmd) { capabilities.put("allowUserExpungeRecoverVM", allowUserExpungeRecoverVM); capabilities.put("allowUserExpungeRecoverVolume", allowUserExpungeRecoverVolume); capabilities.put("allowUserViewAllDomainAccounts", allowUserViewAllDomainAccounts); + capabilities.put("allowUserForceStopVM", allowUserForceStopVM); capabilities.put("kubernetesServiceEnabled", kubernetesServiceEnabled); capabilities.put("kubernetesClusterExperimentalFeaturesEnabled", kubernetesClusterExperimentalFeaturesEnabled); capabilities.put("customHypervisorDisplayName", HypervisorGuru.HypervisorCustomDisplayName.value()); diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java b/server/src/main/java/com/cloud/vm/UserVmManager.java index 0dc7a7bb73f2..047bc8ea6d2a 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManager.java +++ b/server/src/main/java/com/cloud/vm/UserVmManager.java @@ -51,12 +51,15 @@ public interface UserVmManager extends UserVmService { String EnableDynamicallyScaleVmCK = "enable.dynamic.scale.vm"; String AllowDiskOfferingChangeDuringScaleVmCK = "allow.diskoffering.change.during.scale.vm"; String AllowUserExpungeRecoverVmCK ="allow.user.expunge.recover.vm"; + String AllowUserForceStopVmCK = "allow.user.force.stop.vm"; ConfigKey EnableDynamicallyScaleVm = new ConfigKey("Advanced", Boolean.class, EnableDynamicallyScaleVmCK, "false", "Enables/Disables dynamically scaling a vm", true, ConfigKey.Scope.Zone); ConfigKey AllowDiskOfferingChangeDuringScaleVm = new ConfigKey("Advanced", Boolean.class, AllowDiskOfferingChangeDuringScaleVmCK, "false", "Determines whether to allow or disallow disk offering change for root volume during scaling of a stopped or running vm", true, ConfigKey.Scope.Zone); ConfigKey AllowUserExpungeRecoverVm = new ConfigKey("Advanced", Boolean.class, AllowUserExpungeRecoverVmCK, "false", "Determines whether users can expunge or recover their vm", true, ConfigKey.Scope.Account); + ConfigKey AllowUserForceStopVm = new ConfigKey("Advanced", Boolean.class, AllowUserForceStopVmCK, "true", + "Determines whether users are allowed to force stop a vm", true, ConfigKey.Scope.Account); ConfigKey DisplayVMOVFProperties = new ConfigKey("Advanced", Boolean.class, "vm.display.ovf.properties", "false", "Set display of VMs OVF properties as part of VM details", true); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index bf100c8c0d21..6dc57713a9db 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -5341,6 +5341,13 @@ protected void updateVncPasswordIfItHasChanged(String originalVncPassword, Strin public void finalizeExpunge(VirtualMachine vm) { } + private void checkForceStopVmPermission (Account callingAccount) { + if (!AllowUserForceStopVm.valueIn(callingAccount.getId())) { + logger.error(String.format("Parameter [%s] can only be passed by Admin accounts or when the allow.user.force.stop.vm key is true.", ApiConstants.FORCED)); + throw new PermissionDeniedException("Account does not have the permission to force stop the vm."); + } + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VM_STOP, eventDescription = "stopping Vm", async = true) public UserVm stopVirtualMachine(long vmId, boolean forced) throws ConcurrentOperationException { @@ -5358,6 +5365,10 @@ public UserVm stopVirtualMachine(long vmId, boolean forced) throws ConcurrentOpe throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); } + if (forced) { + checkForceStopVmPermission (caller); + } + // check if vm belongs to AutoScale vm group in Disabled state autoScaleManager.checkIfVmActionAllowed(vmId); @@ -8460,7 +8471,7 @@ public ConfigKey[] getConfigKeys() { return new ConfigKey[] {EnableDynamicallyScaleVm, AllowDiskOfferingChangeDuringScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax, VmIpFetchThreadPoolMax, VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig, DisplayVMOVFProperties, KvmAdditionalConfigAllowList, XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList, DestroyRootVolumeOnVmDestruction, - EnforceStrictResourceLimitHostTagCheck, StrictHostTags}; + EnforceStrictResourceLimitHostTagCheck, StrictHostTags, AllowUserForceStopVm}; } @Override diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 4c5a61e3bdcf..7c7480a7b751 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -133,7 +133,10 @@ export default { dataView: true, groupAction: true, groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) }, - args: ['forced'], + args: (record, store, group) => { + return (['Admin'].includes(store.userInfo.roletype) || store.features.allowuserforcestopvm) + ? ['forced'] : [] + }, show: (record) => { return ['Running'].includes(record.state) } }, { From 28039ff0befaed49b12a80ff9b15201581f103ba Mon Sep 17 00:00:00 2001 From: abh1sar Date: Thu, 22 Aug 2024 15:37:21 +0530 Subject: [PATCH 2/5] remove extra space before method call --- server/src/main/java/com/cloud/vm/UserVmManagerImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 6dc57713a9db..b5aff993b1c3 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -5341,7 +5341,7 @@ protected void updateVncPasswordIfItHasChanged(String originalVncPassword, Strin public void finalizeExpunge(VirtualMachine vm) { } - private void checkForceStopVmPermission (Account callingAccount) { + private void checkForceStopVmPermission(Account callingAccount) { if (!AllowUserForceStopVm.valueIn(callingAccount.getId())) { logger.error(String.format("Parameter [%s] can only be passed by Admin accounts or when the allow.user.force.stop.vm key is true.", ApiConstants.FORCED)); throw new PermissionDeniedException("Account does not have the permission to force stop the vm."); @@ -5366,7 +5366,7 @@ public UserVm stopVirtualMachine(long vmId, boolean forced) throws ConcurrentOpe } if (forced) { - checkForceStopVmPermission (caller); + checkForceStopVmPermission(caller); } // check if vm belongs to AutoScale vm group in Disabled state From 7eb84df097d17f42070950b88457b9ab7769bdee Mon Sep 17 00:00:00 2001 From: abh1sar Date: Fri, 23 Aug 2024 14:37:15 +0530 Subject: [PATCH 3/5] moved allowuserforcestopvm to ApiConstants --- api/src/main/java/org/apache/cloudstack/api/ApiConstants.java | 1 + .../cloudstack/api/command/user/config/ListCapabilitiesCmd.java | 2 +- .../apache/cloudstack/api/response/CapabilitiesResponse.java | 2 +- server/src/main/java/com/cloud/server/ManagementServerImpl.java | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 2f0e4f16797d..96034e0ba8b0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -30,6 +30,7 @@ public class ApiConstants { public static final String ALGORITHM = "algorithm"; public static final String ALIAS = "alias"; public static final String ALLOCATED_ONLY = "allocatedonly"; + public static final String ALLOW_USER_FORCE_STOP_VM = "allowuserforcestopvm"; public static final String ANNOTATION = "annotation"; public static final String API_KEY = "apikey"; public static final String ARCHIVED = "archived"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java index fba0380e94fb..a807d2ad8376 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java @@ -55,7 +55,7 @@ public void execute() { response.setAllowUserExpungeRecoverVM((Boolean)capabilities.get("allowUserExpungeRecoverVM")); response.setAllowUserExpungeRecoverVolume((Boolean)capabilities.get("allowUserExpungeRecoverVolume")); response.setAllowUserViewAllDomainAccounts((Boolean)capabilities.get("allowUserViewAllDomainAccounts")); - response.setAllowUserForceStopVM((Boolean)capabilities.get("allowUserForceStopVM")); + response.setAllowUserForceStopVM((Boolean)capabilities.get(ApiConstants.ALLOW_USER_FORCE_STOP_VM)); response.setKubernetesServiceEnabled((Boolean)capabilities.get("kubernetesServiceEnabled")); response.setKubernetesClusterExperimentalFeaturesEnabled((Boolean)capabilities.get("kubernetesClusterExperimentalFeaturesEnabled")); response.setCustomHypervisorDisplayName((String) capabilities.get("customHypervisorDisplayName")); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java index 0d754bb2e483..162386ee0421 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java @@ -92,7 +92,7 @@ public class CapabilitiesResponse extends BaseResponse { @Param(description = "true if users can see all accounts within the same domain, false otherwise") private boolean allowUserViewAllDomainAccounts; - @SerializedName("allowuserforcestopvm") + @SerializedName(ApiConstants.ALLOW_USER_FORCE_STOP_VM) @Param(description = "true if users are allowed to force stop a vm, false otherwise", since = "4.20.0") private boolean allowUserForceStopVM; diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 0188db036937..9ba5b078391d 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -4487,7 +4487,7 @@ public Map listCapabilities(final ListCapabilitiesCmd cmd) { capabilities.put("allowUserExpungeRecoverVM", allowUserExpungeRecoverVM); capabilities.put("allowUserExpungeRecoverVolume", allowUserExpungeRecoverVolume); capabilities.put("allowUserViewAllDomainAccounts", allowUserViewAllDomainAccounts); - capabilities.put("allowUserForceStopVM", allowUserForceStopVM); + capabilities.put(ApiConstants.ALLOW_USER_FORCE_STOP_VM, allowUserForceStopVM); capabilities.put("kubernetesServiceEnabled", kubernetesServiceEnabled); capabilities.put("kubernetesClusterExperimentalFeaturesEnabled", kubernetesClusterExperimentalFeaturesEnabled); capabilities.put("customHypervisorDisplayName", HypervisorGuru.HypervisorCustomDisplayName.value()); From 9245206bb7a9714036939b1e0ec0126624c0b0ae Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Thu, 29 Aug 2024 10:18:34 +0530 Subject: [PATCH 4/5] Update server/src/main/java/com/cloud/vm/UserVmManagerImpl.java Co-authored-by: Suresh Kumar Anaparti --- server/src/main/java/com/cloud/vm/UserVmManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index b5aff993b1c3..fde6c832de7e 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -5343,7 +5343,7 @@ public void finalizeExpunge(VirtualMachine vm) { private void checkForceStopVmPermission(Account callingAccount) { if (!AllowUserForceStopVm.valueIn(callingAccount.getId())) { - logger.error(String.format("Parameter [%s] can only be passed by Admin accounts or when the allow.user.force.stop.vm key is true.", ApiConstants.FORCED)); + logger.error(String.format("Parameter [%s] can only be passed by Admin accounts or when the allow.user.force.stop.vm config is true for the account.", ApiConstants.FORCED)); throw new PermissionDeniedException("Account does not have the permission to force stop the vm."); } } From 1ccb94fa5f8ab10333ecec57b39244a0168ca7d8 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 5 Sep 2024 09:45:16 +0530 Subject: [PATCH 5/5] Update server/src/main/java/com/cloud/vm/UserVmManagerImpl.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bernardo De Marco Gonçalves --- server/src/main/java/com/cloud/vm/UserVmManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index fde6c832de7e..e14df77cea17 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -5343,7 +5343,7 @@ public void finalizeExpunge(VirtualMachine vm) { private void checkForceStopVmPermission(Account callingAccount) { if (!AllowUserForceStopVm.valueIn(callingAccount.getId())) { - logger.error(String.format("Parameter [%s] can only be passed by Admin accounts or when the allow.user.force.stop.vm config is true for the account.", ApiConstants.FORCED)); + logger.error("Parameter [{}] can only be passed by Admin accounts or when the allow.user.force.stop.vm config is true for the account.", ApiConstants.FORCED); throw new PermissionDeniedException("Account does not have the permission to force stop the vm."); } }