mirror of https://github.com/apache/cloudstack.git
framework,api,server,ui: improve error messaging
Framwork level improvements for for contextual and localized API error messages More details to be added Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
parent
8e933b735e
commit
6fb1551b84
|
|
@ -16,6 +16,10 @@
|
|||
// under the License.
|
||||
package com.cloud.exception;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.context.ErrorMessageResolver;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
public class InvalidParameterValueException extends CloudRuntimeException {
|
||||
|
|
@ -26,4 +30,8 @@ public class InvalidParameterValueException extends CloudRuntimeException {
|
|||
super(message);
|
||||
}
|
||||
|
||||
public InvalidParameterValueException(String key, Map<String, Object> metadata) {
|
||||
super(ErrorMessageResolver.getMessage(key, metadata), key, metadata);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package com.cloud.user;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
|
|
@ -27,6 +26,7 @@ import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
|
|||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.RegisterUserKeyCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.domain.Domain;
|
||||
|
|
@ -35,7 +35,7 @@ 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.auth.UserTwoFactorAuthenticator;
|
||||
import com.cloud.utils.Pair;
|
||||
|
||||
public interface AccountService {
|
||||
|
||||
|
|
@ -85,6 +85,8 @@ public interface AccountService {
|
|||
|
||||
boolean isRootAdmin(Long accountId);
|
||||
|
||||
boolean isRootAdmin(Account account);
|
||||
|
||||
boolean isDomainAdmin(Long accountId);
|
||||
|
||||
boolean isResourceDomainAdmin(Long accountId);
|
||||
|
|
|
|||
|
|
@ -648,7 +648,8 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme
|
|||
try {
|
||||
networkId = Long.parseLong(networkid);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InvalidParameterValueException("Unable to translate and find entity with networkId: " + networkid);
|
||||
throw new InvalidParameterValueException("vm.deploy.network.not.found.ip.map",
|
||||
Map.of("networkId", networkid));
|
||||
}
|
||||
}
|
||||
return networkId;
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ package org.apache.cloudstack.api.response;
|
|||
|
||||
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 {
|
||||
|
||||
|
|
@ -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")
|
||||
private String errorTextKey = "";
|
||||
|
||||
@SerializedName("errormetadata")
|
||||
@Param(description = "the metadata associated with this error")
|
||||
private Map<String, String> errorMetadata;
|
||||
|
||||
public ExceptionResponse() {
|
||||
idList = new ArrayList<ExceptionProxyObject>();
|
||||
}
|
||||
|
|
@ -64,6 +72,22 @@ public class ExceptionResponse extends BaseResponse {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -23,17 +23,20 @@ import java.util.UUID;
|
|||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.managed.threadlocal.ManagedThreadLocal;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
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
|
||||
|
|
@ -53,6 +56,7 @@ public class CallContext {
|
|||
private String contextId;
|
||||
private Account account;
|
||||
private long accountId;
|
||||
private Boolean isAccountRootAdmin = null;
|
||||
private long startEventId = 0;
|
||||
private String eventDescription;
|
||||
private String eventDetails;
|
||||
|
|
@ -134,6 +138,21 @@ public class CallContext {
|
|||
return account;
|
||||
}
|
||||
|
||||
public boolean isCallingAccountRootAdmin() {
|
||||
if (isAccountRootAdmin == null) {
|
||||
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());
|
||||
Account caller = getCallingAccount();
|
||||
return caller != null && caller.getType() == Account.Type.ADMIN;
|
||||
}
|
||||
isAccountRootAdmin = accountService.isRootAdmin(getCallingAccount());
|
||||
}
|
||||
return Boolean.TRUE.equals(isAccountRootAdmin);
|
||||
}
|
||||
|
||||
public static CallContext current() {
|
||||
CallContext context = s_currentContext.get();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,241 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.cloudstack.context;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.api.response.ExceptionResponse;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.utils.PropertiesUtil;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
public final class ErrorMessageResolver {
|
||||
|
||||
private static final Logger LOG =
|
||||
LogManager.getLogger(ErrorMessageResolver.class);
|
||||
|
||||
private static final String ERROR_MESSAGES_FILENAME = "error-messages.json";
|
||||
private static final String ERROR_KEY_ADMIN_SUFFIX = ".admin";
|
||||
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
// volatile for safe publication
|
||||
private static volatile Map<String, String> templates =
|
||||
Collections.emptyMap();
|
||||
|
||||
private static volatile long lastModified = -1;
|
||||
|
||||
private ErrorMessageResolver() {
|
||||
}
|
||||
|
||||
public static String getMessage(String errorKey, Map<String, Object> metadata) {
|
||||
return getMessageUsingStringMap(errorKey, getStringMap(metadata));
|
||||
}
|
||||
|
||||
private static String getTemplateForKey(String errorKey) {
|
||||
if (errorKey == null) {
|
||||
return null;
|
||||
}
|
||||
reloadIfRequired();
|
||||
if (!errorKey.endsWith(ERROR_KEY_ADMIN_SUFFIX) && CallContext.current().isCallingAccountRootAdmin()) {
|
||||
String template = templates.get(errorKey + ERROR_KEY_ADMIN_SUFFIX);
|
||||
if (template != null) {
|
||||
return template;
|
||||
}
|
||||
}
|
||||
return templates.get(errorKey);
|
||||
}
|
||||
|
||||
private static String getMessageUsingStringMap(String errorKey, Map<String, String> metadata) {
|
||||
String template = getTemplateForKey(errorKey);
|
||||
if (template == null) {
|
||||
return errorKey;
|
||||
}
|
||||
return expand(template, metadata);
|
||||
}
|
||||
|
||||
private static Map<String, String> getStringMap(Map<String, Object> metadata) {
|
||||
Map<String, String> stringMap = new LinkedHashMap<>();
|
||||
if (MapUtils.isNotEmpty(metadata)) {
|
||||
for (Map.Entry<String, Object> entry : metadata.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
stringMap.put(entry.getKey(), getMetadataObjectStringValue(value));
|
||||
}
|
||||
}
|
||||
return stringMap;
|
||||
}
|
||||
|
||||
private static String getMetadataObjectStringValue(Object obj) {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
// obj is of primitive type
|
||||
// String value structure should be 'NAME' (ID: id, UUID: uuid)
|
||||
// NAME is obtained from obj.getName() or obj.getDisplayText()
|
||||
// ID is obtained from obj.getId() if obj instanceof InternalIdentity and only for root admin
|
||||
// UUID is obtained from obj.getUuid() if obj instanceof Identity
|
||||
// If NAME is not available, fallback to obj.toString() then simply return UUID if available
|
||||
String uuid = null;
|
||||
if (obj instanceof Identity) {
|
||||
uuid = ((Identity) obj).getUuid();
|
||||
}
|
||||
String name = null;
|
||||
for (String getter : new String[]{"getDisplayText", "getDisplayName", "getName"}) {
|
||||
name = invokeStringGetter(obj, getter);
|
||||
if (name != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
if (StringUtils.isNotEmpty(uuid)) {
|
||||
return uuid;
|
||||
}
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("'").append(name).append("'");
|
||||
|
||||
Long id = null;
|
||||
if (CallContext.current().isCallingAccountRootAdmin() && obj instanceof InternalIdentity) {
|
||||
id = ((InternalIdentity) obj).getId();
|
||||
}
|
||||
|
||||
if (id == null && uuid == null) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(" (");
|
||||
if (id != null) {
|
||||
sb.append("ID: ").append(id);
|
||||
if (uuid != null) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
if (uuid != null) {
|
||||
sb.append("UUID: ").append(uuid);
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String invokeStringGetter(Object obj, String methodName) {
|
||||
try {
|
||||
Class<?> cls = obj.getClass();
|
||||
var m = cls.getMethod(methodName);
|
||||
Object val = m.invoke(obj);
|
||||
return val == null ? null : val.toString();
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateExceptionResponse(ExceptionResponse response, CloudRuntimeException cre) {
|
||||
String key = cre.getMessageKey();
|
||||
Map<String, Object> map = cre.getMetadata();
|
||||
|
||||
if (key == null) {
|
||||
if (cre.getCause() instanceof InvalidParameterValueException) {
|
||||
key = ((InvalidParameterValueException) cre.getCause()).getMessageKey();
|
||||
map = ((InvalidParameterValueException) cre.getCause()).getMetadata();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
response.setErrorTextKey(key);
|
||||
Map<String, String> stringMap = getStringMap(map);
|
||||
String message = getMessageUsingStringMap(key, stringMap);
|
||||
if (message != null) {
|
||||
response.setErrorText(message);
|
||||
}
|
||||
response.setErrorMetadata(stringMap);
|
||||
}
|
||||
|
||||
private static synchronized void reloadIfRequired() {
|
||||
try {
|
||||
// log current directory for debugging purposes
|
||||
LOG.debug("Current working directory: {}",
|
||||
Paths.get(".").toAbsolutePath().normalize());
|
||||
File errorMessagesFile = PropertiesUtil.findConfigFile(ERROR_MESSAGES_FILENAME);
|
||||
if (errorMessagesFile == null || !errorMessagesFile.exists()) {
|
||||
if (!templates.isEmpty()) {
|
||||
LOG.warn("Error messages file disappeared: {}",
|
||||
errorMessagesFile != null ? errorMessagesFile.getAbsolutePath() : ERROR_MESSAGES_FILENAME);
|
||||
templates = Collections.emptyMap();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
long modified =
|
||||
Files.getLastModifiedTime(errorMessagesFile.toPath()).toMillis();
|
||||
|
||||
if (modified == lastModified) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (InputStream is =
|
||||
Files.newInputStream(errorMessagesFile.toPath())) {
|
||||
|
||||
templates = MAPPER.readValue(
|
||||
is,
|
||||
new TypeReference<>() {
|
||||
}
|
||||
);
|
||||
lastModified = modified;
|
||||
|
||||
LOG.info("Reloaded {} error message templates from {}",
|
||||
templates.size(), errorMessagesFile.toPath());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to reload error messages from {}",
|
||||
ERROR_MESSAGES_FILENAME, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String expand(String template, Map<String, String> metadata) {
|
||||
if (metadata == null || metadata.isEmpty()) {
|
||||
return template;
|
||||
}
|
||||
String result = template;
|
||||
for (Map.Entry<String, String> entry : metadata.entrySet()) {
|
||||
String placeholder = "{{" + entry.getKey() + "}}";
|
||||
Object value = entry.getValue();
|
||||
if (value != null) {
|
||||
result = result.replace(placeholder, value.toString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"vm.deploy.network.not.found.ip.map": "The network selected {{networkId}} in IP to network map could not be found. It may have been removed or is no longer accessible.",
|
||||
"vm.deploy.serviceoffering.not.specified": "Unable to deploy Instance as the required parameter 'serviceofferingid' is missing.",
|
||||
"vm.deploy.serviceoffering.inactive": "Unable to deploy Instance as the given service offering {{serviceOffering}} is inactive. Specify an active service offering.",
|
||||
"vm.deploy.serviceoffering.override.not.allowed": "Unable to deploy Instance as the selected service offering {{serviceOffering}} does not allow changing the disk offering.",
|
||||
"vm.deploy.serviceoffering.override.not.allowed.admin": "Unable to deploy Instance as the selected service offering {{serviceOffering}} uses disk offering strictness and does not allow changing the disk offering.",
|
||||
"vm.deploy.serviceoffering.fixed.parameters.not.allowed": "Unable to deploy the instance because the selected service offering {{serviceOffering}} does not allow specifying {{cpuNumberKey}}, {{cpuSpeedKey}}, or {{memory}}.",
|
||||
"vm.deploy.serviceoffering.fixed.parameters.not.allowed.admin": "Unable to deploy the instance because the selected service offering {{serviceOffering}} is not a dynamic offering and it does not allow specifying {{cpuNumberKey}}, {{cpuSpeedKey}}, or {{memory}}."
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
/etc/cloudstack/management/java.security.ciphers
|
||||
/etc/cloudstack/management/log4j-cloud.xml
|
||||
/etc/cloudstack/management/config.json
|
||||
/etc/cloudstack/management/error-messages.json
|
||||
/etc/cloudstack/extensions/Proxmox/proxmox.sh
|
||||
/etc/cloudstack/extensions/HyperV/hyperv.py
|
||||
/etc/cloudstack/extensions/MaaS/maas.py
|
||||
|
|
|
|||
|
|
@ -438,7 +438,7 @@ public class ServiceOfferingVO implements ServiceOffering {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Service offering %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "name", "uuid"));
|
||||
return String.format("Service offering %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "name", "uuid"));
|
||||
}
|
||||
|
||||
public boolean isDynamicScalingEnabled() {
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ public class UserAccountVO implements UserAccount, InternalIdentity {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("UserAccount %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields
|
||||
return String.format("UserAccount %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields
|
||||
(this, "id", "uuid", "username", "accountName"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ import javax.persistence.Table;
|
|||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.user.Account.State;
|
||||
import com.cloud.utils.db.Encrypt;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* A bean representing a user
|
||||
|
|
@ -296,7 +296,7 @@ public class UserVO implements User, Identity, InternalIdentity {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("User %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "username"));
|
||||
return String.format("User %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "username"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ cp client/target/lib/*jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib/
|
|||
rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/scripts
|
||||
rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/vms
|
||||
|
||||
for name in db.properties server.properties log4j-cloud.xml environment.properties java.security.ciphers
|
||||
for name in db.properties server.properties log4j-cloud.xml environment.properties java.security.ciphers error-messages.json
|
||||
do
|
||||
cp client/target/conf/$name ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/$name
|
||||
done
|
||||
|
|
|
|||
|
|
@ -17,30 +17,29 @@
|
|||
|
||||
package org.apache.cloudstack.network.contrail.management;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.net.InetAddress;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||
import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
|
||||
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.RegisterUserKeyCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||
import com.cloud.api.query.vo.ControlledViewEntity;
|
||||
import com.cloud.configuration.ResourceLimit;
|
||||
import com.cloud.configuration.dao.ResourceCountDao;
|
||||
|
|
@ -230,6 +229,12 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRootAdmin(Account account) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDomainAdmin(Long accountId) {
|
||||
// TODO Auto-generated method stub
|
||||
|
|
|
|||
1
pom.xml
1
pom.xml
|
|
@ -1033,6 +1033,7 @@
|
|||
<exclude>CHANGES.md</exclude>
|
||||
<exclude>ISSUE_TEMPLATE.md</exclude>
|
||||
<exclude>PULL_REQUEST_TEMPLATE.md</exclude>
|
||||
<exclude>client/conf/error-messages.json.in</exclude>
|
||||
<exclude>build/build.number</exclude>
|
||||
<exclude>debian/cloudstack-agent.dirs</exclude>
|
||||
<exclude>debian/cloudstack-usage.dirs</exclude>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import org.apache.cloudstack.api.BaseAsyncCreateCmd;
|
|||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.ExceptionResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.context.ErrorMessageResolver;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJob;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||
|
|
@ -39,6 +40,7 @@ import com.cloud.user.User;
|
|||
import com.cloud.utils.component.AdapterBase;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
|
|
@ -131,6 +133,9 @@ public class ApiAsyncJobDispatcher extends AdapterBase implements AsyncJobDispat
|
|||
ExceptionResponse response = new ExceptionResponse();
|
||||
response.setErrorCode(errorCode);
|
||||
response.setErrorText(errorMsg);
|
||||
if (e instanceof CloudRuntimeException) {
|
||||
ErrorMessageResolver.updateExceptionResponse(response, (CloudRuntimeException) e);
|
||||
}
|
||||
response.setResponseName((cmdObj == null) ? "unknowncommandresponse" : cmdObj.getCommandName());
|
||||
|
||||
_asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.FAILED, errorCode, ApiSerializerHelper.toSerializedString(response));
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
// under the License.
|
||||
package com.cloud.api;
|
||||
|
||||
import static com.cloud.user.AccountManagerImpl.apiKeyAccess;
|
||||
import static org.apache.cloudstack.user.UserPasswordResetManager.UserPasswordResetEnabled;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
|
|
@ -31,6 +34,7 @@ import java.security.SecureRandom;
|
|||
import java.security.Security;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
|
|
@ -39,7 +43,6 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
|
@ -58,15 +61,6 @@ import javax.naming.ConfigurationException;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import com.cloud.cluster.ManagementServerHostVO;
|
||||
import com.cloud.cluster.dao.ManagementServerHostDao;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.AccountManagerImpl;
|
||||
import com.cloud.user.DomainManager;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.UserAccount;
|
||||
import com.cloud.user.UserVO;
|
||||
import org.apache.cloudstack.acl.APIChecker;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
|
|
@ -106,6 +100,7 @@ import org.apache.cloudstack.api.response.ListResponse;
|
|||
import org.apache.cloudstack.api.response.LoginCmdResponse;
|
||||
import org.apache.cloudstack.config.ApiServiceConfiguration;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.context.ErrorMessageResolver;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||
|
|
@ -156,6 +151,8 @@ import org.springframework.stereotype.Component;
|
|||
import com.cloud.api.dispatch.DispatchChainFactory;
|
||||
import com.cloud.api.dispatch.DispatchTask;
|
||||
import com.cloud.api.response.ApiResponseSerializer;
|
||||
import com.cloud.cluster.ManagementServerHostVO;
|
||||
import com.cloud.cluster.dao.ManagementServerHostDao;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.domain.DomainVO;
|
||||
import com.cloud.domain.dao.DomainDao;
|
||||
|
|
@ -174,11 +171,18 @@ import com.cloud.exception.ResourceUnavailableException;
|
|||
import com.cloud.exception.UnavailableCommandException;
|
||||
import com.cloud.projects.dao.ProjectDao;
|
||||
import com.cloud.storage.VolumeApiService;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.AccountManagerImpl;
|
||||
import com.cloud.user.DomainManager;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.UserAccount;
|
||||
import com.cloud.user.UserVO;
|
||||
import com.cloud.utils.ConstantTimeComparator;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.HttpUtils;
|
||||
import com.cloud.utils.HttpUtils.ApiSessionKeySameSite;
|
||||
import com.cloud.utils.HttpUtils.ApiSessionKeyCheckOption;
|
||||
import com.cloud.utils.HttpUtils.ApiSessionKeySameSite;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.ReflectUtil;
|
||||
import com.cloud.utils.StringUtils;
|
||||
|
|
@ -194,9 +198,6 @@ import com.cloud.utils.exception.ExceptionProxyObject;
|
|||
import com.cloud.utils.net.NetUtils;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import static com.cloud.user.AccountManagerImpl.apiKeyAccess;
|
||||
import static org.apache.cloudstack.user.UserPasswordResetManager.UserPasswordResetEnabled;
|
||||
|
||||
@Component
|
||||
public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiServerService, Configurable {
|
||||
private static final Logger ACCESSLOGGER = LogManager.getLogger("apiserver." + ApiServer.class.getName());
|
||||
|
|
@ -1670,6 +1671,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
apiResponse.setErrorCode(ex.getErrorCode().getHttpCode());
|
||||
apiResponse.setErrorText(ex.getDescription());
|
||||
apiResponse.setResponseName(responseName);
|
||||
ErrorMessageResolver.updateExceptionResponse(apiResponse, ex);
|
||||
final ArrayList<ExceptionProxyObject> idList = ex.getIdProxyList();
|
||||
if (idList != null) {
|
||||
for (ExceptionProxyObject exceptionProxyObject : idList) {
|
||||
|
|
|
|||
|
|
@ -598,21 +598,26 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
@Override
|
||||
public boolean isRootAdmin(Long accountId) {
|
||||
if (accountId != null) {
|
||||
AccountVO acct = _accountDao.findById(accountId);
|
||||
if (acct == null) {
|
||||
return false; //account is deleted or does not exist
|
||||
}
|
||||
for (SecurityChecker checker : _securityCheckers) {
|
||||
try {
|
||||
if (checker.checkAccess(acct, null, null, "SystemCapability")) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Root Access granted to " + acct + " by " + checker.getName());
|
||||
}
|
||||
return true;
|
||||
return isRootAdmin(_accountDao.findById(accountId));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRootAdmin(Account account) {
|
||||
if (account == null) {
|
||||
return false; //account is deleted or does not exist
|
||||
}
|
||||
for (SecurityChecker checker : _securityCheckers) {
|
||||
try {
|
||||
if (checker.checkAccess(account, null, null, "SystemCapability")) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Root Access granted to " + account + " by " + checker.getName());
|
||||
}
|
||||
} catch (PermissionDeniedException ex) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
} catch (PermissionDeniedException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -6323,18 +6323,23 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
|
||||
private void verifyServiceOffering(BaseDeployVMCmd cmd, ServiceOffering serviceOffering) {
|
||||
if (ServiceOffering.State.Inactive.equals(serviceOffering.getState())) {
|
||||
throw new InvalidParameterValueException(String.format("Service offering is inactive: [%s].", serviceOffering.getUuid()));
|
||||
throw new InvalidParameterValueException("vm.deploy.serviceoffering.inactive",
|
||||
Map.of("serviceOffering", serviceOffering));
|
||||
}
|
||||
|
||||
Long overrideDiskOfferingId = cmd.getOverrideDiskOfferingId();
|
||||
if (serviceOffering.getDiskOfferingStrictness() && overrideDiskOfferingId != null) {
|
||||
throw new InvalidParameterValueException(String.format("Cannot override disk offering id %d since provided service offering is strictly mapped to its disk offering", overrideDiskOfferingId));
|
||||
throw new InvalidParameterValueException("vm.deploy.serviceoffering.override.not.allowed",
|
||||
Map.of("serviceOffering", serviceOffering));
|
||||
}
|
||||
|
||||
if (!serviceOffering.isDynamic()) {
|
||||
for (String detail: cmd.getDetails().keySet()) {
|
||||
if (detail.equalsIgnoreCase(VmDetailConstants.CPU_NUMBER) || detail.equalsIgnoreCase(VmDetailConstants.CPU_SPEED) || detail.equalsIgnoreCase(VmDetailConstants.MEMORY)) {
|
||||
throw new InvalidParameterValueException("cpuNumber or cpuSpeed or memory should not be specified for static service offering");
|
||||
throw new InvalidParameterValueException("vm.deploy.serviceoffering.fixed.parameters.not.allowed",
|
||||
Map.of("cpuNumberKey", VmDetailConstants.CPU_NUMBER,
|
||||
"cpuSpeedKey", VmDetailConstants.CPU_NUMBER,
|
||||
"memoryKey", VmDetailConstants.CPU_NUMBER));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6382,7 +6387,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
|
||||
Long serviceOfferingId = cmd.getServiceOfferingId();
|
||||
if (serviceOfferingId == null) {
|
||||
throw new InvalidParameterValueException("Unable to execute API command deployvirtualmachine due to missing parameter serviceofferingid");
|
||||
throw new InvalidParameterValueException("vm.deploy.serviceoffering.not.specified", Collections.emptyMap());
|
||||
}
|
||||
Long overrideDiskOfferingId = cmd.getOverrideDiskOfferingId();
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,12 @@
|
|||
// under the License.
|
||||
package com.cloud.storage;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -24,12 +30,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.cloud.dc.HostPodVO;
|
||||
import com.cloud.dc.dao.HostPodDao;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.storage.dao.StoragePoolAndAccessGroupMapDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.command.admin.storage.ChangeStoragePoolScopeCmd;
|
||||
import org.apache.cloudstack.api.command.admin.storage.ConfigureStorageAccessCmd;
|
||||
|
|
@ -72,9 +72,11 @@ import com.cloud.capacity.CapacityVO;
|
|||
import com.cloud.dc.ClusterVO;
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.HostPodVO;
|
||||
import com.cloud.dc.VsphereStoragePolicyVO;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.dc.dao.HostPodDao;
|
||||
import com.cloud.dc.dao.VsphereStoragePolicyDao;
|
||||
import com.cloud.exception.AgentUnavailableException;
|
||||
import com.cloud.exception.ConnectionException;
|
||||
|
|
@ -83,8 +85,12 @@ import com.cloud.exception.OperationTimedoutException;
|
|||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.exception.StorageUnavailableException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.hypervisor.HypervisorGuruManager;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.storage.dao.StoragePoolAndAccessGroupMapDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.user.AccountManagerImpl;
|
||||
import com.cloud.utils.Pair;
|
||||
|
|
@ -93,12 +99,6 @@ import com.cloud.vm.DiskProfile;
|
|||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StorageManagerImplTest {
|
||||
|
||||
|
|
@ -667,7 +667,7 @@ public class StorageManagerImplTest {
|
|||
final DataCenterVO zone = new DataCenterVO(1L, null, null, null, null, null, null, null, null, null, DataCenter.NetworkType.Advanced, null, null);
|
||||
StoragePoolVO primaryStorage = mockStoragePoolVOForChangeStoragePoolScope(currentScope, status);
|
||||
|
||||
Mockito.when(accountMgr.isRootAdmin(Mockito.any())).thenReturn(true);
|
||||
Mockito.when(accountMgr.isRootAdmin(Mockito.anyLong())).thenReturn(true);
|
||||
Mockito.when(dataCenterDao.findById(1L)).thenReturn(zone);
|
||||
Mockito.when(storagePoolDao.findById(1L)).thenReturn(primaryStorage);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,34 +16,10 @@
|
|||
// under the License.
|
||||
package com.cloud.storage.snapshot;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.org.Grouping;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotPolicyVO;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.server.TaggedResourceService;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.storage.dao.SnapshotPolicyDao;
|
||||
import com.cloud.storage.dao.SnapshotZoneDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.ResourceLimitService;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.Pair;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.DeleteSnapshotPoliciesCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotPoliciesCmd;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
|
@ -57,13 +33,11 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService;
|
|||
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
|
|
@ -71,9 +45,33 @@ import org.mockito.Mockito;
|
|||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.org.Grouping;
|
||||
import com.cloud.server.TaggedResourceService;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotPolicyVO;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.storage.dao.SnapshotPolicyDao;
|
||||
import com.cloud.storage.dao.SnapshotZoneDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.ResourceLimitService;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SnapshotManagerImplTest {
|
||||
|
|
@ -242,7 +240,7 @@ public class SnapshotManagerImplTest {
|
|||
DataCenterVO zone1 = Mockito.mock(DataCenterVO.class);
|
||||
Mockito.when(zone1.getAllocationState()).thenReturn(Grouping.AllocationState.Disabled);
|
||||
Mockito.when(dataCenterDao.findById(2L)).thenReturn(zone1);
|
||||
Mockito.when(accountManager.isRootAdmin(Mockito.any())).thenReturn(false);
|
||||
Mockito.when(accountManager.isRootAdmin(Mockito.anyLong())).thenReturn(false);
|
||||
snapshotManager.validatePolicyZones(List.of(2L), null, volumeVO, Mockito.mock(Account.class));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,12 @@
|
|||
package org.apache.cloudstack.storage.sharedfs;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
|
@ -618,7 +619,7 @@ public class SharedFSServiceImplTest {
|
|||
when(sharedFSJoinDao.searchByIds(List.of(s_sharedFSId).toArray(new Long[0]))).thenReturn(List.of(sharedFSJoinVO));
|
||||
|
||||
when(owner.getId()).thenReturn(s_ownerId);
|
||||
when(accountMgr.isRootAdmin(any())).thenReturn(true);
|
||||
when(accountMgr.isRootAdmin(anyLong())).thenReturn(true);
|
||||
when(sharedFSJoinDao.createSharedFSResponses(any(), any())).thenReturn(null);
|
||||
|
||||
ListSharedFSCmd cmd = getMockListSharedFSCmd();
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ import {
|
|||
cpuArchitectureUtilPlugin,
|
||||
imagesUtilPlugin,
|
||||
extensionsUtilPlugin,
|
||||
backupUtilPlugin
|
||||
backupUtilPlugin,
|
||||
localeErrorUtilPlugin
|
||||
} from './utils/plugins'
|
||||
import { VueAxios } from './utils/request'
|
||||
import directives from './utils/directives'
|
||||
|
|
@ -65,6 +66,7 @@ vueApp.use(cpuArchitectureUtilPlugin)
|
|||
vueApp.use(imagesUtilPlugin)
|
||||
vueApp.use(extensionsUtilPlugin)
|
||||
vueApp.use(backupUtilPlugin)
|
||||
vueApp.use(localeErrorUtilPlugin)
|
||||
vueApp.use(extensions)
|
||||
vueApp.use(directives)
|
||||
|
||||
|
|
|
|||
|
|
@ -229,7 +229,8 @@ export const notifierPlugin = {
|
|||
} else if (error.response.data) {
|
||||
const responseKey = _.findKey(error.response.data, 'errortext')
|
||||
if (responseKey) {
|
||||
desc = error.response.data[responseKey].errortext
|
||||
const errObj = error.response.data[responseKey]
|
||||
desc = this.$toLocaleError(errObj.errortext, errObj.errortextkey, errObj.errormetadata)
|
||||
} else if (typeof error.response.data === 'string') {
|
||||
desc = error.response.data
|
||||
}
|
||||
|
|
@ -620,3 +621,23 @@ export const backupUtilPlugin = {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const localeErrorUtilPlugin = {
|
||||
install (app) {
|
||||
app.config.globalProperties.$toLocaleError = function (msg, key, params) {
|
||||
if (!key) {
|
||||
return msg
|
||||
}
|
||||
let localeMsg = i18n.global.t(key)
|
||||
if (!localeMsg || localeMsg === key) {
|
||||
return msg
|
||||
}
|
||||
if (params && params.constructor === Object) {
|
||||
for (const paramKey in params) {
|
||||
localeMsg = localeMsg.replace(`{{${paramKey}}}`, params[paramKey])
|
||||
}
|
||||
}
|
||||
return localeMsg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import java.io.ObjectInputStream;
|
|||
import java.io.ObjectOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.SerialVersionUID;
|
||||
|
|
@ -42,6 +43,9 @@ public class CloudRuntimeException extends RuntimeException implements ErrorCont
|
|||
|
||||
protected int csErrorCode;
|
||||
|
||||
protected String messageKey = null;
|
||||
protected Map<String, Object> metadata = null;
|
||||
|
||||
public CloudRuntimeException(String message) {
|
||||
super(message);
|
||||
setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName()));
|
||||
|
|
@ -52,6 +56,13 @@ public class CloudRuntimeException extends RuntimeException implements ErrorCont
|
|||
setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName()));
|
||||
}
|
||||
|
||||
public CloudRuntimeException(String message, String messageKey, Map<String, Object> metadata) {
|
||||
super(message);
|
||||
this.messageKey = messageKey;
|
||||
this.metadata = metadata;
|
||||
setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName()));
|
||||
}
|
||||
|
||||
protected CloudRuntimeException() {
|
||||
super();
|
||||
|
||||
|
|
@ -138,4 +149,12 @@ public class CloudRuntimeException extends RuntimeException implements ErrorCont
|
|||
uuidList.add(new Pair<Class<?>, String>(Class.forName(clzName), val));
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessageKey() {
|
||||
return messageKey;
|
||||
}
|
||||
|
||||
public Map<String, Object> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue