This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new b7a2b0419cc server: Global setting to allow/disallow users to force 
stop a vm (#9569)
b7a2b0419cc is described below

commit b7a2b0419ccee96fb80cc43dac9a711927b7f39d
Author: Abhisar Sinha <[email protected]>
AuthorDate: Thu Sep 5 09:46:45 2024 +0530

    server: Global setting to allow/disallow users to force stop a vm (#9569)
    
    Global setting to allow/disallow users to force stop a vm
    
    Fixes #6629
    
    Co-authored-by: Suresh Kumar Anaparti <[email protected]>
    Co-authored-by: Rohit Yadav <[email protected]>
    Co-authored-by: Bernardo De Marco Gonçalves <[email protected]>
---
 .../main/java/org/apache/cloudstack/api/ApiConstants.java   |  1 +
 .../api/command/user/config/ListCapabilitiesCmd.java        |  1 +
 .../cloudstack/api/response/CapabilitiesResponse.java       |  8 ++++++++
 .../main/java/com/cloud/server/ManagementServerImpl.java    |  2 ++
 server/src/main/java/com/cloud/vm/UserVmManager.java        |  3 +++
 server/src/main/java/com/cloud/vm/UserVmManagerImpl.java    | 13 ++++++++++++-
 ui/src/config/section/compute.js                            |  5 ++++-
 7 files changed, 31 insertions(+), 2 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 2f0e4f16797..96034e0ba8b 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 cf25dfaf5b5..a807d2ad837 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 class ListCapabilitiesCmd extends BaseCmd {
         
response.setAllowUserExpungeRecoverVM((Boolean)capabilities.get("allowUserExpungeRecoverVM"));
         
response.setAllowUserExpungeRecoverVolume((Boolean)capabilities.get("allowUserExpungeRecoverVolume"));
         
response.setAllowUserViewAllDomainAccounts((Boolean)capabilities.get("allowUserViewAllDomainAccounts"));
+        
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 e4224c85e97..162386ee042 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(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;
+
     @SerializedName("kubernetesserviceenabled")
     @Param(description = "true if Kubernetes Service plugin is enabled, false 
otherwise")
     private boolean kubernetesServiceEnabled;
@@ -192,6 +196,10 @@ public class CapabilitiesResponse extends BaseResponse {
         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 fb3cf9bf193..fabf82e5182 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -4461,6 +4461,7 @@ public class ManagementServerImpl extends ManagerBase 
implements ManagementServe
         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()));
 
@@ -4488,6 +4489,7 @@ public class ManagementServerImpl extends ManagerBase 
implements ManagementServe
         capabilities.put("allowUserExpungeRecoverVM", 
allowUserExpungeRecoverVM);
         capabilities.put("allowUserExpungeRecoverVolume", 
allowUserExpungeRecoverVolume);
         capabilities.put("allowUserViewAllDomainAccounts", 
allowUserViewAllDomainAccounts);
+        capabilities.put(ApiConstants.ALLOW_USER_FORCE_STOP_VM, 
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 0dc7a7bb73f..047bc8ea6d2 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<Boolean> EnableDynamicallyScaleVm = new 
ConfigKey<Boolean>("Advanced", Boolean.class, EnableDynamicallyScaleVmCK, 
"false",
         "Enables/Disables dynamically scaling a vm", true, 
ConfigKey.Scope.Zone);
     ConfigKey<Boolean> AllowDiskOfferingChangeDuringScaleVm = new 
ConfigKey<Boolean>("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<Boolean> AllowUserExpungeRecoverVm = new 
ConfigKey<Boolean>("Advanced", Boolean.class, AllowUserExpungeRecoverVmCK, 
"false",
         "Determines whether users can expunge or recover their vm", true, 
ConfigKey.Scope.Account);
+    ConfigKey<Boolean> AllowUserForceStopVm = new 
ConfigKey<Boolean>("Advanced", Boolean.class, AllowUserForceStopVmCK, "true",
+            "Determines whether users are allowed to force stop a vm", true, 
ConfigKey.Scope.Account);
     ConfigKey<Boolean> DisplayVMOVFProperties = new 
ConfigKey<Boolean>("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 22ef809e5da..0dc0e84cbd4 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -5348,6 +5348,13 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
     public void finalizeExpunge(VirtualMachine vm) {
     }
 
+    private void checkForceStopVmPermission(Account callingAccount) {
+        if (!AllowUserForceStopVm.valueIn(callingAccount.getId())) {
+            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.");
+        }
+    }
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VM_STOP, eventDescription = 
"stopping Vm", async = true)
     public UserVm stopVirtualMachine(long vmId, boolean forced) throws 
ConcurrentOperationException {
@@ -5365,6 +5372,10 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
             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);
 
@@ -8467,7 +8478,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         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 4c5a61e3bdc..7c7480a7b75 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) }
         },
         {

Reply via email to