Skip to content

Commit b7a2b04

Browse files
abh1sarsureshanapartiyadvrbernardodemarco
authored
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 <sureshkumar.anaparti@gmail.com> Co-authored-by: Rohit Yadav <rohityadav89@gmail.com> Co-authored-by: Bernardo De Marco Gonçalves <bernardomg2004@gmail.com>
1 parent 36d37f7 commit b7a2b04

File tree

7 files changed

+31
-2
lines changed

7 files changed

+31
-2
lines changed

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class ApiConstants {
3030
public static final String ALGORITHM = "algorithm";
3131
public static final String ALIAS = "alias";
3232
public static final String ALLOCATED_ONLY = "allocatedonly";
33+
public static final String ALLOW_USER_FORCE_STOP_VM = "allowuserforcestopvm";
3334
public static final String ANNOTATION = "annotation";
3435
public static final String API_KEY = "apikey";
3536
public static final String ARCHIVED = "archived";

api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public void execute() {
5555
response.setAllowUserExpungeRecoverVM((Boolean)capabilities.get("allowUserExpungeRecoverVM"));
5656
response.setAllowUserExpungeRecoverVolume((Boolean)capabilities.get("allowUserExpungeRecoverVolume"));
5757
response.setAllowUserViewAllDomainAccounts((Boolean)capabilities.get("allowUserViewAllDomainAccounts"));
58+
response.setAllowUserForceStopVM((Boolean)capabilities.get(ApiConstants.ALLOW_USER_FORCE_STOP_VM));
5859
response.setKubernetesServiceEnabled((Boolean)capabilities.get("kubernetesServiceEnabled"));
5960
response.setKubernetesClusterExperimentalFeaturesEnabled((Boolean)capabilities.get("kubernetesClusterExperimentalFeaturesEnabled"));
6061
response.setCustomHypervisorDisplayName((String) capabilities.get("customHypervisorDisplayName"));

api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ public class CapabilitiesResponse extends BaseResponse {
9292
@Param(description = "true if users can see all accounts within the same domain, false otherwise")
9393
private boolean allowUserViewAllDomainAccounts;
9494

95+
@SerializedName(ApiConstants.ALLOW_USER_FORCE_STOP_VM)
96+
@Param(description = "true if users are allowed to force stop a vm, false otherwise", since = "4.20.0")
97+
private boolean allowUserForceStopVM;
98+
9599
@SerializedName("kubernetesserviceenabled")
96100
@Param(description = "true if Kubernetes Service plugin is enabled, false otherwise")
97101
private boolean kubernetesServiceEnabled;
@@ -192,6 +196,10 @@ public void setAllowUserViewAllDomainAccounts(boolean allowUserViewAllDomainAcco
192196
this.allowUserViewAllDomainAccounts = allowUserViewAllDomainAccounts;
193197
}
194198

199+
public void setAllowUserForceStopVM(boolean allowUserForceStopVM) {
200+
this.allowUserForceStopVM = allowUserForceStopVM;
201+
}
202+
195203
public void setKubernetesServiceEnabled(boolean kubernetesServiceEnabled) {
196204
this.kubernetesServiceEnabled = kubernetesServiceEnabled;
197205
}

server/src/main/java/com/cloud/server/ManagementServerImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4461,6 +4461,7 @@ public Map<String, Object> listCapabilities(final ListCapabilitiesCmd cmd) {
44614461
final boolean allowUserViewDestroyedVM = (QueryService.AllowUserViewDestroyedVM.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId()));
44624462
final boolean allowUserExpungeRecoverVM = (UserVmManager.AllowUserExpungeRecoverVm.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId()));
44634463
final boolean allowUserExpungeRecoverVolume = (VolumeApiServiceImpl.AllowUserExpungeRecoverVolume.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId()));
4464+
final boolean allowUserForceStopVM = (UserVmManager.AllowUserForceStopVm.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId()));
44644465

44654466
final boolean allowUserViewAllDomainAccounts = (QueryService.AllowUserViewAllDomainAccounts.valueIn(caller.getDomainId()));
44664467

