Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6fb1551
framework,api,server,ui: improve error messaging
shwstppr Dec 24, 2025
f86b32c
fix
shwstppr Dec 24, 2025
badada1
fix
shwstppr Dec 26, 2025
d535dbc
wip
shwstppr Dec 29, 2025
1036df8
changes
shwstppr Jan 6, 2026
6878f49
handle resourceallocationexception
shwstppr Jan 6, 2026
57a8ef2
fix eof
shwstppr Jan 6, 2026
a9cdfc8
revert
shwstppr Jan 6, 2026
2e51647
use separate errorcontext
shwstppr Jan 6, 2026
d8bc647
fix
shwstppr Jun 12, 2026
dbad2e5
refactor
shwstppr Jun 16, 2026
5a4c449
wip
shwstppr Jun 17, 2026
1a4a511
framework changes
shwstppr Jun 25, 2026
ca13a80
refactor handling ResourceAllocationException
shwstppr Jun 25, 2026
c5acf33
refactor
shwstppr Jun 25, 2026
95b9844
Merge remote-tracking branch 'apache/4.22' into improve-error-messages
shwstppr Jun 25, 2026
ab203d8
build fix
shwstppr Jun 25, 2026
bb6518f
refactor
shwstppr Jun 25, 2026
f5cb3e2
Merge remote-tracking branch 'apache/main' into improve-error-messages
shwstppr Jun 29, 2026
3c948a9
wip: vm flow error changes UserVmManagerImpl
shwstppr Jun 25, 2026
68d69bd
wip: VirtualMachineManagerImpl changes
shwstppr Jun 25, 2026
473a3f8
fix
shwstppr Jun 29, 2026
0c56d6c
umnanagedvmsmanagerimpl and fixes
shwstppr Jun 29, 2026
e71c522
error fixes
shwstppr Jun 29, 2026
97a4629
fix
shwstppr Jun 30, 2026
1c16372
Merge remote-tracking branch 'apache/main' into improve-error-messages
shwstppr Jun 30, 2026
c2dc7f1
changes
shwstppr Jun 30, 2026
153bfe4
error msg tool
shwstppr Jun 30, 2026
02b5fd8
build fix
shwstppr Jun 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 24 additions & 18 deletions api/src/main/java/com/cloud/configuration/Resource.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,39 +22,45 @@ public interface Resource {
String UNLIMITED = "Unlimited";

enum ResourceType { // All storage type resources are allocated_storage and not the physical storage.
user_vm("user_vm", 0),
public_ip("public_ip", 1),
volume("volume", 2),
snapshot("snapshot", 3),
template("template", 4),
project("project", 5),
network("network", 6),
vpc("vpc", 7),
cpu("cpu", 8),
memory("memory", 9),
primary_storage("primary_storage", 10),
secondary_storage("secondary_storage", 11),
backup("backup", 12),
backup_storage("backup_storage", 13),
bucket("bucket", 14),
object_storage("object_storage", 15),
gpu("gpu", 16);
user_vm("user_vm", "Instance", 0),
public_ip("public_ip", "Public IP", 1),
volume("volume", "Volume", 2),
snapshot("snapshot", "Snapshot", 3),
template("template", "Template", 4),
project("project", "Project", 5),
network("network", "Network", 6),
vpc("vpc", "VPC", 7),
cpu("cpu", "CPU", 8),
memory("memory", "Memory", 9),
primary_storage("primary_storage", "Primary Storage", 10),
secondary_storage("secondary_storage", "Secondary Storage", 11),
backup("backup", "Backup", 12),
backup_storage("backup_storage", "Backup Storage", 13),
bucket("bucket", "Bucket", 14),
object_storage("object_storage", "Object Storage", 15),
gpu("gpu", "GPU", 16);

private String name;
private String displayName;
private int ordinal;
public static final long bytesToKiB = 1024;
public static final long bytesToMiB = bytesToKiB * 1024;
public static final long bytesToGiB = bytesToMiB * 1024;

ResourceType(String name, int ordinal) {
ResourceType(String name, String displayName, int ordinal) {
this.name = name;
this.displayName = displayName;
this.ordinal = ordinal;
}

public String getName() {
return name;
}

public String getDisplayName() {
return displayName;
}

public int getOrdinal() {
return ordinal;
}
Expand Down
20 changes: 11 additions & 9 deletions api/src/main/java/com/cloud/user/AccountService.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.List;
import java.util.Map;

import com.cloud.utils.Pair;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.RolePermissionEntity;
import org.apache.cloudstack.acl.RoleType;
Expand All @@ -28,14 +27,6 @@
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;

import com.cloud.dc.DataCenter;
import com.cloud.domain.Domain;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.network.vpc.VpcOffering;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.NetworkOffering;
import com.cloud.offering.ServiceOffering;
import org.apache.cloudstack.api.command.admin.user.DeleteUserKeysCmd;
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
import org.apache.cloudstack.api.command.admin.user.ListUserKeyRulesCmd;
Expand All @@ -47,6 +38,15 @@
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
import org.apache.cloudstack.backup.BackupOffering;

import com.cloud.dc.DataCenter;
import com.cloud.domain.Domain;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.network.vpc.VpcOffering;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.NetworkOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.utils.Pair;

public interface AccountService {

/**
Expand Down Expand Up @@ -102,6 +102,8 @@ User createUser(String userName, String password, String firstName, String lastN

boolean isRootAdmin(Long accountId);

boolean isRootAdmin(Account account);

boolean isDomainAdmin(Long accountId);

boolean isResourceDomainAdmin(Long accountId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.apache.cloudstack.api.response.UserDataResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.error.Exceptions;
import org.apache.cloudstack.kms.KMSKey;
import org.apache.cloudstack.vm.lease.VMLeaseManager;
import org.apache.commons.collections.CollectionUtils;
Expand Down Expand Up @@ -672,7 +673,8 @@ private Long getNetworkIdFomIpMap(HashMap<String, String> ips) {
try {
networkId = Long.parseLong(networkid);
} catch (NumberFormatException e) {
throw new InvalidParameterValueException("Unable to translate and find entity with networkId: " + networkid);
throw Exceptions.invalidParameterValueException("vm.deploy.network.not.found.ip.map",
Map.of("networkId", networkid));
}
}
return networkId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VolumeResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.error.Exceptions;

import com.cloud.configuration.Resource;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InsufficientServerCapacityException;
Expand Down Expand Up @@ -264,7 +266,19 @@ public void create() throws ResourceAllocationException {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
} catch (ResourceAllocationException ex) {
logger.warn("Exception: ", ex);
throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage());
handleCreateResourceAllocationException(ex);
}
}

protected void handleCreateResourceAllocationException(ResourceAllocationException ex) {
Object cause = CallContext.current().getErrorContextParameters().get("resourceLimitCause");
if (Resource.ResourceOwnerType.Account.equals(cause)) {
throw Exceptions.serverApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR,
"vm.deploy.resourcelimit.exceeded.account");
} else if (Resource.ResourceOwnerType.Domain.equals(cause)) {
throw Exceptions.serverApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR,
"vm.deploy.resourcelimit.exceeded.domain");
}
throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@

import java.util.ArrayList;
import java.util.List;

import com.google.gson.annotations.SerializedName;
import java.util.Map;

import org.apache.cloudstack.api.BaseResponse;

import com.cloud.serializer.Param;
import com.cloud.utils.exception.ExceptionProxyObject;
import com.google.gson.annotations.SerializedName;

public class ExceptionResponse extends BaseResponse {

Expand All @@ -44,6 +44,14 @@ public class ExceptionResponse extends BaseResponse {
@Param(description = "The text associated with this error")
private String errorText = "Command failed due to Internal Server Error";

@SerializedName("errortextkey")
@Param(description = "the key for the text associated with this error", since = "4.24.0")
private String errorTextKey;

@SerializedName("errormetadata")
@Param(description = "the metadata associated with this error", since = "4.24.0")
private Map<String, String> errorMetadata;

public ExceptionResponse() {
idList = new ArrayList<ExceptionProxyObject>();
}
Expand All @@ -64,6 +72,22 @@ public void setErrorText(String errorText) {
this.errorText = errorText;
}

public String getErrorTextKey() {
return errorTextKey;
}

public void setErrorTextKey(String errorTextKey) {
this.errorTextKey = errorTextKey;
}

public Map<String, String> getErrorMetadata() {
return errorMetadata;
}

public void setErrorMetadata(Map<String, String> errorMetadata) {
this.errorMetadata = errorMetadata;
}

public void addProxyObject(ExceptionProxyObject id) {
idList.add(id);
return;
Expand Down
43 changes: 41 additions & 2 deletions api/src/main/java/org/apache/cloudstack/context/CallContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,21 @@

import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.managed.threadlocal.ManagedThreadLocal;
import org.apache.logging.log4j.Logger;
import org.apache.commons.collections.MapUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;

import com.cloud.exception.CloudAuthenticationException;
import com.cloud.projects.Project;
import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.user.User;
import com.cloud.utils.UuidUtils;
import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.logging.log4j.ThreadContext;

/**
* CallContext records information about the environment the call is made. This
Expand All @@ -53,6 +57,7 @@ protected Stack<CallContext> initialValue() {
private String contextId;
private Account account;
private long accountId;
private Boolean isAccountRootAdmin = null;
private long startEventId = 0;
private String eventDescription;
private String eventDetails;
Expand All @@ -64,6 +69,7 @@ protected Stack<CallContext> initialValue() {
private long userId;
private final Map<Object, Object> context = new HashMap<Object, Object>();
private final Map<String, String> apiResourcesUuids = new HashMap<>();
private final Map<String, Object> errorContext = new HashMap<>();
private Project project;
private String apiName;

Expand Down Expand Up @@ -135,6 +141,24 @@ public Account getCallingAccount() {
return account;
}

public boolean isCallingAccountRootAdmin() {
if (isAccountRootAdmin == null) {
if (account == null && s_entityMgr == null) {
return false;
}
Account caller = getCallingAccount();
AccountService accountService;
try {
accountService = ComponentContext.getDelegateComponentOfType(AccountService.class);
} catch (NoSuchBeanDefinitionException e) {
LOGGER.warn("Falling back to account type check for isRootAdmin for account ID: {} as no AccountService bean found: {}", accountId, e.getMessage());
return caller != null && caller.getType() == Account.Type.ADMIN;
}
isAccountRootAdmin = accountService.isRootAdmin(caller);
}
return isAccountRootAdmin;
}

public static CallContext current() {
CallContext context = s_currentContext.get();

Expand Down Expand Up @@ -408,6 +432,21 @@ public void putContextParameters(Map<Object, Object> details){
}
}

public Map<String, Object> getErrorContextParameters() {
return errorContext;
}

public void putErrorContextParameter(String key, Object value) {
errorContext.put(key, value);
}

public void putErrorContextParameters(Map<String, Object> details) {
if (MapUtils.isEmpty(details)) {
return;
}
errorContext.putAll(details);
}

public static void setActionEventInfo(String eventType, String description) {
CallContext context = CallContext.current();
if (context != null) {
Expand Down
Loading