@@ -4488,6 +4489,7 @@ public Map<String, Object> listCapabilities(final ListCapabilitiesCmd cmd) {
44884489
capabilities.put("allowUserExpungeRecoverVM", allowUserExpungeRecoverVM);
44894490
capabilities.put("allowUserExpungeRecoverVolume", allowUserExpungeRecoverVolume);
44904491
capabilities.put("allowUserViewAllDomainAccounts", allowUserViewAllDomainAccounts);
4492+
capabilities.put(ApiConstants.ALLOW_USER_FORCE_STOP_VM, allowUserForceStopVM);
44914493
capabilities.put("kubernetesServiceEnabled", kubernetesServiceEnabled);
44924494
capabilities.put("kubernetesClusterExperimentalFeaturesEnabled", kubernetesClusterExperimentalFeaturesEnabled);
44934495
capabilities.put("customHypervisorDisplayName", HypervisorGuru.HypervisorCustomDisplayName.value());

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,15 @@ public interface UserVmManager extends UserVmService {
5151
String EnableDynamicallyScaleVmCK = "enable.dynamic.scale.vm";
5252
String AllowDiskOfferingChangeDuringScaleVmCK = "allow.diskoffering.change.during.scale.vm";
5353
String AllowUserExpungeRecoverVmCK ="allow.user.expunge.recover.vm";
54+
String AllowUserForceStopVmCK = "allow.user.force.stop.vm";
5455
ConfigKey<Boolean> EnableDynamicallyScaleVm = new ConfigKey<Boolean>("Advanced", Boolean.class, EnableDynamicallyScaleVmCK, "false",
5556
"Enables/Disables dynamically scaling a vm", true, ConfigKey.Scope.Zone);
5657
ConfigKey<Boolean> AllowDiskOfferingChangeDuringScaleVm = new ConfigKey<Boolean>("Advanced", Boolean.class, AllowDiskOfferingChangeDuringScaleVmCK, "false",
5758
"Determines whether to allow or disallow disk offering change for root volume during scaling of a stopped or running vm", true, ConfigKey.Scope.Zone);
5859
ConfigKey<Boolean> AllowUserExpungeRecoverVm = new ConfigKey<Boolean>("Advanced", Boolean.class, AllowUserExpungeRecoverVmCK, "false",
5960
"Determines whether users can expunge or recover their vm", true, ConfigKey.Scope.Account);
61+
ConfigKey<Boolean> AllowUserForceStopVm = new ConfigKey<Boolean>("Advanced", Boolean.class, AllowUserForceStopVmCK, "true",
62+
"Determines whether users are allowed to force stop a vm", true, ConfigKey.Scope.Account);
6063
ConfigKey<Boolean> DisplayVMOVFProperties = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.display.ovf.properties", "false",
6164
"Set display of VMs OVF properties as part of VM details", true);
6265

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5348,6 +5348,13 @@ protected void updateVncPasswordIfItHasChanged(String originalVncPassword, Strin
53485348
public void finalizeExpunge(VirtualMachine vm) {
53495349
}
53505350

5351+
private void checkForceStopVmPermission(Account callingAccount) {
5352+
if (!AllowUserForceStopVm.valueIn(callingAccount.getId())) {
5353+
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);
5354+
throw new PermissionDeniedException("Account does not have the permission to force stop the vm.");
5355+
}
5356+
}
5357+
53515358
@Override
53525359
@ActionEvent(eventType = EventTypes.EVENT_VM_STOP, eventDescription = "stopping Vm", async = true)
53535360
public UserVm stopVirtualMachine(long vmId, boolean forced) throws ConcurrentOperationException {
@@ -5365,6 +5372,10 @@ public UserVm stopVirtualMachine(long vmId, boolean forced) throws ConcurrentOpe
53655372
throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
53665373
}
53675374

5375+
if (forced) {
5376+
checkForceStopVmPermission(caller);
5377+
}
5378+
53685379
// check if vm belongs to AutoScale vm group in Disabled state
53695380
autoScaleManager.checkIfVmActionAllowed(vmId);
53705381

@@ -8467,7 +8478,7 @@ public ConfigKey<?>[] getConfigKeys() {
84678478
return new ConfigKey<?>[] {EnableDynamicallyScaleVm, AllowDiskOfferingChangeDuringScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax,
84688479
VmIpFetchThreadPoolMax, VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig, DisplayVMOVFProperties,
84698480
KvmAdditionalConfigAllowList, XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList, DestroyRootVolumeOnVmDestruction,
8470-
EnforceStrictResourceLimitHostTagCheck, StrictHostTags};
8481+
EnforceStrictResourceLimitHostTagCheck, StrictHostTags, AllowUserForceStopVm};
84718482
}
84728483

84738484
@Override

ui/src/config/section/compute.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ export default {
133133
dataView: true,
134134
groupAction: true,
135135
groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) },
136-
args: ['forced'],
136+
args: (record, store, group) => {
137+
return (['Admin'].includes(store.userInfo.roletype) || store.features.allowuserforcestopvm)
138+
? ['forced'] : []
139+
},
137140
show: (record) => { return ['Running'].includes(record.state) }
138141
},
139142
{

0 commit comments

Comments
 (0)