diff --git a/api/src/main/java/com/cloud/configuration/Resource.java b/api/src/main/java/com/cloud/configuration/Resource.java index 97be7f9d64c5..460ff5d527b5 100644 --- a/api/src/main/java/com/cloud/configuration/Resource.java +++ b/api/src/main/java/com/cloud/configuration/Resource.java @@ -22,32 +22,34 @@ 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; } @@ -55,6 +57,10 @@ public String getName() { return name; } + public String getDisplayName() { + return displayName; + } + public int getOrdinal() { return ordinal; } diff --git a/api/src/main/java/com/cloud/user/AccountService.java b/api/src/main/java/com/cloud/user/AccountService.java index fc450e9179c5..20840bb45dd7 100644 --- a/api/src/main/java/com/cloud/user/AccountService.java +++ b/api/src/main/java/com/cloud/user/AccountService.java @@ -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; @@ -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; @@ -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 { /** @@ -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); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java index 38e9921853a3..4bc0d06b1a75 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java @@ -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; @@ -672,7 +673,8 @@ private Long getNetworkIdFomIpMap(HashMap 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; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 0a943fab118d..80f8eb9752ca 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -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; @@ -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()); + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExceptionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExceptionResponse.java index d475dee0620f..19210e6f8642 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ExceptionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExceptionResponse.java @@ -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 { @@ -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 errorMetadata; + public ExceptionResponse() { idList = new ArrayList(); } @@ -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 getErrorMetadata() { + return errorMetadata; + } + + public void setErrorMetadata(Map errorMetadata) { + this.errorMetadata = errorMetadata; + } + public void addProxyObject(ExceptionProxyObject id) { idList.add(id); return; diff --git a/api/src/main/java/org/apache/cloudstack/context/CallContext.java b/api/src/main/java/org/apache/cloudstack/context/CallContext.java index fcfb5b6b1e00..0d03d649cd1d 100644 --- a/api/src/main/java/org/apache/cloudstack/context/CallContext.java +++ b/api/src/main/java/org/apache/cloudstack/context/CallContext.java @@ -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 @@ -53,6 +57,7 @@ protected Stack initialValue() { private String contextId; private Account account; private long accountId; + private Boolean isAccountRootAdmin = null; private long startEventId = 0; private String eventDescription; private String eventDetails; @@ -64,6 +69,7 @@ protected Stack initialValue() { private long userId; private final Map context = new HashMap(); private final Map apiResourcesUuids = new HashMap<>(); + private final Map errorContext = new HashMap<>(); private Project project; private String apiName; @@ -135,6 +141,24 @@ public Account getCallingAccount() { return account; } + public boolean isCallingAccountRootAdmin() { + if (getCallingUserId() == User.UID_SYSTEM) { + return false; + } + 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(); @@ -408,6 +432,21 @@ public void putContextParameters(Map details){ } } + public Map getErrorContextParameters() { + return errorContext; + } + + public void putErrorContextParameter(String key, Object value) { + errorContext.put(key, value); + } + + public void putErrorContextParameters(Map details) { + if (MapUtils.isEmpty(details)) { + return; + } + errorContext.putAll(details); + } + public static void setActionEventInfo(String eventType, String description) { CallContext context = CallContext.current(); if (context != null) { diff --git a/api/src/main/java/org/apache/cloudstack/context/ResponseMessageResolver.java b/api/src/main/java/org/apache/cloudstack/context/ResponseMessageResolver.java new file mode 100644 index 000000000000..d313692a9ff9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/context/ResponseMessageResolver.java @@ -0,0 +1,369 @@ +// 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.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +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.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.Ternary; +import com.cloud.utils.exception.CloudRuntimeException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class ResponseMessageResolver { + private static final Logger LOG = + LogManager.getLogger(ResponseMessageResolver.class); + + protected static final String ERROR_MESSAGES_FILENAME = "error-messages.json"; + protected static final String ERROR_KEY_ADMIN_SUFFIX = ".admin"; + protected static final boolean USE_RESOURCE_TO_STRING_IN_METADATA = false; + protected static final boolean INCLUDE_RESOURCE_ID_FOR_ADMINS_IN_METADATA = true; + + private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{\\{\\s*([A-Za-z0-9_]+)\\s*\\}\\}"); + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + // volatile for safe publication + private static volatile Map templates = + Collections.emptyMap(); + + private static volatile long lastModified = -1; + + private ResponseMessageResolver() { + } + + /** + * Clears the cached templates and last modified timestamp. + * Useful for testing to ensure cache isolation between tests. + */ + protected static synchronized void clearCache() { + templates = Collections.emptyMap(); + lastModified = -1; + } + + protected static List getVariableNamesInErrorKey(String template) { + if (template == null || template.isEmpty()) { + return Collections.emptyList(); + } + List variables = new ArrayList<>(); + Matcher matcher = VARIABLE_PATTERN.matcher(template); + while (matcher.find()) { + String name = matcher.group(1); + if (name != null && !name.isEmpty()) { + variables.add(name); + } + } + return variables; + } + + protected static Map getCombinedMetadataFromErrorTemplate(String template, Map metadata) { + List variableNames = getVariableNamesInErrorKey(template); + if (variableNames.isEmpty()) { + return metadata; + } + Map contextMetadata = CallContext.current().getErrorContextParameters(); + if (MapUtils.isEmpty(contextMetadata)) { + return metadata; + } + Map combinedMetadata = new LinkedHashMap<>(); + for (String varName : variableNames) { + if (contextMetadata.containsKey(varName)) { + combinedMetadata.put(varName, contextMetadata.get(varName)); + } + } + if (MapUtils.isNotEmpty(metadata)) { + combinedMetadata.putAll(metadata); + } + return combinedMetadata; + } + + protected 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); + } + + protected static Map getStringMap(Map metadata) { + Map stringMap = new LinkedHashMap<>(); + if (MapUtils.isNotEmpty(metadata)) { + for (Map.Entry entry : metadata.entrySet()) { + Object value = entry.getValue(); + stringMap.put(entry.getKey(), + USE_RESOURCE_TO_STRING_IN_METADATA ? + getMetadataObjectStringValueAlt(value) : + getMetadataObjectStringValue(value)); + } + } + return stringMap; + } + + + + /** + * Converts a metadata object to a human-readable string for error messages. + * + *

Behavior: + *

    + *
  • If {@code obj} is {@code null}, returns {@code null}.
  • + *
  • Attempts to obtain a display name by invoking one of the getters + * {@code getDisplayText()}, {@code getDisplayName()}, or {@code getName()} via reflection. + * If a name is found, returns it quoted as {@code 'NAME'}.
  • + *
  • When the current calling account is a root admin, the returned value will include + * an identifier suffix in the form {@code (ID: id, UUID: uuid)} when available. + * The ID is included only if {@code INCLUDE_METADATA_ID_IN_MESSAGE} is {@code true} + * and {@code obj} implements {@link InternalIdentity}. The UUID is included when + * {@code obj} implements {@link org.apache.cloudstack.api.Identity}.
  • + *
  • If no display name is available, returns the UUID (if {@code obj} implements + * {@code Identity}); otherwise returns {@code obj.toString()}.
  • + *
+ * + *

Reflection is used to call getters; invocation failures are silently ignored and treated as + * absence of the corresponding value. + * + * @param obj metadata object + * @return formatted metadata string suitable for inclusion in error messages, or {@code null} + * if {@code obj} is {@code null} + */ + protected static String getMetadataObjectStringValue(Object obj) { + if (obj == null) { + return null; + } + 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("'"); + + if (!CallContext.current().isCallingAccountRootAdmin()) { + return sb.toString(); + } + + Long id = null; + if (INCLUDE_RESOURCE_ID_FOR_ADMINS_IN_METADATA && obj instanceof InternalIdentity) { + id = ((InternalIdentity) obj).getId(); + } + + if (ObjectUtils.allNull(id, uuid)) { + 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(); + } + + /** + * Converts a metadata object to a human-readable string, prioritizing toString(). + * + *

Behavior: + *

    + *
  • If {@code obj} is {@code null}, returns {@code null}.
  • + *
  • First attempts to use {@code obj.toString()}. If the result is non-empty, returns it.
  • + *
  • For non-root admins, removes any "id: DBID" patterns from the toString() result.
  • + *
  • If {@code obj.toString()} is empty or null (after filtering), falls back to {@link #getMetadataObjectStringValue(Object)}, + * which uses reflection to find display names and format metadata with UUID/ID info.
  • + *
+ * + * @param obj metadata object + * @return formatted metadata string suitable for inclusion in error messages, or {@code null} + * if {@code obj} is {@code null} + */ + protected static String getMetadataObjectStringValueAlt(Object obj) { + if (obj == null) { + return null; + } + String result = obj.toString(); + if (StringUtils.isNotEmpty(result)) { + // Remove id: DBID pattern for non-root admins + if (!CallContext.current().isCallingAccountRootAdmin()) { + result = result.replaceAll("\\bid:\\s*\\d+", "").trim(); + } + if (StringUtils.isNotEmpty(result)) { + return result; + } + } + return getMetadataObjectStringValue(obj); + } + + protected 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; + } + } + + protected 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); + } + } + + protected static String expand(String template, Map metadata) { + if (MapUtils.isEmpty(metadata)) { + return template; + } + String result = template; + for (Map.Entry entry : metadata.entrySet()) { + String placeholder = "{{" + entry.getKey() + "}}"; + Object value = entry.getValue(); + if (value != null) { + result = result.replace(placeholder, value.toString()); + } + } + return result; + } + + public static String getMessage(String errorKey, Map metadata) { + String template = getTemplateForKey(errorKey); + if (template == null) { + return errorKey; + } + Map combinedMetadata = getCombinedMetadataFromErrorTemplate(template, metadata); + return expand(template, getStringMap(combinedMetadata)); + } + + public static Ternary> resolve(String errorKey, Map metadata) { + String template = getTemplateForKey(errorKey); + if (template == null) { + return new Ternary<>(errorKey, errorKey, metadata); + } + Map combinedMetadata = getCombinedMetadataFromErrorTemplate(template, metadata); + return new Ternary<>(expand(template, getStringMap(combinedMetadata)), errorKey, combinedMetadata); + } + + public static void updateExceptionResponse(ExceptionResponse response, CloudRuntimeException cre) { + String key = cre.getMessageKey(); + Map map = cre.getMetadata(); + + if (key == null) { + Throwable cause = cre.getCause(); + if (!(cause instanceof CloudRuntimeException)) { + return; + } + CloudRuntimeException causeEx = (CloudRuntimeException) cause; + key = causeEx.getMessageKey(); + if (key == null) { + return; + } + map = causeEx.getMetadata(); + } + response.setErrorTextKey(key); + String template = getTemplateForKey(key); + if (template == null) { + response.setErrorText(key); + response.setErrorMetadata(getStringMap(map)); + return; + } + Map combinedMetadata = getCombinedMetadataFromErrorTemplate(template, map); + Map stringMap = getStringMap(combinedMetadata); + response.setErrorText(expand(template, stringMap)); + response.setErrorMetadata(stringMap); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/error/Exceptions.java b/api/src/main/java/org/apache/cloudstack/error/Exceptions.java new file mode 100644 index 000000000000..5c171e003e6e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/error/Exceptions.java @@ -0,0 +1,110 @@ +// 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.error; + +import java.util.Collections; +import java.util.Map; +import java.util.function.Function; + +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.context.ResponseMessageResolver; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.utils.Ternary; +import com.cloud.utils.exception.CloudRuntimeException; + +public final class Exceptions { + + private Exceptions() { + } + + public static InvalidParameterValueException invalidParameterValueException(final String errorKey) { + return invalidParameterValueException(errorKey, Collections.emptyMap()); + } + + public static InvalidParameterValueException invalidParameterValueException(final String errorKey, + final Map metadata) { + return build(errorKey, metadata, InvalidParameterValueException::new); + } + + public static PermissionDeniedException permissionDeniedException(final String errorKey) { + return permissionDeniedException(errorKey, Collections.emptyMap()); + } + + public static PermissionDeniedException permissionDeniedException(final String errorKey, + final Map metadata) { + return build(errorKey, metadata, PermissionDeniedException::new); + } + + public static ServerApiException serverApiException(final ApiErrorCode errorCode, + final String errorKey) { + return serverApiException(errorCode, errorKey, Collections.emptyMap()); + } + + public static ServerApiException serverApiException(final ApiErrorCode errorCode, + final String errorKey, + final Map metadata) { + Ternary> data = + ResponseMessageResolver.resolve(errorKey, metadata); + + ServerApiException ex = new ServerApiException(errorCode, data.first()); + enrich(ex, data); + throw ex; + } + + public static CloudRuntimeException cloudRuntimeException(final String errorKey) { + return cloudRuntimeException(errorKey, Collections.emptyMap()); + } + + public static CloudRuntimeException cloudRuntimeException(final String errorKey, + final Map metadata) { + return build(errorKey, metadata, CloudRuntimeException::new); + } + + public static CloudRuntimeException cloudRuntimeException(final String errorKey, + final Map metadata, + final Throwable cause) { + Ternary> data = + ResponseMessageResolver.resolve(errorKey, metadata); + + CloudRuntimeException ex = new CloudRuntimeException(data.first(), cause); + enrich(ex, data); + return ex; + } + + private static T build( + final String errorKey, + final Map metadata, + final Function exceptionSupplier) { + + Ternary> data = + ResponseMessageResolver.resolve(errorKey, metadata); + + T ex = exceptionSupplier.apply(data.first()); + enrich(ex, data); + return ex; + } + + private static void enrich(final CloudRuntimeException ex, + final Ternary> data) { + ex.setMessageKey(data.second()); + ex.setMetadata(data.third()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/context/ResponseMessageResolverTest.java b/api/src/test/java/org/apache/cloudstack/context/ResponseMessageResolverTest.java new file mode 100644 index 000000000000..b15bcac749d8 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/context/ResponseMessageResolverTest.java @@ -0,0 +1,528 @@ +// 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 static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.response.ExceptionResponse; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.dc.DataCenter; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.exception.CloudRuntimeException; + +@RunWith(MockitoJUnitRunner.class) +public class ResponseMessageResolverTest { + + @Mock + CallContext callContextMock; + + MockedStatic callContextMocked; + MockedStatic propertiesUtilMocked; + + Path tmpFile; + + @Before + public void setup() { + callContextMocked = Mockito.mockStatic(CallContext.class); + callContextMocked.when(CallContext::current).thenReturn(callContextMock); + propertiesUtilMocked = Mockito.mockStatic(PropertiesUtil.class); + propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(anyString())).thenReturn(null); + } + + @After + public void tearDown() throws Exception { + callContextMocked.close(); + propertiesUtilMocked.close(); + ResponseMessageResolver.clearCache(); + if (tmpFile != null) { + try { + Files.deleteIfExists(tmpFile); + } catch (Exception ignored) { + } + } + } + + @Test + public void getVariableNamesInErrorKey_shouldReturnEmptyListForTemplateWithoutVariables() { + String template = "This is a template without variables."; + List result = ResponseMessageResolver.getVariableNamesInErrorKey(template); + Assert.assertTrue("Result should be an empty list", result.isEmpty()); + } + + @Test + public void getVariableNamesInErrorKey_shouldExtractSingleVariable() { + String template = "This is a template with one variable: {{variable}}."; + List result = ResponseMessageResolver.getVariableNamesInErrorKey(template); + Assert.assertEquals("Result should contain one variable", 1, result.size()); + Assert.assertEquals("Variable name should be 'variable'", "variable", result.get(0)); + } + + @Test + public void getVariableNamesInErrorKey_shouldExtractMultipleVariables() { + String template = "This template has {{var1}} and {{var2}}."; + List result = ResponseMessageResolver.getVariableNamesInErrorKey(template); + Assert.assertEquals("Result should contain two variables", 2, result.size()); + Assert.assertEquals("First variable name should be 'var1'", "var1", result.get(0)); + Assert.assertEquals("Second variable name should be 'var2'", "var2", result.get(1)); + } + + @Test + public void getVariableNamesInErrorKey_shouldIgnoreMalformedVariables() { + String template = "This template has {{var1} and {{var2}}."; + List result = ResponseMessageResolver.getVariableNamesInErrorKey(template); + Assert.assertEquals("Result should contain one valid variable", 1, result.size()); + Assert.assertEquals("Variable name should be 'var2'", "var2", result.get(0)); + } + + @Test + public void getVariableNamesInErrorKey_shouldHandleEmptyTemplate() { + String template = ""; + List result = ResponseMessageResolver.getVariableNamesInErrorKey(template); + Assert.assertTrue("Result should be an empty list", result.isEmpty()); + } + + @Test + public void getVariableNamesInErrorKey_shouldHandleTemplateWithOnlyVariables() { + String template = "{{var1}}{{var2}}{{var3}}"; + List result = ResponseMessageResolver.getVariableNamesInErrorKey(template); + Assert.assertEquals("Result should contain three variables", 3, result.size()); + Assert.assertEquals("First variable name should be 'var1'", "var1", result.get(0)); + Assert.assertEquals("Second variable name should be 'var2'", "var2", result.get(1)); + Assert.assertEquals("Third variable name should be 'var3'", "var3", result.get(2)); + } + + @Test + public void getCombinedMetadataFromErrorTemplate_shouldReturnMetadataWhenNoVariablesInTemplate() { + String template = "This is a template without variables."; + Map metadata = Map.of("key1", "value1"); + Map result = ResponseMessageResolver.getCombinedMetadataFromErrorTemplate(template, metadata); + Assert.assertEquals("Result should match the input metadata", metadata, result); + } + + @Test + public void getCombinedMetadataFromErrorTemplate_shouldReturnEmptyMapWhenContextMetadataIsEmpty() { + String template = "This template has {{var1}}."; + Map metadata = Map.of(); + when(callContextMock.getErrorContextParameters()).thenReturn(Map.of()); + Map result = ResponseMessageResolver.getCombinedMetadataFromErrorTemplate(template, metadata); + Assert.assertTrue("Result should be an empty map", result.isEmpty()); + } + + @Test + public void getCombinedMetadataFromErrorTemplate_shouldCombineContextAndProvidedMetadata() { + String template = "This template has {{var1}} and {{var2}}."; + Map metadata = Map.of("key1", "value1"); + when(callContextMock.getErrorContextParameters()).thenReturn(Map.of("var1", "valueVar1", "var2", "valueVar2")); + Map result = ResponseMessageResolver.getCombinedMetadataFromErrorTemplate(template, metadata); + Assert.assertEquals("Result should contain combined metadata", 3, result.size()); + Assert.assertEquals("Result should contain context metadata for var1", "valueVar1", result.get("var1")); + Assert.assertEquals("Result should contain context metadata for var2", "valueVar2", result.get("var2")); + Assert.assertEquals("Result should contain provided metadata", "value1", result.get("key1")); + } + + @Test + public void getCombinedMetadataFromErrorTemplate_shouldIgnoreVariablesNotInContextMetadata() { + String template = "This template has {{var1}} and {{var2}}."; + Map metadata = Map.of("key1", "value1"); + when(callContextMock.getErrorContextParameters()).thenReturn(Map.of("var1", "valueVar1")); + Map result = ResponseMessageResolver.getCombinedMetadataFromErrorTemplate(template, metadata); + Assert.assertEquals("Result should contain combined metadata", 2, result.size()); + Assert.assertEquals("Result should contain context metadata for var1", "valueVar1", result.get("var1")); + Assert.assertEquals("Result should contain provided metadata", "value1", result.get("key1")); + } + + @Test + public void getCombinedMetadataFromErrorTemplate_shouldReturnProvidedMetadataWhenTemplateIsEmpty() { + String template = ""; + Map metadata = Map.of("key1", "value1"); + Map result = ResponseMessageResolver.getCombinedMetadataFromErrorTemplate(template, metadata); + Assert.assertEquals("Result should match the input metadata", metadata, result); + } + + @Test + public void getStringMap_shouldReturnEmptyMapWhenMetadataIsEmpty() { + Map metadata = Map.of(); + Map result = ResponseMessageResolver.getStringMap(metadata); + Assert.assertTrue("Result should be an empty map", result.isEmpty()); + } + + @Test + public void getStringMap_shouldConvertAllMetadataValuesToStrings() { + Map metadata = Map.of("key1", 123, "key2", true, "key3", "value"); + Map result = ResponseMessageResolver.getStringMap(metadata); + Assert.assertEquals("Result should contain all keys", 3, result.size()); + Assert.assertEquals("Value for key1 should be '123'", "123", result.get("key1")); + Assert.assertEquals("Value for key2 should be 'true'", "true", result.get("key2")); + Assert.assertEquals("Value for key3 should be 'value'", "value", result.get("key3")); + } + + @Test + public void getStringMap_shouldHandleNullValuesInMetadata() { + Map metadata = new LinkedHashMap<>(); + metadata.put("key1", null); + metadata.put("key2", "value"); + Map result = ResponseMessageResolver.getStringMap(metadata); + Assert.assertEquals("Result should contain all keys", 2, result.size()); + Assert.assertNull("Value for key1 should be null", result.get("key1")); + Assert.assertEquals("Value for key2 should be 'value'", "value", result.get("key2")); + } + + @Test + public void getStringMap_shouldReturnEmptyMapWhenMetadataIsNull() { + Map result = ResponseMessageResolver.getStringMap(null); + Assert.assertTrue("Result should be an empty map", result.isEmpty()); + } + + @Test + public void getMetadataObjectStringValue_shouldReturnNullWhenObjectIsNull() { + Assert.assertNull(ResponseMessageResolver.getMetadataObjectStringValue(null)); + } + + @Test + public void getMetadataObjectStringValue_shouldReturnUuidWhenNameIsUnavailable() { + Identity identityMock = Mockito.mock(Identity.class); + when(identityMock.getUuid()).thenReturn("uuid-1234"); + Assert.assertEquals("uuid-1234", ResponseMessageResolver.getMetadataObjectStringValue(identityMock)); + } + + @Test + public void getMetadataObjectStringValue_shouldReturnNameWhenAvailable() { + DataCenter identityMock = Mockito.mock(DataCenter.class); + when(identityMock.getUuid()).thenReturn("uuid-1234"); + when(identityMock.getName()).thenReturn("TestName"); + Assert.assertEquals("'TestName'", ResponseMessageResolver.getMetadataObjectStringValue(identityMock)); + } + + @Test + public void getMetadataObjectStringValue_shouldIncludeIdAndUuidForRootAdmin() { + DataCenter internalIdentityMock = Mockito.mock(DataCenter.class); + when(internalIdentityMock.getUuid()).thenReturn("uuid-5678"); + if (ResponseMessageResolver.INCLUDE_RESOURCE_ID_FOR_ADMINS_IN_METADATA) { + when(internalIdentityMock.getId()).thenReturn(42L); + } + when(internalIdentityMock.getName()).thenReturn("AdminName"); + when(CallContext.current().isCallingAccountRootAdmin()).thenReturn(true); + String expected = String.format("'AdminName' (%sUUID: uuid-5678)", ResponseMessageResolver.INCLUDE_RESOURCE_ID_FOR_ADMINS_IN_METADATA ? "ID: 42, " : ""); + Assert.assertEquals(expected, ResponseMessageResolver.getMetadataObjectStringValue(internalIdentityMock)); + } + + @Test + public void getMetadataObjectStringValue_shouldFallbackToToStringWhenNameAndUuidAreUnavailable() { + Object obj = new Object(); + Assert.assertEquals(obj.toString(), ResponseMessageResolver.getMetadataObjectStringValue(obj)); + } + + @Test + public void getMetadataObjectStringValue_shouldReturnNameOnlyForNonRootAdmin() { + DataCenter internalIdentityMock = Mockito.mock(DataCenter.class); + when(internalIdentityMock.getName()).thenReturn("UserName"); + when(CallContext.current().isCallingAccountRootAdmin()).thenReturn(false); + Assert.assertEquals("'UserName'", ResponseMessageResolver.getMetadataObjectStringValue(internalIdentityMock)); + } + + @Test + public void expand_shouldReturnTemplateWhenMetadataIsEmpty() { + String template = "This is a template with no placeholders."; + Map metadata = Map.of(); + String result = ResponseMessageResolver.expand(template, metadata); + Assert.assertEquals("This is a template with no placeholders.", result); + } + + @Test + public void expand_shouldReplaceSinglePlaceholderWithMetadataValue() { + String template = "Hello, {{name}}!"; + Map metadata = Map.of("name", "World"); + String result = ResponseMessageResolver.expand(template, metadata); + Assert.assertEquals("Hello, World!", result); + } + + @Test + public void expand_shouldReplaceMultiplePlaceholdersWithMetadataValues() { + String template = "Hello, {{name}}! Today is {{day}}."; + Map metadata = Map.of("name", "Alice", "day", "Monday"); + String result = ResponseMessageResolver.expand(template, metadata); + Assert.assertEquals("Hello, Alice! Today is Monday.", result); + } + + @Test + public void expand_shouldIgnorePlaceholdersWithoutMatchingMetadata() { + String template = "Hello, {{name}}! Your age is {{age}}."; + Map metadata = Map.of("name", "Bob"); + String result = ResponseMessageResolver.expand(template, metadata); + Assert.assertEquals("Hello, Bob! Your age is {{age}}.", result); + } + + @Test + public void expand_shouldHandleNullMetadataValues() { + String template = "Hello, {{name}}!"; + Map metadata = new LinkedHashMap<>(); + metadata.put("name", null); + String result = ResponseMessageResolver.expand(template, metadata); + Assert.assertEquals("Hello, {{name}}!", result); + } + + @Test + public void expand_shouldReturnTemplateWhenMetadataIsNull() { + String template = "This is a template with no placeholders."; + String result = ResponseMessageResolver.expand(template, null); + Assert.assertEquals("This is a template with no placeholders.", result); + } + + @Test + public void expand_shouldHandleTemplateWithNoPlaceholders() { + String template = "This template has no placeholders."; + Map metadata = Map.of("key", "value"); + String result = ResponseMessageResolver.expand(template, metadata); + Assert.assertEquals("This template has no placeholders.", result); + } + + @Test + public void getMessage_shouldReturnErrorKeyWhenTemplateIsNull() { + String errorKey = "error.key"; + String result = ResponseMessageResolver.getMessage(errorKey, Map.of()); + Assert.assertEquals(errorKey, result); + } + + private void createTempFileWithTemplate(String errorKey, String template) { + try { + tmpFile = Files.createTempFile("error-template-", ".txt"); + Files.writeString(tmpFile, String.format("{ \"%s\": \"%s\" }", errorKey, template)); + propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(anyString())) + .thenReturn(tmpFile.toFile()); + } catch (IOException e) { + Assert.fail("Failed to create temporary file for testing: " + e.getMessage()); + } + } + + @Test + public void getMessage_shouldReturnExpandedMessageWhenTemplateExists() { + String errorKey = "error.key"; + String template = "Error occurred: {{field}}"; + Map metadata = Map.of("field", "value"); + createTempFileWithTemplate(errorKey, template); + when(callContextMock.getErrorContextParameters()).thenReturn(Map.of()); + String result = ResponseMessageResolver.getMessage(errorKey, metadata); + Assert.assertEquals("Error occurred: value", result); + } + + @Test + public void getMessage_shouldHandleEmptyMetadata() { + String errorKey = "error.key"; + String template = "Error occurred."; + createTempFileWithTemplate(errorKey, template); + String result = ResponseMessageResolver.getMessage(errorKey, Map.of()); + Assert.assertEquals("Error occurred.", result); + } + + @Test + public void getMessage_shouldHandleNullMetadata() { + String errorKey = "error.key"; + String template = "Error occurred."; + createTempFileWithTemplate(errorKey, template); + String result = ResponseMessageResolver.getMessage(errorKey, null); + Assert.assertEquals("Error occurred.", result); + } + + @Test + public void getMessage_shouldCombineContextAndProvidedMetadata() { + String errorKey = "error.key"; + String template = "Error in {{field1}} and {{field2}}."; + Map metadata = Map.of("field1", "value1"); + createTempFileWithTemplate(errorKey, template); + when(callContextMock.getErrorContextParameters()).thenReturn(Map.of("field2", "value2")); + String result = ResponseMessageResolver.getMessage(errorKey, metadata); + Assert.assertEquals("Error in value1 and value2.", result); + } + + @Test + public void updateExceptionResponse_shouldSetErrorTextAndMetadataWhenKeyAndTemplateExist() { + ExceptionResponse response = new ExceptionResponse(); + CloudRuntimeException cre = Mockito.mock(CloudRuntimeException.class); + String key = "error.key"; + String template = "Error occurred: {{field}}"; + Map metadata = Map.of("field", "value"); + + when(cre.getMessageKey()).thenReturn(key); + when(cre.getMetadata()).thenReturn(metadata); + createTempFileWithTemplate(key, template); + + ResponseMessageResolver.updateExceptionResponse(response, cre); + + Assert.assertEquals(key, response.getErrorTextKey()); + Assert.assertEquals("Error occurred: value", response.getErrorText()); + Assert.assertEquals(Map.of("field", "value"), response.getErrorMetadata()); + } + + @Test + public void updateExceptionResponse_shouldSetErrorTextKeyOnlyWhenTemplateDoesNotExist() { + ExceptionResponse response = new ExceptionResponse(); + CloudRuntimeException cre = Mockito.mock(CloudRuntimeException.class); + String key = "error.key"; + Map metadata = Map.of("field", "value"); + + when(cre.getMessageKey()).thenReturn(key); + when(cre.getMetadata()).thenReturn(metadata); + + ResponseMessageResolver.updateExceptionResponse(response, cre); + + Assert.assertEquals(key, response.getErrorTextKey()); + Assert.assertEquals(key, response.getErrorText()); + Assert.assertEquals(Map.of("field", "value"), response.getErrorMetadata()); + } + + @Test + public void updateExceptionResponse_shouldHandleNullKeyAndCauseIsNotInvalidParameterValueException() { + ExceptionResponse response = new ExceptionResponse(); + String originalErrorText = response.getErrorText(); + CloudRuntimeException cre = Mockito.mock(CloudRuntimeException.class); + + when(cre.getMessageKey()).thenReturn(null); + when(cre.getCause()).thenReturn(new RuntimeException()); + + ResponseMessageResolver.updateExceptionResponse(response, cre); + + Assert.assertNull(response.getErrorTextKey()); + Assert.assertNull(response.getErrorMetadata()); + Assert.assertEquals(originalErrorText, response.getErrorText()); + } + + @Test + public void updateExceptionResponse_shouldUseCauseMetadataWhenKeyIsNullAndCauseIsInvalidParameterValueException() { + ExceptionResponse response = new ExceptionResponse(); + InvalidParameterValueException cause = Mockito.mock(InvalidParameterValueException.class); + CloudRuntimeException cre = Mockito.mock(CloudRuntimeException.class); + String key = "error.key"; + String template = "Error occurred: {{field}}"; + Map metadata = Map.of("field", "value"); + + when(cre.getMessageKey()).thenReturn(null); + when(cre.getCause()).thenReturn(cause); + when(cause.getMessageKey()).thenReturn(key); + when(cause.getMetadata()).thenReturn(metadata); + createTempFileWithTemplate(key, template); + + ResponseMessageResolver.updateExceptionResponse(response, cre); + + Assert.assertEquals(key, response.getErrorTextKey()); + Assert.assertEquals("Error occurred: value", response.getErrorText()); + Assert.assertEquals(Map.of("field", "value"), response.getErrorMetadata()); + } + + @Test + public void getMetadataObjectStringValueAlt_shouldReturnNullWhenObjectIsNull() { + Assert.assertNull(ResponseMessageResolver.getMetadataObjectStringValueAlt(null)); + } + + @Test + public void getMetadataObjectStringValueAlt_shouldReturnToStringWhenNonEmptyForRootAdmin() { + when(callContextMock.isCallingAccountRootAdmin()).thenReturn(true); + Object obj = new Object() { + @Override public String toString() { return "SomeObject id: 42"; } + }; + Assert.assertEquals("SomeObject id: 42", ResponseMessageResolver.getMetadataObjectStringValueAlt(obj)); + } + + @Test + public void getMetadataObjectStringValueAlt_shouldStripIdPatternForNonRootAdmin() { + when(callContextMock.isCallingAccountRootAdmin()).thenReturn(false); + Object obj = new Object() { + @Override public String toString() { return "SomeObject id: 42"; } + }; + Assert.assertEquals("SomeObject", ResponseMessageResolver.getMetadataObjectStringValueAlt(obj)); + } + + @Test + public void getMetadataObjectStringValueAlt_shouldStripMultipleIdPatternsForNonRootAdmin() { + when(callContextMock.isCallingAccountRootAdmin()).thenReturn(false); + Object obj = new Object() { + @Override public String toString() { return "id: 1 SomeObject id: 42"; } + }; + Assert.assertEquals("SomeObject", ResponseMessageResolver.getMetadataObjectStringValueAlt(obj)); + } + + @Test + public void getMetadataObjectStringValueAlt_shouldReturnToStringUnchangedWhenNoIdPatternForNonRootAdmin() { + when(callContextMock.isCallingAccountRootAdmin()).thenReturn(false); + Object obj = new Object() { + @Override public String toString() { return "SomeObjectWithoutId"; } + }; + Assert.assertEquals("SomeObjectWithoutId", ResponseMessageResolver.getMetadataObjectStringValueAlt(obj)); + } + + @Test + public void getMetadataObjectStringValueAlt_shouldFallbackToGetMetadataObjectStringValueWhenToStringIsEmpty() { + Identity identityMock = Mockito.mock(Identity.class); + when(identityMock.toString()).thenReturn(""); + when(identityMock.getUuid()).thenReturn("uuid-fallback"); + Assert.assertEquals("uuid-fallback", ResponseMessageResolver.getMetadataObjectStringValueAlt(identityMock)); + } + + @Test + public void getMetadataObjectStringValueAlt_shouldFallbackWhenToStringBecomesBlankAfterStrippingIdForNonRootAdmin() { + when(callContextMock.isCallingAccountRootAdmin()).thenReturn(false); + DataCenter dataCenterMock = Mockito.mock(DataCenter.class); + when(dataCenterMock.toString()).thenReturn("id: 99"); + when(dataCenterMock.getName()).thenReturn("FallbackName"); + Assert.assertEquals("'FallbackName'", ResponseMessageResolver.getMetadataObjectStringValueAlt(dataCenterMock)); + } + + @Test + public void getMetadataObjectStringValueAlt_shouldPreserveIdInToStringForRootAdmin() { + when(callContextMock.isCallingAccountRootAdmin()).thenReturn(true); + Object obj = new Object() { + @Override public String toString() { return "Zone [id: 7, name: TestZone]"; } + }; + Assert.assertEquals("Zone [id: 7, name: TestZone]", ResponseMessageResolver.getMetadataObjectStringValueAlt(obj)); + } + + @Test + public void updateExceptionResponse_shouldHandleNullMetadata() { + ExceptionResponse response = new ExceptionResponse(); + CloudRuntimeException cre = Mockito.mock(CloudRuntimeException.class); + String key = "error.key"; + String template = "Error occurred."; + + when(cre.getMessageKey()).thenReturn(key); + when(cre.getMetadata()).thenReturn(null); + createTempFileWithTemplate(key, template); + + ResponseMessageResolver.updateExceptionResponse(response, cre); + + Assert.assertEquals(key, response.getErrorTextKey()); + Assert.assertEquals("Error occurred.", response.getErrorText()); + Assert.assertTrue(response.getErrorMetadata().isEmpty()); + } +} diff --git a/client/conf/error-messages.json.in b/client/conf/error-messages.json.in new file mode 100644 index 000000000000..af76add9ea15 --- /dev/null +++ b/client/conf/error-messages.json.in @@ -0,0 +1,655 @@ +{ + "vm.addnic.already.exists": "A NIC already exists for Instance {{instance}} in Network: {{network}}.", + "vm.addnic.cleanup.failed": "Error while cleaning up NIC: {{error}}.", + "vm.addnic.concurrent.operation": "Concurrent operations on adding NIC to Instance {{instance}}: {{error}}.", + "vm.addnic.hostname.duplicate": "Network {{network}} already has an Instance with host name: {{hostname}}.", + "vm.addnic.insufficient.capacity": "Insufficient capacity when adding NIC to Instance {{instance}}: {{error}}.", + "vm.addnic.ip.invalid": "Invalid format for IP address parameter {{ipAddress}}.", + "vm.addnic.mac.generation.failed": "A MAC address cannot be generated for the NIC in the Network {{network}}.", + "vm.addnic.network.nic.mac.exists": "Unable to add NIC to the Instance {{instance}} as a NIC with same MAC address {{macAddress}} already exists in the Network {{network}}.", + "vm.addnic.network.not.found": "Unable to add NIC to the Instance {{instance}} as the specified Network not found.", + "vm.addnic.network.sharedfs.account.scope": "Unable to add NIC to the Instance {{instance}} as shared network {{network}} which is not Account scoped and not belonging to the same account can not be added to a Shared FileSystem Instance.", + "vm.addnic.pending.jobs.exceeded": "There are too many pending jobs to add network {{network}} to Instance {{instance}}.", + "vm.addnic.resource.unavailable": "Unable to add NIC to the Instance {{instance}}: {{error}}.", + "vm.addnic.vm.having.snapshots": "Unable to add NIC to the Instance {{instance}} as it has Instance Snapshots.", + "vm.addnic.vm.not.found": "Unable to add NIC as the specified Instance is not found.", + "vm.addnic.zone.basic.not.allowed": "Zone {{zone}}, has a NetworkType of Basic. Can't add a new NIC to a Instance on a Basic Network.", + "vm.addnic.zone.mismatch": "Instance {{instance}} is in Zone {{instanceZone}} but the Network {{network}} is in Zone {{networkZone}}.", + "vm.allocate.concurrent.operation": "Concurrent operation occurred while trying to allocate resources for the Instance.", + "vm.assign.advanced.sg.no.network": "No network specified for moving the Instance. Please specify a network or add a network to the Instance.", + "vm.assign.applicable.network.not.found": "Could not find an applicable network to create virtual machine for account {{account}}.", + "vm.assign.basic.zone.network.ids.not.allowed": "Cannot move Instance with Network IDs; this is a basic zone Instance.", + "vm.assign.default.network.not.found": "Unable to find a default network to start the Instance.", + "vm.assign.domain.id.null": "Please provide a valid domain ID. Cannot assign VM to a project if domain ID is null.", + "vm.assign.loadbalancer.rules.exist": "Remove all Load Balancing rules for Instance {{instance}} before assigning it to another user.", + "vm.assign.multiple.default.networks": "More than one default isolated network found for account {{account}}. Please specify networkIDs.", + "vm.assign.network.implement.failed": "Failed to implement network {{network}} elements and resources as part of network provision.", + "vm.assign.network.not.found": "Unable to find network with specified ID.", + "vm.assign.network.offering.required.not.enabled": "Required network offering {{offering}} is not in Enabled state.", + "vm.assign.network.offering.required.not.found": "Unable to find a Required network offering to automatically create the network during VM assignment.", + "vm.assign.network.system.only": "Network {{network}} is system only and cannot be used for VM deployment.", + "vm.assign.networks.update.failed": "Unable to update networks when assigning Instance {{instance}}.", + "vm.assign.new.account.disabled": "The new account owner {{account}} is disabled.", + "vm.assign.new.account.invalid": "Invalid new account {{account}} for VM in domain {{domain}}.", + "vm.assign.old.account.invalid": "Invalid old account for VM in domain {{domain}}.", + "vm.assign.onetoonat.rule.exists": "Remove the One-to-One NAT rule for Instance {{instance}} and IP {{ip}} before assigning it.", + "vm.assign.permission.denied": "Only root or domain administrators are allowed to assign VMs. Caller {{caller}} is of type {{callerType}}.", + "vm.assign.physical.network.not.found": "Unable to find physical network with ID {{physicalNetworkId}} and tag {{tag}}.", + "vm.assign.portforwarding.rules.exist": "Remove all Port Forwarding rules for Instance {{instance}} before assigning it to another user.", + "vm.assign.same.account": "The new account {{account}} is the same as the old account.", + "vm.assign.securitygroup.vmware.not.supported": "Security group feature is not supported for VMware hypervisor.", + "vm.assign.securitygroup.zone.not.enabled": "Cannot move Instance with security groups: security group feature is not enabled in this zone.", + "vm.assign.sharedfs.not.supported": "Assignment is not supported for Shared FileSystem Instances.", + "vm.assign.staticnat.rules.exist": "Remove all Static NAT rules for Instance {{instance}} before assigning it to another user.", + "vm.assign.template.access.denied": "New owner does not have access to the template of Instance {{instance}}.", + "vm.assign.template.not.found": "Template for Instance {{instance}} cannot be found.", + "vm.assign.vm.not.found": "Unable to find Instance with specified ID for assignment.", + "vm.assign.vm.not.right.state": "Cannot move Instance {{instance}} in {{instanceState}} state.", + "vm.assign.volume.snapshots.exist": "Snapshots exist for volume {{volume}}. Detach the volume or remove all snapshots before assigning the Instance.", + "vm.backup.create.instance.not.supported": "Create instance from backup is not supported for this backup provider.", + "vm.backup.cross.zone.not.supported": "Create Instance from Backup across zones is not supported by this backup provider or repository.", + "vm.backup.disk.offering.compute.only": "Disk offering {{diskOffering}} is directly mapped to a service offering. Please provide an individual disk offering.", + "vm.backup.disk.offering.iso.only": "The diskofferingid parameter is only supported for creating instances from backup when using ISO. For templates, use datadisksdetails.", + "vm.backup.disk.offering.not.found": "Unable to find disk offering with specified ID.", + "vm.backup.not.found": "Backup with specified ID does not exist.", + "vm.backup.root.disk.size.smaller.than.backup": "Instance root disk size {{instanceSize}} GiB cannot be less than the backed-up volume size {{backupSize}} GiB.", + "vm.backup.service.offering.not.found": "Unable to find service offering with specified ID.", + "vm.backup.service.offering.uuid.missing": "Backup does not contain a Service Offering UUID. Please specify a valid Service Offering when creating the Instance.", + "vm.backup.service.offering.uuid.not.found": "Unable to find Service Offering from the UUID stored in the Backup. Please specify a valid Service Offering.", + "vm.backup.template.not.found": "Unable to find template with specified ID.", + "vm.backup.template.uuid.missing": "Backup does not contain a Template UUID. Please specify a valid Template or ISO when creating the Instance.", + "vm.backup.template.uuid.not.found": "Unable to find Template from the UUID stored in the Backup. Please specify a valid Template or ISO.", + "vm.backup.zone.not.found": "Unable to find zone with specified ID.", + "vm.deploy.advanced.vpc.hypervisor.not.supported": "Cannot create Instance of hypervisor type {{hypervisorType}} in VPC Network.", + "vm.deploy.advanced.vpc.template.hypervisor.not.supported": "Cannot create Instance from Template with hypervisor {{hypervisorType}} in VPC Network {{network}}.", + "vm.deploy.affinity.group.account.mismatch": "Affinity Group {{group}} does not belong to the VM's account.", + "vm.deploy.affinity.group.domain.mismatch": "Affinity Group {{group}} does not belong to the VM's domain.", + "vm.deploy.affinity.group.not.found": "Unable to find affinity group with specified ID.", + "vm.deploy.affinity.group.type.not.supported": "Affinity group type {{groupType}} is not supported for group {{group}}.", + "vm.deploy.backup.userdata.not.supported": "User data is not supported for instances created from a backup.", + "vm.deploy.basic.network.not.found": "Unable to find a default network to deploy the Instance.", + "vm.deploy.basic.zone.network.ids.not.allowed": "Network IDs cannot be specified when deploying in a Basic zone.", + "vm.deploy.datadisk.template.custom.offering.not.supported": "Custom disk offerings are not supported for datadisk Templates.", + "vm.deploy.datadisk.template.invalid": "Invalid datadisk Template ID specified.", + "vm.deploy.datadisk.template.not.child": "Datadisk Template {{dataDiskTemplate}} does not belong to Template {{template}}.", + "vm.deploy.datadisk.template.offering.invalid": "Invalid disk offering {{diskOffering}} specified for datadisk Template {{dataDiskTemplate}}.", + "vm.deploy.datadisk.template.offering.too.small": "Disk offering {{diskOffering}} size is smaller than datadisk Template {{dataDiskTemplate}} size.", + "vm.deploy.default.network.missing": "At least one default network must be specified for the Instance.", + "vm.deploy.default.network.multiple": "Only one default network per Instance is supported.", + "vm.deploy.detail.extraconfig.prefix.not.allowed": "Detail name should not start with 'extraconfig'.", + "vm.deploy.dhcp.options.network.no.nic": "Instance does not have a NIC in network {{network}} specified in the extra DHCP options.", + "vm.deploy.disk.offering.custom.size.required": "This disk offering requires a custom size to be specified.", + "vm.deploy.diskoffering.compute.only": "Unable to deploy Instance as the specified disk offering {{diskOffering}} is mapped to a service offering. Specify a disk offering that is not mapped to any service offering.", + "vm.deploy.diskoffering.local.storage.zone.unsupported": "Unable to deploy Instance because the zone is not configured to use local storage, but the specified disk offering {{diskOffering}} requires it.", + "vm.deploy.diskoffering.not.found": "Unable to deploy Instance as the specified disk offering is not found.", + "vm.deploy.diskoffering.with.diskoffering.details": "Unable to deploy Instance as both a disk offering ID and data disk offering details were provided. Specify only one.", + "vm.deploy.displayname.exists": "An Instance with the supplied display name already exists.", + "vm.deploy.group.assign.failed": "Unable to assign Instance to group {{group}}.", + "vm.deploy.hostname.exists": "An Instance with hostname {{hostname}} already exists.", + "vm.deploy.hostname.invalid": "Invalid hostname. Must be 1-63 chars, letters/digits/hyphens, cannot start or end with a hyphen or start with a digit.", + "vm.deploy.hostname.network.domain.exists": "An Instance with hostname {{hostname}} already exists in the network domain.", + "vm.deploy.hypervisor.mismatch": "The specified hypervisor type differs from the hypervisor type of the Template.", + "vm.deploy.hypervisor.missing.or.invalid": "Hypervisor parameter is required to deploy the VM, or the value provided is invalid.", + "vm.deploy.hypervisor.volume.snapshot.not.supported": "Unable to deploy Instance because deployment from an existing volume or snapshot is supported only on the KVM hypervisor.", + "vm.deploy.iops.both.or.neither": "Either both Min IOPS and Max IOPS must be specified, or neither.", + "vm.deploy.iops.max.not.integer": "Max IOPS must be a whole number.", + "vm.deploy.iops.min.greater.than.max": "Min IOPS must be less than or equal to Max IOPS.", + "vm.deploy.iops.min.not.integer": "Min IOPS must be a whole number.", + "vm.deploy.iso.disk.offering.required": "Installing from ISO requires a disk offering to be specified for the root disk.", + "vm.deploy.iso.not.bootable": "Installing from ISO requires a bootable ISO. The provided ISO {{iso}} is not bootable.", + "vm.deploy.keypairs.not.found": "Not all specified SSH keypairs exist.", + "vm.deploy.lease.duration.invalid": "Invalid lease duration. Must be a natural number >= 1 or -1; maximum value is 36500.", + "vm.deploy.lease.expired.redefinition": "Lease cannot be redefined on an expired leased instance.", + "vm.deploy.lease.no.active.lease": "Lease cannot be applied to Instance {{instance}}: it does not have an active lease associated from deployment.", + "vm.deploy.lease.not.pending": "Lease cannot be applied to Instance {{instance}}: the lease is not in PENDING state.", + "vm.deploy.lease.params.both.required": "Both leaseduration and leaseexpiryaction must be specified together.", + "vm.deploy.network.multiple.default.found": "More than one default Isolated network found for Account {{account}}; please specify networkIds.", + "vm.deploy.network.none.found.for.account": "No virtual networks found for Account {{account}}.", + "vm.deploy.network.not.found": "Unable to find network with specified ID.", + "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.network.not.stretched": "Network {{network}} cannot be stretched to zone {{zone}} for VM deployment.", + "vm.deploy.network.offering.required.not.enabled": "Required network offering {{offering}} is not in Enabled state.", + "vm.deploy.network.offering.required.not.found": "Unable to find a network offering with Required availability to automatically create the network as part of VM creation.", + "vm.deploy.network.provider.not.enabled.in.zone": "Network in which Instance is getting deployed could not be stretched to the zone as no valid physical network provider was found.", + "vm.deploy.network.system.only": "Network {{network}} is system only and cannot be used for VM deployment.", + "vm.deploy.network.zone.mismatch": "Network {{network}} does not belong to zone {{zone}}.", + "vm.deploy.ovf.default.network.not.found": "Default network not found for zone {{zone}} and account {{account}} for OVF network mapping.", + "vm.deploy.ovf.sg.network.not.found": "No network with security enabled found in zone {{zone}} for OVF network mapping.", + "vm.deploy.owner.disabled": "The owner of the Instance to deploy is disabled: {{owner}}.", + "vm.deploy.physical.network.not.found": "Unable to find physical network with ID {{physicalNetworkId}} and tag {{tag}}.", + "vm.deploy.resourcelimit.exceeded.account": "Unable to deploy Instance because allocating {{resourceRequested}} more {{resourceTypeDisplay}} would exceed the {{resourceOwnerType}} limits. Current: {{resourceAmount}}, Reserved: {{resourceReserved}}, Limit: {{resourceLimit}}. Release unused resources, then retry.", + "vm.deploy.resourcelimit.exceeded.account.admin": "Unable to deploy Instance because allocating {{resourceRequested}} more {{resourceTypeDisplay}} would exceed the {{resourceOwnerType}} limits for {{resourceOwner}}. Current: {{resourceAmount}}, Reserved: {{resourceReserved}}, Limit: {{resourceLimit}}. Release unused resources or increase the limit, then retry.", + "vm.deploy.resourcelimit.exceeded.domain": "Unable to deploy Instance because allocating {{resourceRequested}} more {{resourceTypeDisplay}} would exceed the domain limits. Current: {{resourceAmount}}, Reserved: {{resourceReserved}}, Limit: {{resourceLimit}}. Release unused resources, then retry.", + "vm.deploy.resourcelimit.exceeded.domain.admin": "Unable to deploy Instance because allocating {{resourceRequested}} more {{resourceTypeDisplay}} would exceed the limits for domain {{resourceOwnerDomain}}. Current: {{resourceAmount}}, Reserved: {{resourceReserved}}, Limit: {{resourceLimit}}. Release unused resources or increase the limit, then retry.", + "vm.deploy.root.disk.controller.invalid": "Unsupported root disk controller {{diskController}} for resizing.", + "vm.deploy.root.disk.size.not.positive": "Root disk size must be a positive number.", + "vm.deploy.root.disk.size.override.not.supported": "Hypervisor {{hypervisorType}} does not support root disk size override.", + "vm.deploy.root.disk.size.smaller.than.template": "Root disk size override ({{rootDiskSize}} GB) is smaller than the template size.", + "vm.deploy.root.encryption.hypervisor.not.supported": "Root volume encryption is not supported for hypervisor type {{hypervisorType}}.", + "vm.deploy.security.group.id.and.name.exclusive": "The securitygroupids and securitygroupnames parameters are mutually exclusive.", + "vm.deploy.security.group.name.not.found": "Unable to find security group with name {{groupName}}.", + "vm.deploy.security.group.not.found": "Unable to find security group with specified ID.", + "vm.deploy.securitygroup.service.not.enabled": "Cannot deploy Instance with security groups: SecurityGroup service is not enabled for the VM's network.", + "vm.deploy.securitygroup.vmware.not.supported": "Security group feature is not supported for VMware hypervisor.", + "vm.deploy.securitygroup.zone.not.enabled": "Cannot create Instance with security groups: security group feature is not enabled for this zone.", + "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}}.", + "vm.deploy.serviceoffering.inactive": "Unable to deploy Instance as the given service offering {{serviceOffering}} is inactive. Specify an active service offering.", + "vm.deploy.serviceoffering.local.storage.zone.unsupported": "Unable to deploy Instance because the zone is not configured to use local storage, but the disk offering mapped to service offering {{serviceOffering}} requires it.", + "vm.deploy.serviceoffering.not.found": "Unable to deploy Instance as the specified service offering is not found.", + "vm.deploy.serviceoffering.not.specified": "Unable to deploy Instance as the required parameter 'serviceofferingid' is missing.", + "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.sg.advanced.network.type.invalid": "Only Shared or L2 Guest networks can be specified when deploying a VM in an Advanced Security Group enabled zone.", + "vm.deploy.sg.multiple.networks.not.supported": "Only one network per VM is supported when security group is enabled.", + "vm.deploy.sg.network.not.found": "No network with security group enabled is found in zone {{zone}}.", + "vm.deploy.sg.network.not.sg.enabled": "Network {{network}} is not security group enabled and is not an L2 network.", + "vm.deploy.snapshot.not.found": "Unable to deploy Instance as the specified Snapshot is not found.", + "vm.deploy.snapshot.template.not.determinable": "Could not determine template from snapshot. The source volume no longer exists. Please specify a templateId.", + "vm.deploy.snapshot.zone.mismatch": "The snapshot does not exist in zone {{zone}}.", + "vm.deploy.sshkey.network.service.not.found": "Cannot deploy Instance with SSH keypair: the default network {{network}} does not support the UserData service.", + "vm.deploy.system.template.not.allowed": "System template {{template}} cannot be used to deploy a user VM.", + "vm.deploy.template.associated.not.usable": "Unable to deploy Instance as the associated Template cannot be used.", + "vm.deploy.template.deploy.as.is.boot.not.supported": "Boot type and boot mode are not supported on VMware for deploy-as-is templates.", + "vm.deploy.template.deploy.as.is.override.disk.size.not.supported": "Overriding root disk size is not supported for VMs deployed from deploy-as-is templates.", + "vm.deploy.template.deploy.as.is.root.disk.size.not.allowed": "Cannot deploy VM: a service offering with root disk size cannot be used with a deploy-as-is template.", + "vm.deploy.template.not.available": "Template {{template}} is not available for use.", + "vm.deploy.template.not.found": "Unable to deploy Instance as the specified Template is not found.", + "vm.deploy.template.not.found.by.id": "Unable to look up Template with id {{id}}.", + "vm.deploy.template.not.vnf": "Cannot deploy VNF appliance from a non-VNF template.", + "vm.deploy.template.password.network.service.not.found": "Cannot deploy Instance from password-enabled template {{template}}: the default network {{network}} does not support the UserData service.", + "vm.deploy.userdata.both.inputs": "Both userdata and userdata ID inputs are not allowed; please provide only one.", + "vm.deploy.userdata.network.service.not.found": "Cannot deploy Instance with UserData: the default network {{network}} does not support the UserData service.", + "vm.deploy.userdata.override.denied": "UserData input is not allowed since template {{template}} is configured to deny userdata override.", + "vm.deploy.userdata.policy.not.supported": "UserData policy {{policy}} is not supported.", + "vm.deploy.userdata.template.linked.not.found": "UserData linked to the template {{template}} is not found.", + "vm.deploy.volume.already.attached": "Volume {{volume}} is already attached to another VM.", + "vm.deploy.volume.not.found": "Unable to deploy Instance as the specified Volume is not found.", + "vm.deploy.volume.snapshot.no.template": "The provided volume or snapshot does not have a template for VM deployment.", + "vm.deploy.volume.storage.pool.zone.required": "VM deployment from a volume is supported only for Zone-wide storage pools.", + "vm.deploy.volume.template.mismatch": "The volume's template does not match the provided template.", + "vm.deploy.volume.zone.mismatch": "The volume's zone {{volumeZone}} does not match the provided zone {{zone}}.", + "vm.deploy.zone.disabled": "Cannot deploy Instance: zone {{zone}} is currently disabled.", + "vm.deploy.zone.domain.not.found": "Unable to find the domain for zone {{zone}}.", + "vm.deploy.zone.not.found": "Unable to deploy Instance as the specified zone is not found.", + "vm.destroy.delete.protection.enabled": "Instance {{instance}} has delete protection enabled and cannot be deleted.", + "vm.destroy.expunge.failed": "Failed to expunge Instance {{instance}}.", + "vm.destroy.expunge.state.transition.failed": "Unable to expunge Instance {{instance}}: it is not in the correct state.", + "vm.destroy.failed": "Failed to destroy Instance with specified ID.", + "vm.destroy.no.transition": "Unable to destroy Instance {{instance}}: {{error}}.", + "vm.destroy.sharedfs.not.supported": "Operation not supported on Shared FileSystem Instance.", + "vm.destroy.snapshots.delete.failed": "Unable to delete all snapshots for Instance {{instance}}.", + "vm.destroy.state.transition.failed": "Unable to destroy Instance {{instance}}: it is not in the correct state.", + "vm.destroy.unbacked.snapshots": "There are unbacked up snapshots on the ROOT volume. Instance destroy is not permitted; please try again later.", + "vm.destroy.vm.not.found": "Unable to find Instance with specified ID.", + "vm.destroy.volume.delete.protection": "Volume {{volume}} has delete protection enabled and cannot be deleted.", + "vm.destroy.volume.not.found": "Unable to find volume with specified ID.", + "vm.destroy.volume.type.invalid": "Please specify a volume of type DATADISK or ROOT.", + "vm.destroy.volumes.not.attached": "The supplied volumes are not DATADISK volumes attached to the Instance.", + "vm.expunge.admin.only": "Expunging an Instance can only be done by an Admin, or when allow.user.expunge.recover.vm is enabled.", + "vm.expunge.command.details.failed": "Unable to expunge Instance {{instance}}: {{error}}.", + "vm.expunge.command.failed": "Unable to expunge Instance {{instance}}: {{error}}.", + "vm.expunge.concurrent.operation": "Concurrent operation while expunging the Instance.", + "vm.expunge.destroy.first": "Please destroy the Instance before expunging it.", + "vm.expunge.failed": "Failed to expunge Instance with specified ID.", + "vm.expunge.operation.timeout": "Operation timed out while expunging the Instance.", + "vm.expunge.permission.denied": "Account does not have permission for expunging the Instance.", + "vm.expunge.sharedfs.not.supported": "Operation not supported on Shared FileSystem Instance.", + "vm.expunge.state.transition.failed": "Unable to expunge Instance {{instance}}: it is not in the correct state.", + "vm.expunge.vm.not.found": "Unable to find Instance with specified ID.", + "vm.extraconfig.account.not.enabled": "Additional VM configuration is not enabled for this account.", + "vm.extraconfig.config.incorrect": "An incorrect extra configuration '{{config}}' was passed.", + "vm.extraconfig.hypervisor.not.supported": "Hypervisor {{hypervisorType}} is not supported for additional VM configuration.", + "vm.extraconfig.kvm.key.not.allowed": "Extra config key '{{key}}' is not in the allowed list for KVM hypervisor.", + "vm.extraconfig.kvm.xml.parse.failed": "Failed to parse additional XML configuration.", + "vm.extraconfig.url.decode.failed": "Failed to decode URL-encoded extra config string.", + "vm.extraconfig.vmware.invalid.format": "The extra config string contains an invalid key/value pair pattern.", + "vm.extraconfig.vmware.key.not.allowed": "Extra config key '{{key}}' is not in the allowed list for VMware hypervisor.", + "vm.extraconfig.xenserver.invalid.format": "The extra config string contains an invalid key/value pair pattern.", + "vm.extraconfig.xenserver.key.not.allowed": "Extra config key '{{key}}' is not in the allowed list for XenServer hypervisor.", + "vm.forcereboot.failed": "Unable to force reboot the Instance {{instance}}.", + "vm.forcereboot.host.not.found": "Unable to force reboot the Instance {{instance}} as Host for it is not found.", + "vm.forcereboot.host.not.right.state": "Unable to force reboot the Instance {{instance}} as Host is not in the right state.", + "vm.forcereboot.host.not.right.state.admin": "Unable to force reboot the Instance {{instance}} as Host {{host}} is not in the right state. Current state: {{hostState}}.", + "vm.forcereboot.router.concurrent.start": "Unable to force reboot the Instance {{instance}} as concurrent operations on starting router: {{routerError}}.", + "vm.forcereboot.router.start.failed": "Unable to force reboot the Instance {{instance}} as starting router failed due to: {{routerError}}.", + "vm.gethypervisortype.vm.not.found": "Unable to find Instance with specified ID.", + "vm.group.add.lock.failed": "Failed to acquire lock on Instance group {{group}}.", + "vm.group.create.name.exists": "Unable to create Instance group: a group named {{groupName}} already exists for Account {{account}}.", + "vm.group.delete.not.found": "Unable to find Instance group with specified ID.", + "vm.import.cluster.id.null": "Unable to import Instance as the cluster ID could not be determined.", + "vm.import.cluster.not.enabled": "Unable to import Instance as the cluster {{cluster}} is not enabled.", + "vm.import.cluster.not.found": "Unable to import Instance as the cluster could not be found.", + "vm.import.cpu.mismatch": "Unable to import Instance as the CPU count {{cpu}} does not match the service offering {{serviceOffering}}.", + "vm.import.cpu.speed.mismatch": "Unable to import Instance as the CPU speed {{cpuSpeed}} does not match the service offering {{serviceOffering}}.", + "vm.import.data.disk.size.invalid": "Unable to import Instance as the size of data disk {{id}} is invalid.", + "vm.import.default.template.create.failed": "Unable to import Instance as creating the default template failed.", + "vm.import.deployment.destination.error": "Unable to import Instance {{name}} as finding the deployment destination failed.", + "vm.import.deployment.destination.not.found": "Unable to import Instance {{name}} as no suitable deployment destination was found.", + "vm.import.disk.controller.mismatch": "Unable to import Instance as the disk controller for disk {{disk}} does not match the expected controller {{controller}}.", + "vm.import.disk.details.not.found": "Unable to import Instance as the disk details could not be found for disk {{disk}}.", + "vm.import.disk.missing.offering": "Unable to import Instance as no offering was provided for disk {{disk}}.", + "vm.import.disk.offering.mapping.mismatch": "Unable to import Instance as the disk offering mapping does not match the available disks.", + "vm.import.disk.offering.not.found": "Unable to import Instance as the disk offering {{diskOffering}} could not be found.", + "vm.import.disk.offering.pool.incompatible": "Unable to import Instance as the disk offering {{diskOffering}} is not compatible with the storage pool.", + "vm.import.disk.offering.size.invalid": "Unable to import Instance as the disk offering {{diskOffering}} size {{size}} is invalid.", + "vm.import.disk.offering.too.small": "Unable to import Instance as the disk offering {{diskOffering}} is too small for the disk size {{size}}.", + "vm.import.disk.size.invalid": "Unable to import Instance as the size of disk {{id}} is invalid.", + "vm.import.disk.storage.pool.not.found": "Unable to import Instance as the storage pool for disk {{disk}} could not be found.", + "vm.import.domain.account.required": "Unable to import Instance as both domain ID and account name are required when importing as admin.", + "vm.import.error": "Unable to import Instance: {{error}}.", + "vm.import.extra.param.not.allowed": "Unable to import Instance as the extra parameter '{{param}}' is not in the allowed list.", + "vm.import.extra.params.disabled": "Unable to import Instance as extra parameters are disabled by configuration.", + "vm.import.extra.params.list.empty": "Unable to import Instance as the allowed extra parameters list is empty.", + "vm.import.guest.os.hypervisor.not.found": "Unable to import Instance as no hypervisor mapping was found for guest OS {{os}}.", + "vm.import.guest.os.not.found": "Unable to import Instance as the guest OS {{os}} could not be found.", + "vm.import.host.invalid": "Unable to import virtual machine: invalid host specified.", + "vm.import.host.offering.template.incompatible": "Unable to import Instance as the host, service offering, or template are not compatible.", + "vm.import.hostname.duplicate": "Unable to import Instance as the hostname {{hostname}} already exists in Zone {{zone}}.", + "vm.import.hostname.exists": "Unable to import Instance as the hostname {{hostname}} already exists.", + "vm.import.hostname.invalid": "Unable to import Instance as the hostname {{hostname}} is invalid.", + "vm.import.hostname.invalid.chars": "Unable to import Instance as the hostname {{hostname}} contains invalid characters.", + "vm.import.hypervisor.not.supported": "Unable to import Instance as the Hypervisor {{hypervisor}} is not supported for VM import.", + "vm.import.instance.name.empty": "Unable to import Instance as the instance name is empty.", + "vm.import.insufficient.capacity": "Unable to import Instance {{name}} due to insufficient capacity.", + "vm.import.invalid.cpu": "Unable to import Instance as the CPU value {{cpu}} is invalid.", + "vm.import.invalid.memory": "Unable to import Instance as the memory value {{memory}} is invalid.", + "vm.import.kvm.default.template.incompatible": "Unable to import Instance as the default template is not compatible with KVM.", + "vm.import.kvm.disk.in.use": "Unable to import Instance as the disk {{disk}} is already in use.", + "vm.import.kvm.disk.path.required": "Unable to import Instance as a disk path is required for KVM disk import.", + "vm.import.kvm.domain.account.required": "Unable to import Instance as both domain ID and account name are required for KVM import.", + "vm.import.kvm.host.not.found": "Unable to import Instance as the specified KVM host {{host}} could not be found.", + "vm.import.kvm.host.required": "Unable to import Instance as a KVM host is required.", + "vm.import.kvm.hostname.invalid.chars": "Unable to import KVM Instance as the hostname {{hostname}} contains invalid characters.", + "vm.import.kvm.hypervisor.not.supported": "Unable to import Instance as the KVM hypervisor is not supported for this operation.", + "vm.import.kvm.list.hypervisor.not.supported": "Unable to list unmanaged VMs as the KVM hypervisor is not supported for this listing.", + "vm.import.kvm.local.pool.not.found": "Unable to import Instance as no local storage pool could be found on the host.", + "vm.import.kvm.multiple.default.networks": "Unable to import Instance as multiple default networks were found.", + "vm.import.kvm.network.by.id.not.found": "Unable to import Instance as the network with ID {{id}} could not be found.", + "vm.import.kvm.network.required": "Unable to import Instance as a network is required for KVM import.", + "vm.import.kvm.network.system.only": "Unable to import Instance as the network {{id}} is a system-only network.", + "vm.import.kvm.network.zone.mismatch": "Unable to import Instance as the network {{id}} is in a different zone {{zone}}.", + "vm.import.kvm.no.virtual.network": "Unable to import Instance as no virtual network is available.", + "vm.import.kvm.offering.pool.incompatible": "Unable to import Instance as the offering is not compatible with the storage pool.", + "vm.import.kvm.physical.network.not.found": "Unable to import Instance as the physical network could not be found.", + "vm.import.kvm.remote.vm.not.found": "Unable to import Instance as the remote VM could not be found.", + "vm.import.kvm.required.network.offering.disabled": "Unable to import Instance as the required network offering is disabled.", + "vm.import.kvm.required.network.offering.not.found": "Unable to import Instance as the required network offering could not be found.", + "vm.import.kvm.storage.pool.not.found": "Unable to import Instance as the specified storage pool could not be found.", + "vm.import.kvm.storage.pool.required": "Unable to import Instance as a storage pool is required.", + "vm.import.kvm.username.required": "Unable to import Instance as a username is required for KVM import.", + "vm.import.memory.mismatch": "Unable to import Instance as the memory {{memory}} does not match the service offering {{serviceOffering}}.", + "vm.import.migration.check.failed": "Unable to import Instance as the migration check failed: {{error}}.", + "vm.import.migration.check.failed.vm": "Unable to import Instance {{instance}} as the migration check failed.", + "vm.import.migration.destination.error": "Unable to import Instance {{instance}} as finding the migration destination failed.", + "vm.import.migration.destination.not.found": "Unable to import Instance {{instance}} as no suitable migration destination was found.", + "vm.import.migration.failed": "Unable to import Instance {{instance}} as the migration failed.", + "vm.import.nic.adapter.mismatch": "Unable to import Instance as the adapter type for NIC {{nic}} does not match.", + "vm.import.nic.details.not.found": "Unable to import Instance as NIC details could not be found for NIC {{nic}}.", + "vm.import.nic.import.failed": "Unable to import Instance as importing NIC {{nic}} failed: {{error}}.", + "vm.import.nic.ip.not.available": "Unable to import Instance as the IP address for NIC {{nic}} is not available.", + "vm.import.nic.ip.required": "Unable to import Instance as an IP address is required for NIC {{nic}} in network {{network}}.", + "vm.import.nic.multiple.ips": "Unable to import Instance as multiple IP addresses were provided for NIC {{nic}}.", + "vm.import.nic.network.not.found": "Unable to import Instance as the network for NIC {{nic}} could not be found.", + "vm.import.nic.network.suitable.not.found": "Unable to import Instance as no suitable network could be found for NIC {{nic}}.", + "vm.import.nic.network.zone.mismatch": "Unable to import Instance as the network {{network}} for NIC {{nic}} is in a different zone.", + "vm.import.nic.pvlan.mismatch": "Unable to import Instance as the PVLAN type for NIC {{nic}} does not match.", + "vm.import.nic.vlan.mismatch": "Unable to import Instance as the VLAN for NIC {{nic}} does not match.", + "vm.import.nics.failed": "Unable to import NICs for Instance {{name}}: {{error}}.", + "vm.import.no.disks.found": "Unable to import Instance {{name}} as no attached disks were found.", + "vm.import.root.disk.not.found": "Unable to import Instance {{vm}} as the root disk could not be found or has no controller.", + "vm.import.root.disk.size.invalid": "Unable to import Instance as the root disk size for disk {{id}} is invalid.", + "vm.import.service.offering.id.not.found": "Unable to import Instance as the specified service offering is not found.", + "vm.import.service.offering.id.null": "Unable to import Instance as the service offering ID is not specified.", + "vm.import.service.offering.incompatible": "Unable to import Instance as the service offering {{serviceOffering}} is not compatible.", + "vm.import.service.offering.not.found": "Unable to import Instance as a matching service offering could not be found.", + "vm.import.template.not.found": "Unable to import Instance as the template could not be found.", + "vm.import.unmanaged.vm.details.not.found": "Unable to import Instance as the details for unmanaged VM {{name}} could not be retrieved.", + "vm.import.unmanaged.vm.not.found": "Unable to import Instance as the unmanaged VM {{name}} is not found.", + "vm.import.usage.publish.failed": "Unable to publish usage for imported Instance: {{error}}.", + "vm.import.usage.publish.failed.vm": "Unable to publish usage for imported Instance {{instance}}: {{error}}.", + "vm.import.vcenter.param.exclusive": "Unable to import Instance as 'vcenter' and 'existingvcenterid' parameters are mutually exclusive.", + "vm.import.vcenter.params.incomplete": "Unable to import Instance as vcenter, datacenter, username, and password are all required when specifying a new vCenter.", + "vm.import.vm.allocation.error": "Unable to import Instance as resource allocation failed for account {{owner}}: {{error}}.", + "vm.import.vm.create.failed": "Unable to import Instance {{name}} as VM creation failed.", + "vm.import.vm.failed": "Unable to import Instance {{name}}.", + "vm.import.vm.not.found": "Unable to import Instance as the VM could not be found.", + "vm.import.volume.allocation.error": "Unable to import Instance as volume allocation failed: {{error}}.", + "vm.import.volume.migration.failed": "Unable to import Instance as volume migration failed: {{error}}.", + "vm.import.volume.migration.no.pool": "Unable to import Instance as no storage pool is available for volume migration.", + "vm.import.volume.migration.no.pool.found": "Unable to import Instance as no suitable storage pool was found for volume migration.", + "vm.import.volumes.failed": "Unable to import volumes for Instance {{name}}: {{error}}.", + "vm.import.zone.invalid": "Unable to import virtual machine: invalid zone specified.", + "vm.migrate.affinity.conflict": "Unable to create migration deployment: affinity rules conflict.", + "vm.migrate.away.affinity.conflict": "Unable to create deployment for migrating Instance {{instance}}: affinity rules conflict.", + "vm.migrate.away.failed": "Unable to migrate Instance {{instance}}.", + "vm.migrate.away.no.host": "Unable to migrate Instance {{instance}} as it does not have a host id.", + "vm.migrate.away.vm.not.found": "Unable to find Instance to migrate away.", + "vm.migrate.checkpoint.recreation.failed": "Failed to recreate checkpoints after migration.", + "vm.migrate.command.details": "Migration command failed: {{error}}.", + "vm.migrate.concurrent.jobs.exceeded": "Number of concurrent migration jobs per datastore exceeded the threshold. Please try again after some time.", + "vm.migrate.cross.cluster.volume.not.zone.wide": "Source and destination hosts are not in the same cluster and not all volumes are on zone-wide primary store; unable to migrate.", + "vm.migrate.destination.different.pod": "Cannot migrate Instance: destination host is not in the same pod as the current host.", + "vm.migrate.destination.host.not.right.state": "Cannot migrate Instance: destination host {{host}} is not in the correct state.", + "vm.migrate.destination.not.found": "Unable to find a suitable destination to migrate Instance {{instance}}.", + "vm.migrate.destination.pool.tags.mismatch": "Destination storage pool does not support the volume's disk offering due to tag mismatch.", + "vm.migrate.dpdk.host.not.supported": "Cannot migrate DPDK-enabled Instance to a host that is not DPDK-enabled.", + "vm.migrate.gpu.not.supported": "Live migration of GPU-enabled Instance is not supported.", + "vm.migrate.host.maintenance.mode": "Operation {{operation}} on Instance {{instance}} is not allowed as the host is preparing for maintenance mode.", + "vm.migrate.host.source.prepare.failed": "Failed to prepare source host for migration of Instance {{instance}}.", + "vm.migrate.host.source.prepare.failed.admin": "Failed to prepare source host {{srcHost}} for migration of Instance {{instance}}: {{error}}.", + "vm.migrate.host.tags.mismatch": "Cannot migrate Instance: destination host {{host}} does not match the host tags of the service offering.", + "vm.migrate.hypervisor.not.supported": "Unsupported hypervisor type for VM migration. Supported: XenServer, VMware, KVM, OVM, Hyper-V, OVM3.", + "vm.migrate.local.storage.not.supported": "VM uses local storage and cannot be live migrated.", + "vm.migrate.managed.storage.no.access": "The target host does not have access to the volume's managed storage pool.", + "vm.migrate.managed.storage.pool.mismatch": "A volume on managed storage can only be migrated to itself.", + "vm.migrate.no.storage.pools.available": "There are no storage pools available at the target to migrate the volume.", + "vm.migrate.permission.denied": "No permission to migrate VM. Only root administrators can perform this operation.", + "vm.migrate.pool.answer.failed": "Instance {{instance}} migration failed: {{error}}.", + "vm.migrate.pool.no.results": "No relevant migration results found.", + "vm.migrate.server.not.found": "Unable to find a server to migrate the Instance to.", + "vm.migrate.source.host.not.found": "Cannot migrate VM: source host not found.", + "vm.migrate.state.transition.cancelled": "Migration cancelled: {{error}}.", + "vm.migrate.storage.access.groups.mismatch": "Destination pool {{pool}} does not have matching storage access groups as host {{host}}.", + "vm.migrate.storage.access.groups.mismatch.hosts": "Storage access groups on the source and destination hosts do not match.", + "vm.migrate.storage.access.groups.src.has.dest.none": "Source host has storage access groups but destination host has none.", + "vm.migrate.storage.data.disks.attached": "Data disks are attached to the Instance. Detach all data disks before migrating storage.", + "vm.migrate.storage.destination.host.not.right.state": "Cannot migrate VM with storage: destination host {{host}} is not in the correct state.", + "vm.migrate.storage.destination.hypervisor.not.supported": "Migration with storage is not supported for destination host {{host}} on hypervisor {{hypervisorType}} version {{version}}.", + "vm.migrate.storage.different.clusters": "VM disk migration failed: destination storage pools belong to different clusters.", + "vm.migrate.storage.hypervisor.mismatch": "Hypervisor mismatch for storage migration: destination has {{destHypervisor}}, VM requires {{vmHypervisor}}.", + "vm.migrate.storage.hypervisor.not.supported": "Unsupported hypervisor {{hypervisorType}} for VM migration with storage.", + "vm.migrate.storage.hypervisor.version.mismatch": "Source and destination hosts must have the same hypervisor type and version for migration with storage.", + "vm.migrate.storage.instance.snapshots.exist": "Instance cannot be migrated with storage because it has Instance Snapshots. Remove all Instance Snapshots first.", + "vm.migrate.storage.no.volume.migration": "Migration of Instance {{instance}} from host {{srcHost}} to {{destHost}} does not involve migrating any volumes.", + "vm.migrate.storage.nonuser.different.pods": "Storage migration of non-user VMs between storage pools in different pods is not supported.", + "vm.migrate.storage.nonuser.hypervisor.not.supported": "Unable to migrate storage of non-user VM for hypervisor {{hypervisorType}}.", + "vm.migrate.storage.permission.denied": "No permission to migrate VM storage. Only root administrators can perform this operation.", + "vm.migrate.storage.pool.not.found": "Storage pool with specified ID not found for migration.", + "vm.migrate.storage.snapshots.exist": "Instance disk cannot be migrated. Remove all Instance Snapshots before proceeding.", + "vm.migrate.storage.source.host.not.found": "Cannot migrate VM with storage: source host not found.", + "vm.migrate.storage.source.hypervisor.not.supported": "Migration with storage is not supported for source host {{host}} on hypervisor {{hypervisorType}} version {{version}}.", + "vm.migrate.storage.suitability.failed": "Storage suitability check failed for volume {{volume}}: {{error}}.", + "vm.migrate.storage.vm.already.on.host": "Cannot migrate VM with storage: Instance is already on the destination host. Please specify a different host.", + "vm.migrate.storage.vm.not.found": "Unable to find Instance with specified ID for storage migration.", + "vm.migrate.storage.vm.not.running": "Instance is not Running. Unable to migrate the Instance with storage.", + "vm.migrate.storage.vm.not.stopped": "Instance is not Stopped. Unable to perform storage migration.", + "vm.migrate.storage.volume.not.found": "Volume with specified ID not found for storage migration.", + "vm.migrate.storage.volume.not.ready": "Volume {{volume}} of the Instance is not in Ready state. Cannot proceed with storage migration.", + "vm.migrate.storage.volume.policy.noncompliant": "Storage pool {{pool}} is not compliant with the storage policy for volume {{volume}}.", + "vm.migrate.storage.volume.policy.verify.failed": "Failed to verify storage policy compliance for storage pool {{pool}}.", + "vm.migrate.storage.volume.pool.access.groups.mismatch": "Destination pool {{pool}} for volume {{volume}} does not have matching storage access groups as host {{host}}.", + "vm.migrate.storage.volume.pool.incompatible": "Cannot migrate volume {{volume}} to destination storage pool {{pool}}: incompatible hypervisor type.", + "vm.migrate.storage.volume.pool.unsuitable": "Unable to migrate {{volume}} to destination storage pool {{pool}}: {{error}}.", + "vm.migrate.storage.volume.vm.mismatch": "Volume {{volume}} does not belong to Instance {{instance}} being migrated.", + "vm.migrate.unbacked.snapshots": "There are unbacked up snapshots on volumes attached to this Instance. Migration is not permitted; please try again later.", + "vm.migrate.unregister.source.failed": "Failed to unregister Instance {{instance}} from source host {{host}} after migrating storage across datacenters.", + "vm.migrate.verification.failed": "Unable to complete migration for Instance {{instance}}: Instance not found on destination host.", + "vm.migrate.vm.already.on.host": "Cannot migrate Instance: the Instance is already on the destination host. Please specify a different host.", + "vm.migrate.vm.not.found": "Unable to find Instance with specified ID for migration.", + "vm.migrate.vm.not.running": "Instance is not Running. Unable to migrate the Instance.", + "vm.migrate.with.volume.empty.map": "Migration of the Instance does not involve migrating any volumes.", + "vm.migrate.with.volume.pool.no.access": "Cannot migrate the volume to the storage pool: the target host does not have access to the specified storage pool.", + "vm.migrate.with.volume.verification.failed": "Unable to complete migration with volumes for Instance {{instance}}: Instance not found on destination host.", + "vm.network.account.not.found": "Unable to find account for the Instance's network.", + "vm.network.domain.not.found": "Unable to find domain for the Instance's network.", + "vm.network.vpc.not.found": "Unable to find VPC for the Instance's network.", + "vm.network.zone.not.found": "Unable to find zone for the Instance's network.", + "vm.password.encrypt.failed": "Failed to encrypt the password for the Instance.", + "vm.reboot.active.snapshots": "Unable to reboot Instance {{instance}} as it has active Instance Snapshot tasks.", + "vm.reboot.answer.failed": "Unable to reboot Instance {{instance}} on host {{host}}: {{error}}.", + "vm.reboot.concurrent.operation": "Unable to reboot Instance due to a concurrent operation.", + "vm.reboot.host.not.found": "Unable to retrieve host for Instance {{instance}}.", + "vm.reboot.operation.timeout": "Failed to reboot Instance {{instance}} on host {{host}}: operation timed out.", + "vm.reboot.serviceoffering.not.found": "Unable to find Service Offering for the Instance.", + "vm.reboot.setup.menu.hypervisor.not.supported": "Booting into a hardware setup menu is not supported on hypervisor {{hypervisorType}}.", + "vm.reboot.vm.not.found": "Unable to find Instance with specified ID.", + "vm.reboot.vm.not.running": "The Instance {{instance}} is not running and cannot be rebooted.", + "vm.recover.account.deleted": "Unable to recover Instance as the Account is deleted.", + "vm.recover.permission.denied": "Recovering a vm can only be done by an Admin. Or when the allow.user.expunge.recover.vm key is set.", + "vm.recover.sharedfs.not.supported": "Operation not supported on Shared FileSystem Instance.", + "vm.recover.state.transition.failed": "Unable to recover the vm {{instance}} because it is not in the correct state. Current state: {{instanceState}}.", + "vm.recover.vm.not.destroyed": "Vm with id {{vmUuid}} is not in the right state.", + "vm.recover.vm.not.found": "Unable to find an Instance with id {{vmId}}.", + "vm.recover.vm.removed": "Unable to find vm by id {{vmUuid}}.", + "vm.removenic.concurrent.operation": "Concurrent operations on removing the Network {{network}} from the Instance {{instance}}: {{error}}.", + "vm.removenic.default.nic": "Unable to remove NIC {{nic}} from the Instance {{instance}} in Network {{network}} it is the default NIC for the Instance.", + "vm.removenic.failed": "Unable to remove the NIC {{nic}} from the Instance {{instance}}.", + "vm.removenic.network.not.found": "Unable to remove NIC {{nic}} from the Instance {{instance}} as a network for the NIC is not found.", + "vm.removenic.nic.not.attached": "Unable to remove NIC {{nic}} from the Instance {{instance}} as it is not attached.", + "vm.removenic.nic.not.found": "Unable to remove NIC from the Instance {{instance}} as the specified NIC is not found.", + "vm.removenic.nic.with.rules": "Unable to remove NIC {{nic}} from the Instance {{instance}} in Network {{network}}, as it has associated Port forwarding or Load balancer or Static NAT rules.", + "vm.removenic.resource.unavailable": "Unable to remove NIC for the Network {{network}} from the Instance {{instance}}: {{error}}.", + "vm.removenic.vm.having.snapshots": "Unable to remove NIC from the Instance {{instance}} as it has Instance Snapshots.", + "vm.removenic.vm.not.found": "Unable to remove NIC as the specified Instance is not found.", + "vm.removenic.zone.basic.not.allowed": "Zone {{zone}}, has a NetworkType of Basic. Can't remove a NIC from a Instance on a Basic Network.", + "vm.resetpassword.failed": "Failed to reset password for the Instance {{instance}}.", + "vm.resetpassword.network.service.not.found": "Unable to reset password for the Instance {{instance}} as its default Network {{network}} does not supports the service {{networkService}}.", + "vm.resetpassword.template.not.password.enabled": "Unable to reset password for the Instance {{instance}}, the Template {{template}} is not password enabled.", + "vm.resetpassword.vm.not.found": "Unable to reset password as the specified Instance is not found.", + "vm.resetpassword.vm.not.right.state": "Unable to reset password for the Instance {{instance}} as it is not in the right state. Current state: {{instanceState}}.", + "vm.resetpassword.vm.not.stopped": "Unable to reset password for the Instance {{instance}} as it is not in Stopped state. Current state: {{instanceState}}.", + "vm.resetsshkey.failed": "Failed to reset SSH Key for the Instance {{instance}}.", + "vm.resetsshkey.key.not.all.found": "Unable to reset SSH Key as the Instance {{instance}} as not all the specified keypairs found.", + "vm.resetsshkey.key.not.specified": "'keypair' or 'keypairs' must be specified.", + "vm.resetsshkey.network.service.not.found": "Unable to reset SSH Key for the Instance {{instance}} as its default Network {{network}} does not supports the service {{networkService}}.", + "vm.resetsshkey.vm.not.found": "Unable to reset SSH Key as the specified Instance is not found.", + "vm.resetsshkey.vm.not.right.state": "Unable to reset SSH Key for the Instance {{instance}} as it is not in the right state. Current state: {{instanceState}}.", + "vm.resetsshkey.vm.not.stopped": "Unable to reset SSH Key as the Instance {{instance}} as it is not in Stopped state. Current state {{instanceState}}.", + "vm.resetsshkey.vm.operation.not.supported": "Unable to reset SSH Key as the operation is not supported for Instance {{instance}}.", + "vm.resetsshkey.vm.sharedfs.operation.not.supported": "Unable to reset SSH Key as the operation is not supported for Shared FileSystem Instance {{instance}}.", + "vm.resetuserdata.vm.not.found": "Unable to reset userdata as the specified Instance is not found.", + "vm.resetuserdata.vm.not.stopped": "Unable to reset userdata as the Instance {{instance}} as it is not in Stopped state. Current state {{instanceState}}.", + "vm.resetuserdata.vm.operation.not.supported": "Unable to reset userdata as the operation is not supported for Instance {{instance}}.", + "vm.resetuserdata.vm.sharedfs.operation.not.supported": "Unable to reset userdata as the operation is not supported for Shared FileSystem Instance {{instance}}.", + "vm.resize.volume.current.diskoffering.not.found": "Could not find disk offering matching the current root offering ID.", + "vm.resize.volume.new.diskoffering.not.found": "Could not find disk offering for resizing the root volume.", + "vm.resize.volume.new.offering.smaller": "Failed to resize root volume. New Service Offering {{newOffering}} has a smaller disk size ({{newSize}} GB) than the current disk ({{currentSize}} GB).", + "vm.resize.volume.root.not.found": "Root volume for Instance not found while preparing resize.", + "vm.restore.disk.offering.not.found": "Cannot find disk offering with the specified ID.", + "vm.restore.disk.offering.size.smaller.than.template": "Disk size {{diskSize}} for the selected offering is less than the template size {{templateSize}}.", + "vm.restore.disk.offering.strict.mismatch": "The service offering has a strict disk offering requirement, and the specified disk offering does not match.", + "vm.restore.external.hypervisor.type.not.allowed": "Restore operation is not allowed for External hypervisor type.", + "vm.restore.failed": "Failed to restore Instance {{instance}}.", + "vm.restore.instance.not.supported": "Operation not supported for Instance {{instance}}.", + "vm.restore.iso.cannot.use.template": "This Instance was created from an ISO and cannot be re-installed with a template.", + "vm.restore.iso.not.attached": "Cannot restore the Instance: no ISO is attached to the VM.", + "vm.restore.managed.storage.hypervisor.not.supported": "This hypervisor type is not supported on managed storage for this command.", + "vm.restore.managed.storage.reset.failed": "Unable to reset Instance {{instance}} during managed storage operation.", + "vm.restore.managed.storage.send.failed": "Failed to send command to host for managed storage operation.", + "vm.restore.multiple.root.volumes": "There are {{count}} root volumes for Instance {{instance}}; expected at most one.", + "vm.restore.owner.disabled": "The owner of Instance {{instance}} is disabled.", + "vm.restore.owner.not.found": "The owner of Instance {{instance}} does not exist.", + "vm.restore.password.reset.failed": "VM restore is complete but failed to reset the password.", + "vm.restore.root.disk.size.smaller.than.template": "Root disk size {{rootDiskSize}} is smaller than the template size {{templateSize}}.", + "vm.restore.root.volume.not.found": "Cannot find root volume for Instance {{instance}}.", + "vm.restore.sharedfs.not.supported": "Operation not supported on Shared FileSystem Instance.", + "vm.restore.snapshots.exist": "Unable to restore Instance: please remove all Instance Snapshots first.", + "vm.restore.start.failed": "Unable to start Instance after restore.", + "vm.restore.stop.failed": "Failed to stop the Instance before restore.", + "vm.restore.template.bypassed.not.available": "Cannot restore Instance: bypassed template {{template}} is not available in the zone.", + "vm.restore.template.id.invalid": "Invalid template ID provided to restore the Instance.", + "vm.restore.template.iso.not.found": "Unable to find template or ISO for the specified volume and Instance.", + "vm.restore.template.linked.not.found": "Cannot find the template linked with the Instance.", + "vm.restore.template.not.available.in.zone": "Cannot restore Instance: template {{template}} is not available in the zone.", + "vm.restore.template.not.found": "Cannot find template with the specified ID.", + "vm.restore.unbacked.snapshots": "There are unbacked up snapshots on the ROOT volume. Re-install VM is not permitted; please try again later.", + "vm.restore.vm.not.found": "Unable to find Instance with specified ID.", + "vm.restore.vm.not.right.state": "Instance {{instance}} is in {{instanceState}} state. Restore can only be performed when the Instance is Running or Stopped.", + "vm.scale.affinity.conflict": "Unable to create deployment for scaling Instance {{instance}}: affinity rules conflict.", + "vm.scale.cross.cluster.not.supported": "Source and destination hosts are not in the same cluster; unable to migrate for scaling.", + "vm.scale.dynamic.scaling.disabled": "Dynamically scaling Instances is disabled for this zone, please contact your admin.", + "vm.scale.dynamic.scaling.not.supported": "Unable to scale {{instance}} as it does not have tools to support dynamic scaling.", + "vm.scale.hypervisor.simulator.root.resize.not.supported": "Hypervisor {{hypervisorType}} does not support volume resize.", + "vm.scale.insufficient.resources": "Unable to scale {{instance}} due to insufficient resources.", + "vm.scale.kvm.fixed.offering.not.supported": "Unable to live scale VM on KVM when current service offering is a Fixed Offering. KVM needs the tag maxMemory to live scale and it is only configured when VM is deployed with a custom service offering and Dynamic Scalable is enabled.", + "vm.scale.migrate.failed": "Unable to migrate Instance {{instance}} for scaling: {{error}}.", + "vm.scale.migrate.verification.failed": "Unable to complete migration for scaling Instance {{instance}}: Instance not found on destination.", + "vm.scale.no.host": "Unable to scale Instance {{instance}}: it does not have a host id.", + "vm.scale.reconfigure.answer.failed": "Unable to scale Instance: {{error}}.", + "vm.scale.root.volume.change.failed": "Failed to change disk offering of the root volume.", + "vm.scale.source.host.not.found": "Unable to find source host for Instance scaling migration.", + "vm.scale.up.required": "While the VM is running, only scalling up it is supported. New service offering {{newOffering}} should have at least one value (ram, speed or cpu) greater than the current values {{currentOffering}}.", + "vm.scale.vgpu.not.supported": "Dynamic scaling of vGPU type is not supported. VM has vGPU Type: {{vgpuType}}.", + "vm.scale.vm.not.found": "Unable to find Instance to scale.", + "vm.scale.vm.not.found.retry": "Unable to find Instance for scale migration.", + "vm.scale.vm.not.running": "Instance {{instance}} is not in Running state; unable to migrate for scaling.", + "vm.start.account.removed": "The caller account {{account}} has been removed.", + "vm.start.affinity.conflict": "Unable to deploy Instance: affinity rules associated with the Instance conflict.", + "vm.start.cluster.not.found": "Unable to find cluster with specified ID for VM deployment.", + "vm.start.cluster.root.admin.only": "Only root administrators can specify a cluster ID for VM deployment.", + "vm.start.concurrent.operation": "Unable to start Instance {{instance}} due to a concurrent operation.", + "vm.start.concurrent.operation.exposed": "Unable to start Instance {{instance}}: {{error}}.", + "vm.start.deployment.failed.exposed": "Unable to start Instance: {{error}}.", + "vm.start.external.prepare.failed": "Failed to prepare Instance for external provisioning.", + "vm.start.failed": "Unable to start Instance {{instance}}.", + "vm.start.host.capacity.insufficient": "Cannot deploy VM to specified host {{host}}: insufficient CPU or RAM capacity.", + "vm.start.host.guid.not.found": "Unable to start Instance: host GUID does not exist in the database.", + "vm.start.host.not.found": "Unable to find host with specified ID for VM deployment.", + "vm.start.host.not.right.state": "Cannot deploy Instance on host {{host}} as it is not in the correct state.", + "vm.start.host.root.admin.only": "Only root administrators can specify a host ID for VM deployment.", + "vm.start.insufficient.capacity": "Unable to start Instance {{instance}} due to insufficient capacity.", + "vm.start.insufficient.capacity.exposed": "Unable to start Instance {{instance}}: {{error}}.", + "vm.start.network.unavailable": "Network is unavailable. Please contact your administrator.", + "vm.start.network.unavailable.admin": "Network is unavailable. Failure: {{error}}.", + "vm.start.orchestrate.failed": "Unable to orchestrate the start of Instance {{instance}}.", + "vm.start.owner.disabled": "The owner of Instance {{instance}} is disabled.", + "vm.start.owner.not.found": "The owner of Instance {{instance}} does not exist.", + "vm.start.password.not.provided": "A valid password for the Instance was not provided.", + "vm.start.planner.not.found": "Cannot find deployment planner '{{plannerName}}'.", + "vm.start.pod.not.found": "Unable to find pod with specified ID for VM deployment.", + "vm.start.pod.root.admin.only": "Only root administrators can specify a pod ID for VM deployment.", + "vm.start.resource.unavailable": "Unable to start Instance {{instance}}: {{error}}.", + "vm.start.setup.menu.hypervisor.not.applicable": "Boot into setup mode is not applicable for hypervisor {{hypervisorType}}.", + "vm.start.systemvm.certificate.failed": "Failed to setup and secure agent for the system Instance {{instance}}.", + "vm.start.template.not.found": "Template for the Instance cannot be found. Instance configuration needs to be updated.", + "vm.start.template.zone.not.found": "Template for the Instance cannot be found in this zone. Instance configuration needs to be updated.", + "vm.start.vm.already.running": "The Instance {{instance}} is already running.", + "vm.start.vm.not.found": "Unable to find Instance with specified ID.", + "vm.start.wrong.state": "Cannot start Instance {{instance}} in state {{state}}.", + "vm.stop.account.removed": "The caller account has been removed and cannot be used.", + "vm.stop.agent.no.contact": "Unable to contact the agent to stop the Instance {{instance}}.", + "vm.stop.answer.failed": "Unable to stop Instance {{instance}}: {{error}}.", + "vm.stop.cleanup.failed": "Failed to cleanup Instance {{instance}}.", + "vm.stop.cleanup.state.change.failed": "Unable to stop Instance {{instance}}.", + "vm.stop.concurrent.operation": "Unable to stop Instance due to a concurrent operation.", + "vm.stop.final.state.transition.failed": "Unable to stop Instance {{instance}}.", + "vm.stop.force.permission.denied": "Account does not have permission to force stop the Instance.", + "vm.stop.forced.concurrent.operation": "Unable to stop Instance {{instance}} due to a concurrent operation.", + "vm.stop.host.not.found": "Unable to stop Instance {{instance}}: host is not available.", + "vm.stop.host.preparing.maintenance": "Stop operation on Instance {{instance}} is not allowed as the host is preparing for maintenance mode.", + "vm.stop.no.answer": "Unable to stop Instance {{instance}}: no answer received from the hypervisor.", + "vm.stop.no.transition": "Unable to stop Instance {{instance}}: {{error}}.", + "vm.stop.not.stopping.state": "Unable to proceed with stopping Instance {{instance}} as it is not in 'Stopping' state.", + "vm.stop.state.transition.failed": "Unable to stop Instance {{instance}}: the Instance is not in the correct state.", + "vm.stop.transaction.failed": "Unable to stop Instance {{instance}}.", + "vm.stop.vm.not.found": "Unable to find Instance with specified ID.", + "vm.stop.work.vm.not.found": "Unable to find Instance to stop.", + "vm.storage.migration.destination.failed": "Unable to migrate Instance: failed to create deployment destination with given volume to pool map.", + "vm.storage.migration.failed": "Failed to migrate storage for Instance {{instance}}: {{error}}.", + "vm.storage.migration.no.volume.mapping": "Unable to migrate Instance {{instance}}: missing volume to pool mapping.", + "vm.storage.migration.state.requested.failed": "Unable to migrate Instance {{instance}}.", + "vm.storage.migration.state.transition.failed": "Failed to change state of Instance {{instance}} after storage migration.", + "vm.unmanage.failed": "Failed to unmanage Instance {{instance}}.", + "vm.unmanage.kvm.command.failed": "Failed to send command to persist domain XML for Instance {{instance}}.", + "vm.unmanage.kvm.no.host": "No available host to persist domain XML for Instance {{instance}}.", + "vm.unmanage.kvm.persist.failed": "Failed to persist domain XML for Instance {{instance}}.", + "vm.unmanage.sharedfs.not.supported": "Unable to unmanage Instance {{instance}} as the operation is not supported for Shared FileSystem Instances.", + "vm.unmanage.unbacked.snapshots": "There are unbacked up snapshots on the ROOT volume. VM unmanage is not permitted; please try again later.", + "vm.unmanage.user.vm.not.found": "Unable to unmanage Instance as the user VM record is not found.", + "vm.unmanage.vm.invalid.state": "Unable to unmanage Instance {{instance}} as it is not in a valid state. Current state: {{state}}.", + "vm.unmanage.vm.not.found": "Unable to find Instance with specified ID for unmanage operation.", + "vm.unmanage.vm.not.right.state": "Instance {{instance}} is not running or stopped and cannot be unmanaged.", + "vm.unmanage.volume.not.attached": "Volume {{volume}} of Instance {{instance}} is not attached to the Instance.", + "vm.unmanage.volume.type.invalid": "Volume {{volume}} has invalid type {{volumeType}}: expected ROOT or DATADISK.", + "vm.update.detail.nondisplayable": "You are not allowed to add or edit the non-displayable setting: {{detailName}}.", + "vm.update.detail.readonly": "You are not allowed to add or edit the read-only setting: {{detailName}}.", + "vm.update.detail.restricted": "You are not allowed to add or edit the restricted setting: {{detailName}}.", + "vm.update.dns.hostname.failed": "Failed to update hostname of Instance {{instance}} to {{hostname}}.", + "vm.update.dns.hostname.update.failed": "Failed to update hostname for the Instance.", + "vm.update.dns.router.send.failed": "Unable to send commands to virtual router to update DNS for the Instance.", + "vm.update.dynamic.scaling.global.setting.disabled": "Dynamic Scaling cannot be enabled for the Instance as the corresponding global setting is disabled.", + "vm.update.dynamic.scaling.offering.not.supported": "Dynamic Scaling cannot be enabled for the Instance since its Service Offering does not support dynamic scaling.", + "vm.update.dynamic.scaling.template.not.supported": "Dynamic Scaling cannot be enabled for the Instance since its Template does not support dynamic scaling.", + "vm.update.extraconfig.disabled": "Cannot set extraconfig because enable.additional.vm.configuration is disabled.", + "vm.update.extraconfig.in.details": "'extraconfig' should not be included in the details map as a key.", + "vm.update.guestos.not.found": "Please specify a valid guest OS ID.", + "vm.update.ha.offering.disabled": "Cannot enable HA for the Instance as its Service Offering has HA disabled.", + "vm.update.instance.name.not.unique": "Instance name {{instanceName}} is not unique.", + "vm.update.lease.params.in.details": "Lease parameters should not be included in the details map as a key.", + "vm.update.non.dynamic.cpu.memory.not.updatable": "CPU number, memory, and CPU speed cannot be updated for a non-dynamic service offering.", + "vm.update.ova.details.change.not.allowed": "OVA detail settings are read from OVA and cannot be changed via API call.", + "vm.update.ova.details.cleanup.not.allowed": "OVA detail settings are read from OVA and cannot be cleaned up via API call.", + "vm.update.owner.not.found": "The owner of Instance {{instance}} does not exist.", + "vm.update.securitygroup.vm.not.stopped": "Instance {{instance}} must be stopped before updating security groups.", + "vm.update.securitygroup.vmware.not.supported": "Security group feature is not supported for VMware hypervisor.", + "vm.update.sharedfs.not.supported": "Operation not supported on Shared FileSystem Instance.", + "vm.update.userdata.failed": "Failed to reset userdata for the Instance.", + "vm.update.userdata.network.element.not.found": "Cannot find network element for UserData provider needed for userdata update.", + "vm.update.vm.not.found": "Unable to find virtual machine with specified ID.", + "vm.update.vm.not.right.state": "Instance {{instance}} is not in the right state for this operation.", + "vm.updatenic.default.nic.not.found": "Unable to find the default NIC for the Instance.", + "vm.updatenic.default.no.default": "Unable to update default NIC to {{nic}} for the Instance {{instance}} and now it has no default.", + "vm.updatenic.default.unknown.error": "Something strange happened, new default network {{newDefaultNetwork}} is not null, and is not equal to the network {{networkId}} of the chosen nic.", + "vm.updatenic.default.update.failed": "Setting a default NIC failed, and we had no default nic, but we were unable to set it back to the original.", + "vm.updatenic.job.exception": "Exception while updating Instance {{instance}} NIC.", + "vm.updatenic.job.exception.admin": "Exception while updating Instance {{instance}} NIC. Check logs for more information.", + "vm.updatenic.job.execution.failed": "Unexpected job execution result.", + "vm.updatenic.nic.already.default": "NIC {{nic}} is already the default for the Instance {{instance}}.", + "vm.updatenic.nic.network.not.found": "Unable to update NIC {{nic}} for the Instance {{instance}} as the Network for the NIC is not found.", + "vm.updatenic.nic.not.attached": "Unable to update NIC {{nic}} for the Instance {{instance}} as the NIC is not attached to the Instance.", + "vm.updatenic.nic.not.found": "Unable to update NIC for the Instance {{instance}} as the specified NIC is not found.", + "vm.updatenic.nic.profile.not.found": "Unable to update NIC {{nic}} for the Instance {{instance}} as failed to find a NIC profile for the existing default Network.", + "vm.updatenic.vm.having.snapshots": "Unable to update NIC for the Instance {{instance}} as it has Instance Snapshots.", + "vm.updatenic.vm.hypervisor.not.kvm": "Unable to update NIC {{nic}} for the Instance {{instance}} as NIC update is only supported with KVM hypervisor.", + "vm.updatenic.vm.not.found": "Unable to update NIC as the specified Instance is not found.", + "vm.updatenic.vm.not.running.or.stopped": "Unable to update NIC {{nic}} for the Instance {{instance}} as the Instance not Running or Stopped.", + "vm.updatenic.work.nic.not.found": "Unable to update NIC as the specified NIC is not found.", + "vm.updatenic.work.nic.not.found.admin": "Unable to update NIC as the specified NIC ID: {{nicId} is not found.", + "vm.updatenic.zone.basic.not.allowed": "Zone {{zone}}, has a NetworkType of Basic. Can't change default NIC on a Basic Network.", + "vm.updatenicip.allocation.failed.basic": "Allocating IP to guest NIC {{nicId}} failed, please choose another IP.", + "vm.updatenicip.allocation.failed.capacity": "Allocating IP to guest NIC {{nic}} failed, for insufficient address capacity.", + "vm.updatenicip.allocation.failed.choose.another": "Allocating IP to guest NIC {{nic}} failed, please choose another IP.", + "vm.updatenicip.datacenter.not.found": "There is no dc with the NIC.", + "vm.updatenicip.network.not.found": "There is no network with the nic.", + "vm.updatenicip.network.not.right.state": "Network is not in the right state to update vm nic ip. Correct states are: {{allowedStates}}.", + "vm.updatenicip.nic.not.found": "There is no nic for the {{nicId}}.", + "vm.updatenicip.nic.not.user.vm": "The nic is not belongs to user vm.", + "vm.updatenicip.not.supported.l2.network": "UpdateVmNicIpCmd is not supported in L2 network.", + "vm.updatenicip.offering.not.found": "There is no network offering with the network.", + "vm.updatenicip.pod.id.null": "Instance Pod ID is null in Basic zone; can't decide the range for IP allocation.", + "vm.updatenicip.vm.not.found": "There is no vm with the nic.", + "vm.updatenicip.vm.running": "VM is not Stopped, unable to update the vm nic having the specified id.", + "vm.upgrade.disk.offering.id.not.same": "Unable to Scale VM, since disk offering id associated with the old service offering is not same for new service offering.", + "vm.upgrade.disk.offering.strictness.mismatch": "Unable to Scale VM, since disk offering strictness flag is not same for new service offering and old service offering.", + "vm.upgrade.disk.offering.tags.mismatch": "Unable to upgrade Instance: the current service offering tags must be a subset of the new service offering tags.", + "vm.upgrade.dynamic.scaling.flag.mismatch": "Unable to Scale VM: since dynamic scaling enabled flag is not same for new service offering and old service offering.", + "vm.upgrade.external.hypervisor.not.supported": "Operation not supported for instance: {{instance}} as it is external hypervisor instance.", + "vm.upgrade.hypervisor.not.supported": "Scaling the VM dynamically is not supported for VMs running on Hypervisor {{hypervisorType}}.", + "vm.upgrade.insufficient.resources": "Unable to upgrade Instance: not enough resources available for the new service offering.", + "vm.upgrade.same.serviceoffering": "Not upgrading Instance {{instance}} since it already has the requested service offering {{serviceOffering}}.", + "vm.upgrade.serviceoffering.inactive": "Unable to upgrade Instance: service offering {{serviceOffering}} is inactive.", + "vm.upgrade.serviceoffering.not.active": "Unable to upgrade Instance {{instance}} with an inactive service offering {{serviceOffering}}.", + "vm.upgrade.serviceoffering.null": "Unable to upgrade Instance: new service offering cannot be null.", + "vm.upgrade.storage.local.to.shared": "Unable to upgrade Instance {{instance}}: target service offering uses local storage but the volume is in a shared storage pool.", + "vm.upgrade.storage.shared.to.local": "Unable to upgrade Instance {{instance}}: target service offering uses shared storage but the volume is in a local storage pool.", + "vm.upgrade.system.use.mismatch": "Unable to upgrade Instance: the system use property differs between the current and new service offering.", + "vm.upgrade.vm.not.right.state": "Unable to upgrade Instance {{instance}} in state {{instanceState}}; make sure the Instance is stopped.", + "vm.upgrade.vm.sunning.scale.down.unsupported": "Scaling down is not supported while the VM is running. The new service offering attributes {\"memory\": {{newMemory}}, \"CPU speed\": {{newSpeed}}, \"vCPUs\": {{newCPU}}} must not be lower than the current values {\"memory\": {{currentMemory}}, \"CPU speed\": {{currentSpeed}}, \"vCPUs\": {{currentCPU}}}.", + "vm.upgrade.vm.uuid.not.found": "Unable to find VM's UUID.", + "vm.upgrade.vm.wrong.state": "Unable to upgrade Instance {{instance}} in state {{instanceState}}; make sure the Instance is stopped or running.", + "vm.userdata.vm.not.found": "Unable to find Instance with specified ID.", + "vm.validate.serviceoffering.cpu.cores.not.dynamic": "CPU cores cannot be customized for service offering {{serviceOffering}} as they have been already predefined to {{cpuCores}}.", + "vm.validate.serviceoffering.cpu.speed.not.dynamic": "CPU speed cannot be customized for service offering {{serviceOffering}} as they have been already predefined to {{cpuSpeed}} MHz.", + "vm.validate.serviceoffering.custom.params.missing": "Need to specify custom parameter values cpu, cpu speed and memory when using custom service offering {{serviceOffering}}.", + "vm.validate.serviceoffering.invalid.cpu.cores": "Invalid cpu cores value for service offering {{serviceOffering}}, please choose another service offering with cpu cores between 1 and {{maxCPUCores}}.", + "vm.validate.serviceoffering.invalid.memory": "Invalid memory value for service offering {{serviceOffering}}, please choose another service offering with memory between 32 and {{maxMemory}} MB.", + "vm.validate.serviceoffering.invalid.params.cpu.cores": "Invalid CPU cores value for service offering {{serviceOffering}}, specify a value between {{minCPUCores}} and {{maxCPUCores}}.", + "vm.validate.serviceoffering.invalid.params.cpu.speed": "Invalid CPU speed value for service offering {{serviceOffering}}, specify a value between 1 and {{maxCPUSpeed}}.", + "vm.validate.serviceoffering.invalid.params.memory": "Invalid memory value for service offering {{serviceOffering}}, specify a value between {{minMemory}} and {{maxMemory}} MB.", + "vm.validate.serviceoffering.memory.not.dynamic": "Memory cannot be customized for service offering {{serviceOffering}} as it has been already predefined to {{memory}} MB.", + "vm.import.vddk.root.disk.offering.not.found": "Disk offering linked to service offering {{serviceOffering}} is not found.", + "vm.import.vddk.data.disk.offering.not.found": "Disk offering specified for disk {{disk}} is not found.", + "vm.import.vddk.root.disk.offering.incompatible": "The root disk offering {{diskOffering}} is not supported by the selected conversion storage pool {{storagePool}}. When using VDDK, all selected disk offerings must be compatible with the conversion storage pool, as it will become the primary storage for the imported volumes." + "vm.import.vddk.data.disk.offering.incompatible": "The data disk offering {{diskOffering}} is not supported by the selected conversion storage pool {{storagePool}}. When using VDDK, all selected disk offerings must be compatible with the conversion storage pool, as it will become the primary storage for the imported volumes." +} diff --git a/debian/cloudstack-management.install b/debian/cloudstack-management.install index befc7049c30e..a1f09003c734 100644 --- a/debian/cloudstack-management.install +++ b/debian/cloudstack-management.install @@ -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 diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index a232bdd05daa..b87d31b1f0fd 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -50,9 +50,6 @@ import javax.naming.ConfigurationException; import javax.persistence.EntityExistsException; -import com.cloud.agent.api.PostMigrationCommand; -import com.cloud.storage.clvm.ClvmPoolManager; -import com.cloud.hypervisor.KVMGuru; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -75,6 +72,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.error.Exceptions; import org.apache.cloudstack.framework.ca.Certificate; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; @@ -104,7 +102,6 @@ import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.utils.cache.SingleCache; import org.apache.cloudstack.utils.identity.ManagementServerNode; -import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.cloudstack.vm.UnmanagedVMsManager; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; @@ -134,11 +131,12 @@ import com.cloud.agent.api.PingRoutingCommand; import com.cloud.agent.api.PlugNicAnswer; import com.cloud.agent.api.PlugNicCommand; +import com.cloud.agent.api.PostMigrationCommand; +import com.cloud.agent.api.PreMigrationCommand; import com.cloud.agent.api.PrepareExternalProvisioningAnswer; import com.cloud.agent.api.PrepareExternalProvisioningCommand; import com.cloud.agent.api.PrepareForMigrationAnswer; import com.cloud.agent.api.PrepareForMigrationCommand; -import com.cloud.agent.api.PreMigrationCommand; import com.cloud.agent.api.RebootAnswer; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.RecreateCheckpointsCommand; @@ -209,7 +207,6 @@ import com.cloud.exception.ConnectionException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientServerCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; @@ -226,6 +223,7 @@ import com.cloud.hypervisor.HypervisorGuru; import com.cloud.hypervisor.HypervisorGuruBase; import com.cloud.hypervisor.HypervisorGuruManager; +import com.cloud.hypervisor.KVMGuru; import com.cloud.network.Network; import com.cloud.network.NetworkModel; import com.cloud.network.NetworkService; @@ -263,6 +261,7 @@ import com.cloud.storage.VolumeApiService; import com.cloud.storage.VolumeApiServiceImpl; import com.cloud.storage.VolumeVO; +import com.cloud.storage.clvm.ClvmPoolManager; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.GuestOSDao; @@ -579,7 +578,7 @@ public void allocate(final String vmInstanceName, final VirtualMachineTemplate t _networkMgr.allocate(vmProfile, auxiliaryNetworks, extraDhcpOptions); } } catch (final ConcurrentOperationException e) { - throw new CloudRuntimeException("Concurrent operation while trying to allocate resources for the VM", e); + throw Exceptions.cloudRuntimeException("vm.allocate.concurrent.operation", Map.of(), e); } logger.debug("Allocating disks for {}", persistedVm); @@ -673,9 +672,9 @@ public void expunge(final String vmUuid) throws ResourceUnavailableException { try { advanceExpunge(vmUuid); } catch (final OperationTimedoutException e) { - throw new CloudRuntimeException("Operation timed out", e); + throw Exceptions.cloudRuntimeException("vm.expunge.operation.timeout", Map.of(), e); } catch (final ConcurrentOperationException e) { - throw new CloudRuntimeException("Concurrent operation ", e); + throw Exceptions.cloudRuntimeException("vm.expunge.concurrent.operation", Map.of(), e); } } @@ -716,12 +715,12 @@ protected void advanceExpunge(VMInstanceVO vm) throws ResourceUnavailableExcepti try { if (!stateTransitTo(vm, VirtualMachine.Event.ExpungeOperation, vm.getHostId())) { logger.debug("Unable to expunge the vm because it is not in the correct state: " + vm); - throw new CloudRuntimeException("Unable to expunge " + vm); + throw Exceptions.cloudRuntimeException("vm.expunge.state.transition.failed", Map.of("instance", vm)); } } catch (final NoTransitionException e) { logger.debug("Unable to expunge the vm because it is not in the correct state: " + vm); - throw new CloudRuntimeException("Unable to expunge " + vm, e); + throw Exceptions.cloudRuntimeException("vm.expunge.state.transition.failed", Map.of("instance", vm), e); } logger.debug("Expunging vm " + vm); @@ -794,7 +793,7 @@ private void handleUnsuccessfulExpungeOperation(List finalizeExpungeCom for (final Answer answer : cmds.getAnswers()) { if (!answer.getResult()) { logger.warn("Failed to expunge vm due to: {}", answer.getDetails()); - throw new CloudRuntimeException(String.format("Unable to expunge %s due to %s", vm, answer.getDetails())); + throw Exceptions.cloudRuntimeException("vm.expunge.command.failed", Map.of("instance", vm, "error", answer.getDetails())); } } } @@ -823,7 +822,7 @@ protected void handleUnsuccessfulCommands(Commands cmds, VMInstanceVO vm) throws if (!answer.getResult()) { String message = String.format("Unable to expunge %s due to [%s].", vmToString, details); logger.error(message); - throw new CloudRuntimeException(message); + throw Exceptions.cloudRuntimeException("vm.expunge.command.details.failed", Map.of("instance", vmToString, "error", details)); } logger.debug("Commands [{}] to {} got answer [{}].", cmdsStr, vmToString, details); @@ -951,25 +950,22 @@ public void start(final String vmUuid, final Map doInTransaction(final if (state != State.Stopped) { String msg = String.format("Cannot start %s in %s state", vm, state); logger.warn(msg); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.start.wrong.state", Map.of("instance", vm, "state", state)); } } @@ -1171,13 +1167,13 @@ protected void checkIfTemplateNeededForCreatingVmVolumes(VMInstanceVO vm) { if (template == null) { String msg = "Template for the VM instance can not be found, VM instance configuration needs to be updated"; logger.error("{}. Template ID: {} seems to be removed", msg, vm.getTemplateId()); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.start.template.not.found"); } final VMTemplateZoneVO templateZoneVO = templateZoneDao.findByZoneTemplate(vm.getDataCenterId(), template.getId()); if (templateZoneVO == null) { String msg = "Template for the VM instance can not be found in the zone ID: %s, VM instance configuration needs to be updated"; logger.error("{}. {}", msg, template); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.start.template.zone.not.found"); } } @@ -1328,7 +1324,7 @@ protected void processPrepareExternalProvisioning(boolean firstStart, Host host, PrepareExternalProvisioningCommand cmd = new PrepareExternalProvisioningCommand(virtualMachineTO); cmd.setExternalDetails(externalDetails); Answer answer = null; - CloudRuntimeException cre = new CloudRuntimeException("Failed to prepare VM"); + CloudRuntimeException cre = Exceptions.cloudRuntimeException("vm.start.external.prepare.failed"); try { answer = _agentMgr.send(host.getId(), cmd); } catch (AgentUnavailableException | OperationTimedoutException e) { @@ -1474,7 +1470,7 @@ public void orchestrateStart(final String vmUuid, final Map unmanage(String vmUuid, Long paramHostId) { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); if (vm == null || vm.getRemoved() != null) { - throw new CloudRuntimeException("Could not find VM with id = " + vmUuid); + throw Exceptions.cloudRuntimeException("vm.unmanage.vm.not.found"); } final List pendingWorkJobs = _workJobDao.listPendingWorkJobs(VirtualMachine.Type.Instance, vm.getId()); @@ -2137,7 +2133,7 @@ Long persistDomainForKVM(VMInstanceVO vm, Long paramHostId) { if (agentHostId == null) { String errorMsg = "No available host to persist domain XML for Instance: " + vmName; logger.debug(errorMsg); - throw new CloudRuntimeException(errorMsg); + throw Exceptions.cloudRuntimeException("vm.unmanage.kvm.no.host", Map.of("instance", vm)); } } else { agentHostId = paramHostId; @@ -2154,12 +2150,12 @@ Long persistDomainForKVM(VMInstanceVO vm, Long paramHostId) { if (!answer.getResult()) { String errorMsg = "Failed to persist domain XML for Instance: " + vmName + " on host ID: " + agentHostId; logger.debug(errorMsg); - throw new CloudRuntimeException(errorMsg); + throw Exceptions.cloudRuntimeException("vm.unmanage.kvm.persist.failed", Map.of("instance", vm)); } } catch (AgentUnavailableException | OperationTimedoutException e) { String errorMsg = "Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + agentHostId; logger.error(errorMsg, e); - throw new CloudRuntimeException(errorMsg); + throw Exceptions.cloudRuntimeException("vm.unmanage.kvm.command.failed", Map.of("instance", vm)); } return agentHostId; } @@ -2493,7 +2489,7 @@ private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnabl if (hostId == null) { if (!cleanUpEvenIfUnableToStop) { logger.debug("HostId is null but this is not a forced stop, cannot stop vm {} with state: {}", vm, vm.getState()); - throw new CloudRuntimeException("Unable to stop " + vm); + throw Exceptions.cloudRuntimeException("vm.stop.host.not.found", Map.of("instance", vm)); } try { stateTransitTo(vm, Event.AgentReportStopped, null, null); @@ -2511,7 +2507,7 @@ private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnabl HostVO host = _hostDao.findById(hostId); if (!cleanUpEvenIfUnableToStop && vm.getState() == State.Running && host.getResourceState() == ResourceState.PrepareForMaintenance) { logger.debug("Host is in PrepareForMaintenance state - Stop VM operation on the VM: {} is not allowed", vm); - throw new CloudRuntimeException(String.format("Stop VM operation on the VM %s is not allowed as host is preparing for maintenance mode", vm)); + throw Exceptions.cloudRuntimeException("vm.stop.host.preparing.maintenance", Map.of("instance", vm)); } } @@ -2524,7 +2520,7 @@ private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnabl } } catch (final NoTransitionException e1) { if (!cleanUpEvenIfUnableToStop) { - throw new CloudRuntimeException("We cannot stop " + vm + " when it is in state " + vm.getState()); + throw Exceptions.cloudRuntimeException("vm.stop.state.transition.failed", Map.of("instance", vm)); } final boolean doCleanup = true; logger.warn("Unable to transition the state but we're moving on because it's forced stop", e1); @@ -2536,22 +2532,22 @@ private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnabl logger.debug("Updating work item to Done, id: {}", work.getId()); } if (!changeState(vm, Event.AgentReportStopped, null, work, Step.Done)) { - throw new CloudRuntimeException("Unable to stop " + vm); + throw Exceptions.cloudRuntimeException("vm.stop.cleanup.state.change.failed", Map.of("instance", vm)); } } catch (final NoTransitionException e) { logger.warn("Unable to cleanup {}", vm); - throw new CloudRuntimeException("Unable to stop " + vm, e); + throw Exceptions.cloudRuntimeException("vm.stop.cleanup.state.change.failed", Map.of("instance", vm), e); } } else { logger.debug("Failed to cleanup VM: {}", vm); - throw new CloudRuntimeException("Failed to cleanup " + vm + " , current state " + vm.getState()); + throw Exceptions.cloudRuntimeException("vm.stop.cleanup.failed", Map.of("instance", vm)); } } } if (vm.getState() != State.Stopping) { - throw new CloudRuntimeException("We cannot proceed with stop VM " + vm + " since it is not in 'Stopping' state, current state: " + vm.getState()); + throw Exceptions.cloudRuntimeException("vm.stop.not.stopping.state", Map.of("instance", vm)); } vmGuru.prepareStop(profile); @@ -2583,13 +2579,13 @@ private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnabl } stopped = answer.getResult(); if (!stopped) { - throw new CloudRuntimeException("Unable to stop the Instance due to " + answer.getDetails()); + throw Exceptions.cloudRuntimeException("vm.stop.answer.failed", Map.of("instance", vm, "error", answer.getDetails())); } vmGuru.finalizeStop(profile, answer); final GPUDeviceTO gpuDevice = stop.getGpuDevice(); _resourceMgr.updateGPUDetailsForVmStop(vm, gpuDevice); } else { - throw new CloudRuntimeException("Invalid answer received in response to a StopCommand on " + vm.instanceName); + throw Exceptions.cloudRuntimeException("vm.stop.no.answer", Map.of("instance", vm)); } } catch (AgentUnavailableException | OperationTimedoutException e) { @@ -2603,7 +2599,7 @@ private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnabl } catch (final NoTransitionException e) { logger.warn("Unable to transition the state " + vm, e); } - throw new CloudRuntimeException("Unable to stop " + vm); + throw Exceptions.cloudRuntimeException("vm.stop.final.state.transition.failed", Map.of("instance", vm)); } else { logger.warn("Unable to actually stop {} but continue with release because it's a force stop", vm); vmGuru.finalizeStop(profile, answer); @@ -2653,12 +2649,12 @@ public Boolean doInTransaction(TransactionStatus status) throws NoTransitionExce }); if (!result) { - throw new CloudRuntimeException("unable to stop " + vm); + throw Exceptions.cloudRuntimeException("vm.stop.transaction.failed", Map.of("instance", vm)); } } catch (final NoTransitionException e) { String message = String.format("Unable to stop %s due to [%s].", vm.toString(), e.getMessage()); logger.warn(message, e); - throw new CloudRuntimeException(message, e); + throw Exceptions.cloudRuntimeException("vm.stop.no.transition", Map.of("instance", vm, "error", e.getMessage()), e); } } @@ -2718,20 +2714,20 @@ public void doInTransactionWithoutResult(final TransactionStatus status) throws try { if (!stateTransitTo(vm, VirtualMachine.Event.DestroyRequested, vm.getHostId())) { logger.debug("Unable to destroy the vm because it is not in the correct state: {}", vm); - throw new CloudRuntimeException("Unable to destroy " + vm); + throw Exceptions.cloudRuntimeException("vm.destroy.state.transition.failed", Map.of("instance", vm)); } else { if (expunge) { backupManager.checkAndRemoveBackupOfferingBeforeExpunge(vm); if (!stateTransitTo(vm, VirtualMachine.Event.ExpungeOperation, vm.getHostId())) { logger.debug("Unable to expunge the vm because it is not in the correct state: {}", vm); - throw new CloudRuntimeException("Unable to expunge " + vm); + throw Exceptions.cloudRuntimeException("vm.destroy.expunge.state.transition.failed", Map.of("instance", vm)); } } } } catch (final NoTransitionException e) { String message = String.format("Unable to destroy %s due to [%s].", vm.toString(), e.getMessage()); logger.debug(message, e); - throw new CloudRuntimeException(message, e); + throw Exceptions.cloudRuntimeException("vm.destroy.no.transition", Map.of("instance", vm, "error", e.getMessage()), e); } } }); @@ -2748,7 +2744,7 @@ private void deleteVMSnapshots(VMInstanceVO vm, boolean expunge) { if (! vm.getHypervisorType().equals(HypervisorType.VMware)) { if (!_vmSnapshotMgr.deleteAllVMSnapshots(vm.getId(), null)) { logger.debug("Unable to delete all Snapshots for {}", vm); - throw new CloudRuntimeException("Unable to delete Instance Snapshots for " + vm); + throw Exceptions.cloudRuntimeException("vm.destroy.snapshots.delete.failed", Map.of("instance", vm)); } } else { @@ -2829,14 +2825,14 @@ private void orchestrateStorageMigration(final String vmUuid, final Map results = relevantAnswer.getVolumeTos(); if (results == null) { @@ -2944,7 +2940,7 @@ private void migrateThroughHypervisorOrStorage(VMInstanceVO vm, Map prepareVmStorageMigration(VMInstanceVO vm, Map volumeToPool) { Map volumeToPoolMap = new HashMap<>(); if (MapUtils.isEmpty(volumeToPool)) { - throw new CloudRuntimeException(String.format("Unable to migrate %s: missing volume to pool mapping.", vm.toString())); + throw Exceptions.cloudRuntimeException("vm.storage.migration.no.volume.mapping", Map.of("instance", vm)); } Cluster cluster = null; Long dataCenterId = null; @@ -2966,7 +2962,7 @@ private Map prepareVmStorageMigration(VMInstanceVO vm, Map< if (dataCenterId == null) { String msg = "Unable to migrate Instance: failed to create deployment destination with given volume to pool map"; logger.debug(msg); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.storage.migration.destination.failed"); } final DataCenterDeployment destination = new DataCenterDeployment(dataCenterId, podId, clusterId, null, null, null); // Create a map of which volume should go in which storage pool. @@ -2977,7 +2973,7 @@ private Map prepareVmStorageMigration(VMInstanceVO vm, Map< } catch (final NoTransitionException e) { String msg = String.format("Unable to migrate Instance: %s", vm.getUuid()); logger.warn(msg, e); - throw new CloudRuntimeException(msg, e); + throw Exceptions.cloudRuntimeException("vm.storage.migration.state.requested.failed", Map.of("instance", vm), e); } return volumeToPoolMap; } @@ -2990,13 +2986,7 @@ private void checkDestinationForTags(StoragePool destPool, VMInstanceVO vm) { DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId()); List volumeTags = StringUtils.csvTagsToList(diskOffering.getTags()); if(! matches(volumeTags, storageTags)) { - String msg = String.format("destination pool '%s' with tags '%s', does not support the volume diskoffering for volume '%s' (tags: '%s') ", - destPool.getName(), - StringUtils.listToCsvTags(storageTags), - vol.getName(), - StringUtils.listToCsvTags(volumeTags) - ); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.migrate.destination.pool.tags.mismatch"); } } } @@ -3064,9 +3054,7 @@ private void removeStaleVmFromSource(VMInstanceVO vm, HostVO srcHost) { try { _agentMgr.send(srcHost.getId(), uvc); } catch (AgentUnavailableException | OperationTimedoutException e) { - throw new CloudRuntimeException(String.format( - "Failed to unregister VM: %s from source host: %s after successfully migrating VM's storage across VMware Datacenters", - vm, srcHost), e); + throw Exceptions.cloudRuntimeException("vm.migrate.unregister.source.failed", Map.of("instance", vm, "host", srcHost), e); } } @@ -3102,7 +3090,7 @@ private void orchestrateMigrate(final String vmUuid, final long srcHostId, final final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); if (vm == null) { logger.debug("Unable to find the Instance {}", vmUuid); - throw new CloudRuntimeException("Unable to find a Instance with ID: " + vmUuid); + throw Exceptions.cloudRuntimeException("vm.migrate.vm.not.found"); } migrate(vm, srcHostId, dest); } @@ -3113,7 +3101,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy final Host fromHost = _hostDao.findById(srcHostId); if (fromHost == null) { logger.info("Unable to find the host to migrate from: {}", srcHostId); - throw new CloudRuntimeException("Unable to find the host to migrate from: " + srcHostId); + throw Exceptions.cloudRuntimeException("vm.migrate.source.host.not.found"); } if (fromHost.getClusterId() != dest.getCluster().getId() && vm.getHypervisorType() != HypervisorType.VMware) { @@ -3122,9 +3110,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy if (!_storagePoolDao.findById(volume.getPoolId()).getScope().equals(ScopeType.ZONE)) { logger.info("Source and destination host are not in same cluster and all volumes are not on zone wide primary store, unable to migrate to host: {}", dest.getHost()); - throw new CloudRuntimeException(String.format( - "Source and destination host are not in same cluster and all volumes are not on zone wide primary store, unable to migrate to host: %s", - dest.getHost())); + throw Exceptions.cloudRuntimeException("vm.migrate.cross.cluster.volume.not.zone.wide"); } } } @@ -3133,7 +3119,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy if (vm.getState() != State.Running) { logger.debug("VM is not Running, unable to migrate the vm {}", vm); - throw new CloudRuntimeException("VM is not Running, unable to migrate the vm currently " + vm + " , current state: " + vm.getState().toString()); + throw Exceptions.cloudRuntimeException("vm.migrate.vm.not.running"); } AlertManager.AlertType alertType = AlertManager.AlertType.ALERT_TYPE_USERVM_MIGRATE; @@ -3225,7 +3211,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy } catch (final NoTransitionException e3) { logger.warn(e3.getMessage()); } - throw new CloudRuntimeException("Migration cancelled because " + e2.getMessage()); + throw Exceptions.cloudRuntimeException("vm.migrate.state.transition.cancelled", Map.of("error", e2.getMessage())); } boolean migrated = false; @@ -3240,7 +3226,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy String msg = String.format("Migration command failed for VM %s on source host id=%s to destination host %s: %s", vm.getInstanceName(), vm.getLastHostId(), dstHostId, details); logger.error(msg); - throw new CloudRuntimeException(details); + throw Exceptions.cloudRuntimeException("vm.migrate.command.details", Map.of("error", details)); } logger.info("Migration command successful for VM {}", vm.getInstanceName()); } catch (final OperationTimedoutException e) { @@ -3286,7 +3272,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy logger.error("AgentUnavailableException while cleanup on source host: {}", fromHost, e); } cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.AgentReportStopped, true); - throw new CloudRuntimeException("Unable to complete migration for " + vm); + throw Exceptions.cloudRuntimeException("vm.migrate.verification.failed", Map.of("instance", vm)); } } catch (final OperationTimedoutException e) { logger.warn("Error while checking the vm {} on host {}", vm, dest.getHost(), e); @@ -3477,9 +3463,7 @@ protected Map buildMapUsingUserInformation(VirtualMachinePr executeManagedStorageChecksWhenTargetStoragePoolProvided(currentPool, volume, targetPool); if (targetHost != null && _poolHostDao.findByPoolHost(targetPool.getId(), targetHost.getId()) == null) { - throw new CloudRuntimeException( - String.format("Cannot migrate the volume [%s] to the storage pool [%s] while migrating VM [%s] to target host [%s]. The host does not have access to the storage pool entered.", - volume.getUuid(), targetPool.getUuid(), profile.getUuid(), targetHost.getUuid())); + throw Exceptions.cloudRuntimeException("vm.migrate.with.volume.pool.no.access"); } if (currentPool.getId() == targetPool.getId()) { logger.info("The volume [{}] is already allocated in storage pool [{}].", volume.getUuid(), targetPool.getUuid()); @@ -3509,8 +3493,7 @@ protected void executeManagedStorageChecksWhenTargetStoragePoolProvided(StorageP if (details != null && Boolean.parseBoolean(details.get(Storage.Capability.ALLOW_MIGRATE_OTHER_POOLS.toString()))) { return; } - throw new CloudRuntimeException(String.format("Currently, a volume on managed storage can only be 'migrated' to itself " + "[volumeId=%s, currentStoragePoolId=%s, targetStoragePoolId=%s].", - volume.getUuid(), currentPool.getUuid(), targetPool.getUuid())); + throw Exceptions.cloudRuntimeException("vm.migrate.managed.storage.pool.mismatch"); } /** @@ -3558,8 +3541,7 @@ protected void executeManagedStorageChecksWhenTargetStoragePoolNotProvided(Host return; } if (targetHost != null && _poolHostDao.findByPoolHost(currentPool.getId(), targetHost.getId()) == null) { - throw new CloudRuntimeException(String.format("The target host does not have access to the volume's managed storage pool. [volumeId=%s, storageId=%s, targetHostId=%s].", volume.getUuid(), - currentPool.getUuid(), targetHost.getUuid())); + throw Exceptions.cloudRuntimeException("vm.migrate.managed.storage.no.access"); } } @@ -3592,7 +3574,7 @@ protected void createVolumeToStoragePoolMappingIfPossible(VirtualMachineProfile Cluster targetCluster = _clusterDao.findById(plan.getClusterId()); msg = String.format("There are no storage pools available in the target cluster [%s] to migrate volume [%s]", targetCluster.getUuid(), volume.getUuid()); } - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.migrate.no.storage.pools.available"); } Collections.shuffle(storagePoolList); @@ -3715,8 +3697,7 @@ private void orchestrateMigrateWithStorage(final String vmUuid, final long srcHo final Map volumeToPoolMap = createMappingVolumeAndStoragePool(profile, destHost, volumeToPool); if (volumeToPoolMap == null || volumeToPoolMap.isEmpty()) { - throw new InvalidParameterValueException("Migration of the vm " + vm + "from host " + srcHost + " to destination host " + destHost + - " doesn't involve migrating the volumes."); + throw Exceptions.invalidParameterValueException("vm.migrate.with.volume.empty.map"); } AlertManager.AlertType alertType = AlertManager.AlertType.ALERT_TYPE_USERVM_MIGRATE; @@ -3789,7 +3770,7 @@ private void orchestrateMigrateWithStorage(final String vmUuid, final long srcHo logger.error("AgentUnavailableException while cleanup on source host: {}", srcHost, e); } cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.AgentReportStopped, true); - throw new CloudRuntimeException("VM not found on destination host. Unable to complete migration for " + vm); + throw Exceptions.cloudRuntimeException("vm.migrate.with.volume.verification.failed", Map.of("instance", vm)); } } catch (final OperationTimedoutException e) { logger.error("Error while checking the vm {} is on host {}", vm, destHost, e); @@ -3854,7 +3835,7 @@ protected void recreateCheckpointsKvmOnVmAfterMigration(VMInstanceVO vm, long ho answer = _agentMgr.send(hostId, recreateCheckpointsCommand); } catch (AgentUnavailableException | OperationTimedoutException e) { logger.error(String.format("Exception while sending command to host [%s] to recreate checkpoints with URLs [%s] of volumes [%s] on %s due to: [%s].", hostId, volumes.stream().map(VolumeObjectTO::getCheckpointPaths).collect(Collectors.toList()), volumes, vm, e.getMessage()), e); - throw new CloudRuntimeException(e); + throw Exceptions.cloudRuntimeException("vm.migrate.checkpoint.recreation.failed", Map.of(), e); } finally { if (answer != null && answer.getResult()) { logger.debug(String.format("Successfully recreated checkpoints on VM [%s].", vm)); @@ -3970,7 +3951,7 @@ private void orchestrateMigrateAway(final String vmUuid, final long srcHostId, f if (vm == null) { String message = String.format("Unable to find VM with uuid [%s].", vmUuid); logger.warn(message); - throw new CloudRuntimeException(message); + throw Exceptions.cloudRuntimeException("vm.migrate.away.vm.not.found"); } ServiceOfferingVO offeringVO = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); @@ -3980,7 +3961,7 @@ private void orchestrateMigrateAway(final String vmUuid, final long srcHostId, f if (hostId == null) { String message = String.format("Unable to migrate %s due to it does not have a host id.", vm.toString()); logger.warn(message); - throw new CloudRuntimeException(message); + throw Exceptions.cloudRuntimeException("vm.migrate.away.no.host", Map.of("instance", vm)); } final Host host = _hostDao.findById(hostId); @@ -4006,7 +3987,7 @@ private void orchestrateMigrateAway(final String vmUuid, final long srcHostId, f } catch (final AffinityConflictException e2) { String message = String.format("Unable to create deployment, affinity rules associated to the %s conflict.", vm.toString()); logger.warn(message, e2); - throw new CloudRuntimeException(message, e2); + throw Exceptions.cloudRuntimeException("vm.migrate.away.affinity.conflict", Map.of("instance", vm), e2); } if (dest == null) { logger.warn("Unable to find destination for migrating the vm {}", profile); @@ -4024,10 +4005,10 @@ private void orchestrateMigrateAway(final String vmUuid, final long srcHostId, f try { advanceStop(vmUuid, true); - throw new CloudRuntimeException("Unable to migrate " + vm); + throw Exceptions.cloudRuntimeException("vm.migrate.away.failed", Map.of("instance", vm)); } catch (final ResourceUnavailableException | ConcurrentOperationException | OperationTimedoutException e) { logger.error("Unable to stop {} due to [{}].", vm.toString(), e.getMessage(), e); - throw new CloudRuntimeException("Unable to migrate " + vm); + throw Exceptions.cloudRuntimeException("vm.migrate.away.failed", Map.of("instance", vm)); } } } @@ -4086,7 +4067,7 @@ public void reboot(final String vmUuid, final Map currentTags = StringUtils.csvTagsToList(currentDiskOffering.getTags()); final List newTags = StringUtils.csvTagsToList(newDiskOffering.getTags()); if (VolumeApiServiceImpl.MatchStoragePoolTagsWithDiskOffering.valueIn(vmInstance.getDataCenterId())) { if (!VolumeApiServiceImpl.doesNewDiskOfferingHasTagsAsOldDiskOffering(currentDiskOffering, newDiskOffering)) { - throw new InvalidParameterValueException("Unable to upgrade virtual machine; the current service offering " + " should have tags as subset of " + - "the new service offering tags. Current service offering tags: " + currentTags + "; " + "new service " + "offering tags: " + newTags); + throw Exceptions.invalidParameterValueException("vm.upgrade.disk.offering.tags.mismatch"); } } } @@ -4537,15 +4513,11 @@ protected void checkIfNewOfferingStorageScopeMatchesStoragePool(VirtualMachine v boolean isRootVolumeOnLocalStorage = isRootVolumeOnLocalStorage(vmInstance.getId()); if (newDiskOffering.isUseLocalStorage() && !isRootVolumeOnLocalStorage) { - String message = String .format("Unable to upgrade virtual machine %s, target offering use local storage but the storage pool where " - + "the volume is allocated is a shared storage.", vmInstance.toString()); - throw new InvalidParameterValueException(message); + throw Exceptions.invalidParameterValueException("vm.upgrade.storage.local.to.shared", Map.of("instance", vmInstance)); } if (!newDiskOffering.isUseLocalStorage() && isRootVolumeOnLocalStorage) { - String message = String.format("Unable to upgrade virtual machine %s, target offering use shared storage but the storage pool where " - + "the volume is allocated is a local storage.", vmInstance.toString()); - throw new InvalidParameterValueException(message); + throw Exceptions.invalidParameterValueException("vm.upgrade.storage.shared.to.local", Map.of("instance", vmInstance)); } } @@ -4623,7 +4595,7 @@ private void checkIfNetworkExistsForUserVM(VirtualMachine virtualMachine, Networ List allNics = _nicsDao.listByVmId(virtualMachine.getId()); for (NicVO nic : allNics) { if (nic.getNetworkId() == network.getId()) { - throw new CloudRuntimeException("A NIC already exists for VM:" + virtualMachine.getInstanceName() + " in network: " + network.getUuid()); + throw Exceptions.cloudRuntimeException("vm.addnic.already.exists", Map.of("instance", virtualMachine, "network", network)); } } } @@ -4803,7 +4775,7 @@ private boolean orchestrateRemoveVmFromNetwork(final VirtualMachine vm, final Ne if (nic.isDefaultNic() && vm.getType() == VirtualMachine.Type.User) { logger.warn("Failed to remove NIC from {} in {}, NIC is default.", vm, network); - throw new CloudRuntimeException("Failed to remove NIC from " + vm + " in " + network + ", NIC is default."); + throw Exceptions.cloudRuntimeException("vm.removenic.default.nic", Map.of("nic", nic, "instance", vm, "network", network)); } final Nic lock = _nicsDao.acquireInLockTable(nic.getId()); @@ -4854,7 +4826,7 @@ public void findHostAndMigrate(final String vmUuid, final Long newSvcOfferingId, final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); if (vm == null) { - throw new CloudRuntimeException("Unable to find " + vmUuid); + throw Exceptions.cloudRuntimeException("vm.scale.vm.not.found"); } ServiceOfferingVO newServiceOffering = _offeringDao.findById(newSvcOfferingId); if (newServiceOffering.isDynamic()) { @@ -4866,7 +4838,7 @@ public void findHostAndMigrate(final String vmUuid, final Long newSvcOfferingId, final Long srcHostId = vm.getHostId(); final Long oldSvcOfferingId = vm.getServiceOfferingId(); if (srcHostId == null) { - throw new CloudRuntimeException("Unable to scale the vm because it doesn't have a host id"); + throw Exceptions.cloudRuntimeException("vm.scale.no.host", Map.of("instance", vm)); } final Host host = _hostDao.findById(srcHostId); final DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, null, null); @@ -4880,7 +4852,7 @@ public void findHostAndMigrate(final String vmUuid, final Long newSvcOfferingId, } catch (final AffinityConflictException e2) { String message = String.format("Unable to create deployment, affinity rules associated to the %s conflict.", vm.toString()); logger.warn(message, e2); - throw new CloudRuntimeException(message); + throw Exceptions.cloudRuntimeException("vm.scale.affinity.conflict", Map.of("instance", vm)); } if (dest != null) { @@ -4939,7 +4911,7 @@ private void orchestrateMigrateForScale(final String vmUuid, final long srcHostI if (fromHost == null) { String logMessageUnableToFindHost = String.format("Unable to find host to migrate from %s.", srcHostId); logger.info(logMessageUnableToFindHost); - throw new CloudRuntimeException(logMessageUnableToFindHost); + throw Exceptions.cloudRuntimeException("vm.scale.source.host.not.found"); } Host dstHost = _hostDao.findById(dstHostId); @@ -4948,7 +4920,7 @@ private void orchestrateMigrateForScale(final String vmUuid, final long srcHostI if (fromHostClusterId != destHostClusterId) { String logMessageHostsOnDifferentCluster = String.format("Source and destination host are not in same cluster, unable to migrate to %s", fromHost); logger.info(logMessageHostsOnDifferentCluster); - throw new CloudRuntimeException(logMessageHostsOnDifferentCluster); + throw Exceptions.cloudRuntimeException("vm.scale.cross.cluster.not.supported"); } final VirtualMachineGuru vmGuru = getVmGuru(vm); @@ -4957,13 +4929,13 @@ private void orchestrateMigrateForScale(final String vmUuid, final long srcHostI if (vm == null) { String message = String.format("Unable to find VM {\"uuid\": \"%s\"}.", vmUuid); logger.warn(message); - throw new CloudRuntimeException(message); + throw Exceptions.cloudRuntimeException("vm.scale.vm.not.found.retry"); } if (vm.getState() != State.Running) { String message = String.format("%s is not in \"Running\" state, unable to migrate it. Current state [%s].", vm.toString(), vm.getState()); logger.warn(message); - throw new CloudRuntimeException(message); + throw Exceptions.cloudRuntimeException("vm.scale.vm.not.running", Map.of("instance", vm)); } AlertManager.AlertType alertType = AlertManager.AlertType.ALERT_TYPE_USERVM_MIGRATE; @@ -5030,9 +5002,7 @@ private void orchestrateMigrateForScale(final String vmUuid, final long srcHostI try { final Answer ma = _agentMgr.send(vm.getLastHostId(), mc); if (ma == null || !ma.getResult()) { - String msg = String.format("Unable to migrate %s due to [%s].", vm.toString(), ma != null ? ma.getDetails() : "null answer returned"); - logger.error(msg); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.scale.migrate.failed", Map.of("instance", vm, "error", (ma != null ? ma.getDetails() : "null answer returned"))); } } catch (final OperationTimedoutException e) { if (e.isActive()) { @@ -5062,7 +5032,7 @@ private void orchestrateMigrateForScale(final String vmUuid, final long srcHostI logger.error("Unable to cleanup source host [{}] due to [{}].", fromHost, e.getMessage(), e); } cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.AgentReportStopped, true); - throw new CloudRuntimeException("Unable to complete migration for " + vm); + throw Exceptions.cloudRuntimeException("vm.scale.migrate.verification.failed", Map.of("instance", vm)); } } catch (final OperationTimedoutException e) { logger.debug("Error while checking the {} on {}", vm, dstHost, e); @@ -5294,7 +5264,7 @@ private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering old if (reconfigureAnswer == null || !reconfigureAnswer.getResult()) { logger.error("Unable to scale vm due to {}", (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails())); - throw new CloudRuntimeException("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails())); + throw Exceptions.cloudRuntimeException("vm.scale.reconfigure.answer.failed", Map.of("error", (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()))); } if (reconfiguringOnExistingHost) { @@ -5904,7 +5874,7 @@ private void checkConcurrentJobsPerDatastoreThreshhold(final StoragePool destPoo if (threshold != null && threshold > 0) { long count = _jobMgr.countPendingJobs("\"storageid\":\"" + destPool.getUuid() + "\"", MigrateVMCmd.class.getName(), MigrateVolumeCmd.class.getName(), MigrateVolumeCmdByAdmin.class.getName()); if (count > threshold) { - throw new CloudRuntimeException("Number of concurrent migration jobs per datastore exceeded the threshold: " + threshold.toString() + ". Please try again after some time."); + throw Exceptions.cloudRuntimeException("vm.migrate.concurrent.jobs.exceeded"); } } } @@ -5953,7 +5923,7 @@ public Outcome addVmToNetworkThroughJobQueue( VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { if (pendingWorkJobs.size() > 1) { - throw new CloudRuntimeException(String.format("The number of jobs to add network %s to vm %s are %d", network.getUuid(), vm.getInstanceName(), pendingWorkJobs.size())); + throw Exceptions.cloudRuntimeException("vm.addnic.pending.jobs.exceeded", Map.of("instance", vm, "network", network)); } workJob = pendingWorkJobs.get(0); } else { @@ -6079,8 +6049,7 @@ private Pair orchestrateStart(final VmWorkStart work) th orchestrateStart(vm.getUuid(), work.getParams(), work.getPlan(), _dpMgr.getDeploymentPlannerByName(work.getDeploymentPlanner())); } catch (CloudRuntimeException e){ logger.error("Unable to orchestrate start {} due to [{}].", vm, e.getMessage()); - CloudRuntimeException ex = new CloudRuntimeException(String.format("Unable to orchestrate the start of VM instance %s.", - ReflectionToStringBuilderUtils.reflectOnlySelectedFields(vm, "instanceName", "uuid"))); + CloudRuntimeException ex = Exceptions.cloudRuntimeException("vm.start.orchestrate.failed", Map.of("instance", vm)); return new Pair<>(JobInfo.Status.FAILED, JobSerializerHelper.toObjectSerializedString(ex)); } return new Pair<>(JobInfo.Status.SUCCEEDED, null); @@ -6092,7 +6061,7 @@ private Pair orchestrateStop(final VmWorkStop work) thro if (vm == null) { String message = String.format("Unable to find VM [%s].", work.getVmId()); logger.warn(message); - throw new CloudRuntimeException(message); + throw Exceptions.cloudRuntimeException("vm.stop.work.vm.not.found"); } orchestrateStop(vm.getUuid(), work.isCleanup()); @@ -6370,11 +6339,11 @@ private Pair orchestrateUpdateDefaultNic(final VmWorkUpd VMInstanceVO vm = findVmById(work.getVmId()); final NicVO nic = _entityMgr.findById(NicVO.class, work.getNicId()); if (nic == null) { - throw new CloudRuntimeException("Unable to find nic " + work.getNicId()); + throw Exceptions.cloudRuntimeException("vm.updatenic.nic.not.found"); } final NicVO defaultNic = _entityMgr.findById(NicVO.class, work.getDefaultNicId()); if (defaultNic == null) { - throw new CloudRuntimeException("Unable to find default nic " + work.getDefaultNicId()); + throw Exceptions.cloudRuntimeException("vm.updatenic.default.nic.not.found"); } final boolean result = orchestrateUpdateDefaultNicForVM(vm, nic, defaultNic); return new Pair<>(JobInfo.Status.SUCCEEDED, @@ -6393,9 +6362,9 @@ public boolean updateVmNic(VirtualMachine vm, Nic nic, Boolean enabled) { return BooleanUtils.isTrue((Boolean) jobResult); } } catch (ResourceUnavailableException | InsufficientCapacityException ex) { - throw new CloudRuntimeException(String.format("Exception while updating VM [%s] NIC. Check the logs for more information.", vm.getUuid())); + throw Exceptions.cloudRuntimeException("vm.updatenic.job.exception", Map.of("instance", vm)); } - throw new CloudRuntimeException("Unexpected job execution result."); + throw Exceptions.cloudRuntimeException("vm.updatenic.job.execution.failed"); } private boolean orchestrateUpdateVmNic(final VirtualMachine vm, final Nic nic, final Boolean enabled) throws ResourceUnavailableException { @@ -6449,7 +6418,7 @@ private Pair orchestrateUpdateVmNic(final VmWorkUpdateNi VMInstanceVO vm = findVmById(work.getVmId()); final NicVO nic = _entityMgr.findById(NicVO.class, work.getNicId()); if (nic == null) { - throw new CloudRuntimeException(String.format("Unable to find NIC with ID %s.", work.getNicId())); + throw Exceptions.cloudRuntimeException("vm.updatenic.nic.not.found", Map.of("nicId", work.getNicId())); } final boolean result = orchestrateUpdateVmNic(vm, nic, work.isEnabled()); return new Pair<>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(result)); @@ -6517,7 +6486,8 @@ private void executePreMigrationCommand(VMInstanceVO vm, VirtualMachineTO to, lo return; } final String vmInstanceName = vm.getInstanceName(); - final String srcHostUuid = _hostDao.findById(srcHostId).getUuid(); + final HostVO srcHost = _hostDao.findById(srcHostId); + final String srcHostUuid = srcHost.getUuid(); logger.info("Sending PreMigrationCommand to source host {} for VM {} with CLVM volumes", srcHostUuid, vmInstanceName); final PreMigrationCommand preMigCmd = new PreMigrationCommand(to, vmInstanceName); Answer preMigAnswer = null; @@ -6525,14 +6495,15 @@ private void executePreMigrationCommand(VMInstanceVO vm, VirtualMachineTO to, lo preMigAnswer = _agentMgr.send(srcHostId, preMigCmd); if (preMigAnswer == null || !preMigAnswer.getResult()) { final String details = preMigAnswer != null ? preMigAnswer.getDetails() : "null answer returned"; - final String msg = "Failed to prepare source host for migration: " + details; logger.error("Failed to prepare source host {} for migration of VM {}: {}", srcHostUuid, vmInstanceName, details); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.migrate.host.source.prepare.failed", + Map.of("instance", vm, "host", srcHost, "error", details)); } logger.info("Successfully prepared source host {} for migration of VM {}", srcHostUuid, vmInstanceName); } catch (final AgentUnavailableException | OperationTimedoutException e) { logger.error("Failed to send PreMigrationCommand to source host {}: {}", srcHostUuid, e.getMessage(), e); - throw new CloudRuntimeException("Failed to prepare source host for migration: " + e.getMessage(), e); + throw Exceptions.cloudRuntimeException("vm.migrate.host.source.prepare.failed", + Map.of("instance", vm, "host", srcHost, "error", e.getMessage()), e); } } diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java index c656e1a282af..f07c53e5944b 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -971,7 +971,6 @@ public void checkIfNewOfferingStorageScopeMatchesStoragePoolTestSharedLocal() { private void prepareAndRunCheckIfNewOfferingStorageScopeMatchesStoragePool(boolean isRootOnLocal, boolean isOfferingUsingLocal) { Mockito.doReturn(isRootOnLocal).when(virtualMachineManagerImpl).isRootVolumeOnLocalStorage(anyLong()); - Mockito.doReturn("vmInstanceMockedToString").when(vmInstanceMock).toString(); Mockito.doReturn(isOfferingUsingLocal).when(diskOfferingMock).isUseLocalStorage(); virtualMachineManagerImpl.checkIfNewOfferingStorageScopeMatchesStoragePool(vmInstanceMock, diskOfferingMock); } @@ -1824,7 +1823,7 @@ public void testPersistDomainForKvmForStoppedVmNoHost() { vmTO.setName(vmName); when(virtualMachineManagerImpl.findClusterAndHostIdForVm(vmInstanceMock, false)).thenReturn(new Pair<>(clusterMockId, null)); CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null)); - assertEquals("No available host to persist domain XML for Instance: " + vmName, exception.getMessage()); + assertEquals("vm.unmanage.kvm.no.host", exception.getMessageKey()); } @Test @@ -1834,7 +1833,7 @@ public void testPersistDomainForKvmForRunningVmAgentFailure() throws AgentUnavai UnmanageInstanceAnswer failureAnswer = new UnmanageInstanceAnswer(null, false, "failure"); when(agentManagerMock.send(anyLong(), any(UnmanageInstanceCommand.class))).thenReturn(failureAnswer); CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null)); - assertEquals("Failed to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage()); + assertEquals("vm.unmanage.kvm.persist.failed", exception.getMessageKey()); } @Test @@ -1843,7 +1842,7 @@ public void testPersistDomainForKvmAgentUnavailable() throws AgentUnavailableExc when(vmInstanceMock.getHostId()).thenReturn(hostMockId); doThrow(new AgentUnavailableException("Agent down", hostMockId)).when(agentManagerMock).send(anyLong(), any(UnmanageInstanceCommand.class)); CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null)); - assertEquals("Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage()); + assertEquals("vm.unmanage.kvm.command.failed", exception.getMessageKey()); } @Test(expected = ConcurrentOperationException.class) @@ -1860,7 +1859,7 @@ public void testPersistDomainForKvmOperationTimedOut() throws AgentUnavailableEx when(vmInstanceMock.getHostId()).thenReturn(hostMockId); doThrow(new OperationTimedoutException(null, hostMockId, 123L, 60, false)).when(agentManagerMock).send(anyLong(), any(UnmanageInstanceCommand.class)); CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null)); - assertEquals("Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage()); + assertEquals("vm.unmanage.kvm.command.failed", exception.getMessageKey()); } @Test(expected = CloudRuntimeException.class) diff --git a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java index cfe8049f5b2c..261efbb1d301 100644 --- a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java @@ -438,7 +438,7 @@ public String getUuid() { @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() { diff --git a/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java b/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java index 7345eeb48539..32031a85c52d 100644 --- a/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java +++ b/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java @@ -346,7 +346,7 @@ public void setDetails(Map details) { @Override public String toString() { - return String.format("UserAccount %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields + return String.format("UserAccount %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields (this, "id", "uuid", "username", "accountName")); } } diff --git a/engine/schema/src/main/java/com/cloud/user/UserVO.java b/engine/schema/src/main/java/com/cloud/user/UserVO.java index 1b89bc215cf7..59ba37318ca3 100644 --- a/engine/schema/src/main/java/com/cloud/user/UserVO.java +++ b/engine/schema/src/main/java/com/cloud/user/UserVO.java @@ -31,10 +31,10 @@ 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.GenericDao; -import org.apache.commons.lang3.StringUtils; /** * A bean representing a user @@ -266,7 +266,7 @@ public void setRegistered(boolean registered) { @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 diff --git a/packaging/el8/cloud.spec b/packaging/el8/cloud.spec index 3ba2e4d5789e..36989160efa3 100644 --- a/packaging/el8/cloud.spec +++ b/packaging/el8/cloud.spec @@ -293,7 +293,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 @@ -622,6 +622,7 @@ pip3 install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz %config(noreplace) %{_sysconfdir}/%{name}/management/log4j2.xml %config(noreplace) %{_sysconfdir}/%{name}/management/environment.properties %config(noreplace) %{_sysconfdir}/%{name}/management/java.security.ciphers +%config(noreplace) %{_sysconfdir}/%{name}/management/error-messages.json %config(noreplace) %attr(0644,root,root) %{_sysconfdir}/logrotate.d/%{name}-management %attr(0644,root,root) %{_unitdir}/%{name}-management.service %attr(0755,cloud,cloud) %{_localstatedir}/run/%{name}-management.pid diff --git a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java index a0b1973fd8e8..1990c9a5e6dd 100644 --- a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java +++ b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java @@ -239,6 +239,12 @@ public boolean isRootAdmin(Long accountId) { 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 diff --git a/pom.xml b/pom.xml index cc14a86fb8be..fd50da6db959 100644 --- a/pom.xml +++ b/pom.xml @@ -1064,6 +1064,7 @@ CHANGES.md ISSUE_TEMPLATE.md PULL_REQUEST_TEMPLATE.md + client/conf/error-messages.json.in build/build.number debian/cloudstack-agent.dirs debian/cloudstack-usage.dirs diff --git a/server/src/main/java/com/cloud/api/ApiAsyncJobDispatcher.java b/server/src/main/java/com/cloud/api/ApiAsyncJobDispatcher.java index e70a6b4da639..8b2b86c7c5d8 100644 --- a/server/src/main/java/com/cloud/api/ApiAsyncJobDispatcher.java +++ b/server/src/main/java/com/cloud/api/ApiAsyncJobDispatcher.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ExceptionResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.context.ResponseMessageResolver; 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.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 void runJob(final AsyncJob job) { ExceptionResponse response = new ExceptionResponse(); response.setErrorCode(errorCode); response.setErrorText(errorMsg); + if (e instanceof CloudRuntimeException) { + ResponseMessageResolver.updateExceptionResponse(response, (CloudRuntimeException) e); + } response.setResponseName((cmdObj == null) ? "unknowncommandresponse" : cmdObj.getCommandName()); _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.FAILED, errorCode, ApiSerializerHelper.toSerializedString(response)); diff --git a/server/src/main/java/com/cloud/api/ApiServer.java b/server/src/main/java/com/cloud/api/ApiServer.java index 1aaf593a8da0..64dc677a79eb 100644 --- a/server/src/main/java/com/cloud/api/ApiServer.java +++ b/server/src/main/java/com/cloud/api/ApiServer.java @@ -62,9 +62,6 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.instrumentation.annotations.WithSpan; - import org.apache.cloudstack.acl.APIChecker; import org.apache.cloudstack.acl.ApiKeyPairManagerImpl; import org.apache.cloudstack.acl.apikeypair.ApiKeyPair; @@ -107,6 +104,7 @@ import org.apache.cloudstack.api.response.LoginCmdResponse; import org.apache.cloudstack.config.ApiServiceConfiguration; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.context.ResponseMessageResolver; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.events.EventDistributor; @@ -206,6 +204,9 @@ import com.cloud.utils.net.NetUtils; import com.google.gson.reflect.TypeToken; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.instrumentation.annotations.WithSpan; + @Component public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiServerService, Configurable { private static final Logger ACCESSLOGGER = LogManager.getLogger("apiserver." + ApiServer.class.getName()); @@ -1714,6 +1715,7 @@ public String getSerializedApiError(final ServerApiException ex, final Map idList = ex.getIdProxyList(); if (idList != null) { for (ExceptionProxyObject exceptionProxyObject : idList) { diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 27facb304eba..72f0bf088eb8 100644 --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -558,6 +558,8 @@ protected void checkDomainResourceLimit(Long domainId, final ResourceType type, if (domainResourceLimit != Resource.RESOURCE_UNLIMITED && requestedDomainResourceCount > domainResourceLimit) { String message = "Maximum" + messageSuffix; + addResourceLimitErrorContext(domain, null, null, type, tag, convDomainResourceLimit, + convCurrentDomainResourceCount, convCurrentResourceReservation, convNumResources); ResourceAllocationException e = new ResourceAllocationException(message, type); logger.error(message, e); throw e; @@ -597,12 +599,39 @@ protected void checkAccountResourceLimit(final Account account, final Project pr if (accountResourceLimit != Resource.RESOURCE_UNLIMITED && requestedResourceCount > accountResourceLimit) { String message = "Maximum" + messageSuffix; + addResourceLimitErrorContext(null, account, project, type, tag, convertedAccountResourceLimit, + convertedCurrentResourceCount, convertedCurrentResourceReservation, convertedNumResources); ResourceAllocationException e = new ResourceAllocationException(message, type); logger.error(message, e); throw e; } } + private void addResourceLimitErrorContext(Domain domain, Account account, Project project, ResourceType type, + String tag, String convertedResourceLimit, + String convertedCurrentResourceCount, + String convertedCurrentResourceReservation, + String convertedNumResources) { + Map details = new HashMap<>(); + details.put("resourceTypeDisplay", StringUtils.isBlank(tag) ? + type.getDisplayName() : + type.getDisplayName() + " (tag: " + tag + ")"); + if (domain != null) { + details.put("resourceLimitCause", ResourceOwnerType.Domain); + details.put("resourceOwnerDomain", domain); + } + if (account != null) { + details.put("resourceLimitCause", ResourceOwnerType.Account); + details.put("resourceOwner", ObjectUtils.firstNonNull(project, account)); + details.put("resourceOwnerType", project == null ? "Account" : "Project"); + } + details.put("resourceLimit", convertedResourceLimit); + details.put("resourceAmount", convertedCurrentResourceCount); + details.put("resourceReserved", convertedCurrentResourceReservation); + details.put("resourceRequested", convertedNumResources); + CallContext.current().putErrorContextParameters(details); + } + protected List lockAccountAndOwnerDomainRows(long accountId, final ResourceType type, String tag) { Set rowIdsToLock = _resourceCountDao.listAllRowsToUpdate(accountId, ResourceOwnerType.Account, type, tag); SearchCriteria sc = ResourceCountSearch.create(); diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index 6c8d9c842fb9..ffbe04614322 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -643,21 +643,26 @@ public boolean isAdmin(Long accountId) { @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; diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 1a65bbaca8d1..ac9b45ca4261 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -108,7 +108,6 @@ import org.apache.cloudstack.backup.BackupVO; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupScheduleDao; -import org.apache.cloudstack.schedule.ResourceScheduleManager; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.kms.KMSManager; import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity; @@ -129,6 +128,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult; +import org.apache.cloudstack.error.Exceptions; import org.apache.cloudstack.extension.ExtensionHelper; import org.apache.cloudstack.framework.async.AsyncCallFuture; import org.apache.cloudstack.framework.config.ConfigKey; @@ -141,6 +141,7 @@ import org.apache.cloudstack.query.QueryService; import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.resourcelimit.Reserver; +import org.apache.cloudstack.schedule.ResourceScheduleManager; import org.apache.cloudstack.snapshot.SnapshotHelper; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.command.DettachCommand; @@ -158,8 +159,6 @@ import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -268,12 +267,12 @@ import com.cloud.kubernetes.cluster.KubernetesServiceHelper; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; -import com.cloud.network.NetworkService; import com.cloud.network.Network.GuestType; import com.cloud.network.Network.IpAddresses; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkService; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetwork; import com.cloud.network.as.AutoScaleManager; @@ -315,8 +314,8 @@ import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceState; import com.cloud.resourcelimit.CheckedReservation; -import com.cloud.serializer.GsonHelper; import com.cloud.resourcelimit.ReservationHelper; +import com.cloud.serializer.GsonHelper; import com.cloud.server.ManagementService; import com.cloud.server.ResourceTag; import com.cloud.service.ServiceOfferingVO; @@ -872,24 +871,38 @@ public UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password) throws Re // Do parameters input validation if (userVm == null) { - throw new InvalidParameterValueException("unable to find an Instance with id " + cmd.getId()); + throw Exceptions.invalidParameterValueException("vm.resetpassword.vm.not.found"); } _vmDao.loadDetails(userVm); VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId()); - if (template == null || !template.isEnablePassword()) { - throw new InvalidParameterValueException("Fail to reset password for the Instance, the Template is not password enabled"); + if (template == null) { + throw Exceptions.invalidParameterValueException("vm.resetpassword.template.not.found", Map.of( + "instance", userVm + )); + } + if (!template.isEnablePassword()) { + throw Exceptions.invalidParameterValueException("vm.resetpassword.template.not.passwordenabled", Map.of( + "instance", userVm, + "template", template + )); } - if (userVm.getState() == State.Error || userVm.getState() == State.Expunging) { + if (State.Error.equals(userVm.getState()) || State.Expunging.equals(userVm.getState())) { logger.error("vm is not in the right state: {}", userVm); - throw new InvalidParameterValueException(String.format("Vm %s is not in the right state", userVm)); + throw Exceptions.invalidParameterValueException("vm.resetpassword.vm.not.right.state", Map.of( + "instance", userVm, + "instanceState", userVm.getState() + )); } - if (userVm.getState() != State.Stopped) { + if (!State.Stopped.equals(userVm.getState())) { logger.error("vm is not in the right state: {}", userVm); - throw new InvalidParameterValueException(String.format("Vm %s should be stopped to do password reset", userVm)); + throw Exceptions.invalidParameterValueException("vm.resetpassword.vm.not.stopped", Map.of( + "instance", userVm, + "instanceState", userVm.getState() + )); } _accountMgr.checkAccess(caller, null, true, userVm); @@ -899,7 +912,8 @@ public UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password) throws Re if (result) { userVm.setPassword(password); } else { - throw new CloudRuntimeException("Failed to reset password for the Instance "); + throw Exceptions.cloudRuntimeException( + "vm.resetpassword.failed", Map.of("instance", userVm)); } return userVm; @@ -929,7 +943,12 @@ private boolean resetVMPasswordInternal(Long vmId, String password) throws Resou UserDataServiceProvider element = _networkMgr.getPasswordResetProvider(defaultNetwork); if (element == null) { - throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for password reset"); + throw Exceptions.cloudRuntimeException( + "vm.resetpassword.network.service.not.found", Map.of( + "instance", vmInstance, + "network", defaultNetwork, + "networkService", Service.UserData.getName()) + ); } boolean result = element.savePassword(defaultNetwork, defaultNicProfile, vmProfile); @@ -977,16 +996,19 @@ public UserVm resetVMUserData(ResetVMUserDataCmd cmd) throws ResourceUnavailable UserVmVO userVm = _vmDao.findById(cmd.getId()); if (userVm == null) { - throw new InvalidParameterValueException("unable to find an Instance by id" + cmd.getId()); + throw Exceptions.invalidParameterValueException("vm.resetuserdata.vm.not.found"); } if (UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) { - throw new InvalidParameterValueException("Operation not supported on Shared FileSystem Instance"); + throw Exceptions.invalidParameterValueException("vm.resetuserdata.vm.sharedfs.operation.not.supported", Map.of( + "instance", userVm + )); } if (Hypervisor.HypervisorType.External.equals(userVm.getHypervisorType())) { logger.error("Reset VM userdata not supported for {} as it is {} hypervisor instance", userVm, Hypervisor.HypervisorType.External.name()); - throw new InvalidParameterValueException(String.format("Operation not supported for instance: %s", - userVm.getName())); + throw Exceptions.invalidParameterValueException("vm.resetuserdata.vm.operation.not.supported", Map.of( + "instance", userVm + )); } _accountMgr.checkAccess(caller, null, true, userVm); @@ -996,7 +1018,10 @@ public UserVm resetVMUserData(ResetVMUserDataCmd cmd) throws ResourceUnavailable if (userVm.getState() != State.Stopped) { logger.error("vm ({}) should be stopped to do UserData reset. current state: {}", userVm, userVm.getState()); - throw new InvalidParameterValueException(String.format("VM %s should be stopped to do UserData reset", userVm)); + throw Exceptions.invalidParameterValueException("vm.resetuserdata.vm.no.stopped", Map.of( + "instance", userVm, + "instanceState", userVm.getState() + )); } String userData = cmd.getUserData(); @@ -1028,31 +1053,40 @@ public UserVm resetVMSSHKey(ResetVMSSHKeyCmd cmd) throws ResourceUnavailableExce UserVmVO userVm = _vmDao.findById(cmd.getId()); if (userVm == null) { - throw new InvalidParameterValueException("unable to find an Instance by id" + cmd.getId()); + throw Exceptions.invalidParameterValueException("vm.resetsshkey.vm.not.found"); } if (UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) { - throw new InvalidParameterValueException("Operation not supported on Shared FileSystem Instance"); + throw Exceptions.invalidParameterValueException("vm.resetsshkey.vm.sharedfs.operation.not.supported", Map.of( + "instance", userVm + )); } if (Hypervisor.HypervisorType.External.equals(userVm.getHypervisorType())) { logger.error("Reset VM SSH key not supported for {} as it is {} hypervisor instance", userVm, Hypervisor.HypervisorType.External.name()); - throw new InvalidParameterValueException(String.format("Operation not supported for instance: %s", - userVm.getName())); + throw Exceptions.invalidParameterValueException("vm.resetsshkey.vm.operation.not.supported", Map.of( + "instance", userVm + )); } // Do parameters input validation if (userVm.getState() == State.Error || userVm.getState() == State.Expunging) { logger.error("vm ({}) is not in the right state: {}", userVm, userVm.getState()); - throw new InvalidParameterValueException("Vm with specified id is not in the right state"); + throw Exceptions.invalidParameterValueException("vm.resetsshkey.vm.not.right.state", Map.of( + "instance", userVm, + "instanceState", userVm.getState() + )); } if (userVm.getState() != State.Stopped) { logger.error(String.format("vm (%s) is not in the stopped state. current state: %s", userVm, userVm.getState())); - throw new InvalidParameterValueException("Vm " + userVm + " should be stopped to do SSH Key reset"); + throw Exceptions.invalidParameterValueException("vm.resetsshkey.vm.no.stopped", Map.of( + "instance", userVm, + "instanceState", userVm.getState() + )); } List names = cmd.getNames(); if (CollectionUtils.isEmpty(names)) { - throw new InvalidParameterValueException("'keypair' or 'keypairs' must be specified"); + throw Exceptions.invalidParameterValueException("vm.resetsshkey.key.not.specified"); } userVm = resetVMSSHKeyInternal(userVm, owner, names); @@ -1068,7 +1102,7 @@ private UserVmVO resetVMSSHKeyInternal(UserVmVO userVm, Account owner, List p.getPublicKey()).collect(Collectors.joining("\n")); keypairnames = String.join(",", names); @@ -1080,7 +1114,8 @@ private UserVmVO resetVMSSHKeyInternal(UserVmVO userVm, Account owner, List params = null; + Map params = null; if (enterSetup) { params = new HashMap(); params.put(VirtualMachineProfile.Param.BootIntoSetup, Boolean.TRUE); @@ -1237,14 +1298,16 @@ private UserVm rebootVirtualMachine(long userId, long vmId, boolean enterSetup, private UserVm forceRebootVirtualMachine(UserVmVO vm, long hostId, boolean enterSetup) { try { if (stopVirtualMachine(vm.getId(), false) != null) { - Map params = new HashMap<>(); + Map params = new HashMap<>(); if (enterSetup) { params.put(VirtualMachineProfile.Param.BootIntoSetup, Boolean.TRUE); } return startVirtualMachine(vm.getId(), null, null, hostId, params, null, false).first(); } } catch (CloudException e) { - throw new CloudRuntimeException(String.format("Unable to reboot the VM: %s", vm), e); + throw Exceptions.cloudRuntimeException("vm.forcereboot.failed", Map.of( + "instance", vm + ), e); } return null; } @@ -1264,10 +1327,11 @@ public UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) throws ResourceAllocationE //UserVmVO vmInstance = _vmDao.findById(vmId); VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId); if (vmInstance == null) { - throw new InvalidParameterValueException("unable to find an Instance with id " + vmId); + throw Exceptions.invalidParameterValueException("unable to find an Instance with id " + vmId); } else if (!(vmInstance.getState().equals(State.Stopped))) { - throw new InvalidParameterValueException("Unable to upgrade Instance " + vmInstance + " " + " in state " + vmInstance.getState() - + "; make sure the Instance is stopped"); + throw Exceptions.invalidParameterValueException("vm.upgrade.vm.not.right.state", + Map.of("instance", vmInstance, + "instanceState", vmInstance.getState())); } _accountMgr.checkAccess(caller, null, true, vmInstance); @@ -1316,56 +1380,70 @@ protected void addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecifie private void validateOfferingMaxResource(ServiceOfferingVO offering) { - Integer maxCPUCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value(); + Integer maxCPUCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE : ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value(); if (offering.getCpu() > maxCPUCores) { - throw new InvalidParameterValueException("Invalid cpu cores value, please choose another service offering with cpu cores between 1 and " + maxCPUCores); + throw Exceptions.invalidParameterValueException("vm.validate.serviceoffering.invalid.cpu.cores", + Map.of("serviceOffering", offering, "maxCPUCores", maxCPUCores)); } - Integer maxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value(); + Integer maxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE : ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value(); if (offering.getRamSize() > maxRAMSize) { - throw new InvalidParameterValueException("Invalid memory value, please choose another service offering with memory between 32 and " + maxRAMSize + " MB"); + throw Exceptions.invalidParameterValueException("vm.validate.serviceoffering.invalid.memory", + Map.of("serviceOffering", offering, "maxMemory", maxRAMSize)); } } @Override public void validateCustomParameters(ServiceOfferingVO serviceOffering, Map customParameters) { if (MapUtils.isEmpty(customParameters) && serviceOffering.isDynamic()) { - throw new InvalidParameterValueException("Need to specify custom parameter values cpu, cpu speed and memory when using custom offering"); + throw Exceptions.invalidParameterValueException("vm.validate.serviceoffering.custom.params.missing", + Map.of("serviceOffering", serviceOffering)); } Map offeringDetails = serviceOfferingDetailsDao.listDetailsKeyPairs(serviceOffering.getId()); if (serviceOffering.getCpu() == null) { int minCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_CPU_NUMBER), 1); int maxCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_CPU_NUMBER), Integer.MAX_VALUE); int cpuNumber = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name()), -1); - int maxCPUCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value(); + int maxCPUCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE : ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value(); if (cpuNumber < minCPU || cpuNumber > maxCPU || cpuNumber > maxCPUCores) { - throw new InvalidParameterValueException(String.format("Invalid CPU cores value, specify a value between %d and %d", minCPU, Math.min(maxCPUCores, maxCPU))); + throw Exceptions.invalidParameterValueException("vm.validate.serviceoffering.invalid.params.cpu.cores", + Map.of("serviceOffering", serviceOffering, + "minCPUCores", minCPU, + "maxCPUCores", Math.min(maxCPUCores, maxCPU))); } } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuNumber.name())) { - throw new InvalidParameterValueException("The CPU cores of this offering id:" + serviceOffering.getUuid() - + " is not customizable. This is predefined in the Template."); + throw Exceptions.invalidParameterValueException("vm.validate.serviceoffering.cpu.cores.not.dynamic", + Map.of("serviceOffering", serviceOffering, + "cpuCores", serviceOffering.getCpu())); } if (serviceOffering.getSpeed() == null) { String cpuSpeed = customParameters.get(UsageEventVO.DynamicParameters.cpuSpeed.name()); if ((cpuSpeed == null) || (NumbersUtil.parseInt(cpuSpeed, -1) <= 0)) { - throw new InvalidParameterValueException("Invalid CPU speed value, specify a value between 1 and " + Integer.MAX_VALUE); + throw Exceptions.invalidParameterValueException("vm.validate.serviceoffering.invalid.params.cpu.speed", + Map.of("serviceOffering", serviceOffering, + "maxCPUSpeed", Integer.MAX_VALUE)); } } else if (!serviceOffering.isCustomCpuSpeedSupported() && customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) { - throw new InvalidParameterValueException(String.format("The CPU speed of this offering id:%s" - + " is not customizable. This is predefined as %d MHz.", - serviceOffering.getUuid(), serviceOffering.getSpeed())); + throw Exceptions.invalidParameterValueException("vm.validate.serviceoffering.cpu.speed.not.dynamic", + Map.of("serviceOffering", serviceOffering, + "cpuSpeed", serviceOffering.getSpeed())); } if (serviceOffering.getRamSize() == null) { int minMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_MEMORY), 32); int maxMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_MEMORY), Integer.MAX_VALUE); int memory = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.memory.name()), -1); - int maxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value(); + int maxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE : ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value(); if (memory < minMemory || memory > maxMemory || memory > maxRAMSize) { - throw new InvalidParameterValueException(String.format("Invalid memory value, specify a value between %d and %d", minMemory, Math.min(maxRAMSize, maxMemory))); + throw Exceptions.invalidParameterValueException("vm.validate.serviceoffering.invalid.params.memory", + Map.of("serviceOffering", serviceOffering, + "minMemory", minMemory, + "maxMemory", Math.min(maxRAMSize, maxMemory))); } } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.memory.name())) { - throw new InvalidParameterValueException("The memory of this offering id:" + serviceOffering.getUuid() + " is not customizable. This is predefined in the Template."); + throw Exceptions.invalidParameterValueException("vm.validate.serviceoffering.memory.not.dynamic", + Map.of("serviceOffering", serviceOffering, + "memory", serviceOffering.getRamSize())); } } @@ -1375,7 +1453,9 @@ private UserVm upgradeStoppedVirtualMachine(Long vmId, Long svcOffId, Map 0l && newNewOfferingRootSizeInBytes < currentRootDiskOffering.getDiskSize()) { - throw new InvalidParameterValueException(String.format( - "Failed to resize Root volume. The new Service Offering [%s] has a smaller disk size [%d GB] than the current disk [%d GB].", - newRootDiskOffering, newNewOfferingRootSizeInGiB, currentRootDiskOfferingGiB)); + throw Exceptions.invalidParameterValueException("vm.resize.volume.new.offering.smaller", Map.of("newOffering", newRootDiskOffering, "newSize", newNewOfferingRootSizeInGiB, "currentSize", currentRootDiskOfferingGiB)); } return resizeVolumeCmd; } @@ -1473,24 +1551,27 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV UserVmVO vmInstance = _vmDao.findById(vmId); if (vmInstance == null) { - throw new InvalidParameterValueException("Unable to find an Instance with ID " + vmId); + throw Exceptions.invalidParameterValueException("vm.addnic.vm.not.found"); } // Check that Vm does not have VM Snapshots - if (_vmSnapshotDao.findByVm(vmId).size() > 0) { - throw new InvalidParameterValueException("NIC cannot be added to Instance with Instance Snapshots"); + if (!_vmSnapshotDao.findByVm(vmId).isEmpty()) { + throw Exceptions.invalidParameterValueException("vm.addnic.vm.having.snapshots", + Map.of("instance", vmInstance)); } NetworkVO network = _networkDao.findById(networkId); if (network == null) { - throw new InvalidParameterValueException("Unable to find a Network with ID " + networkId); + throw Exceptions.invalidParameterValueException("vm.addnic.network.not.found", + Map.of("instance", vmInstance)); } - if (UserVmManager.SHAREDFSVM.equals(vmInstance.getUserVmType()) && network.getGuestType() == Network.GuestType.Shared) { + if (UserVmManager.SHAREDFSVM.equals(vmInstance.getUserVmType()) && network.getGuestType() == Network.GuestType.Shared) { if ((network.getAclType() != ControlledEntity.ACLType.Account) || (network.getDomainId() != vmInstance.getDomainId()) || (network.getAccountId() != vmInstance.getAccountId())) { - throw new InvalidParameterValueException("Shared network which is not Account scoped and not belonging to the same account can not be added to a Shared FileSystem Instance"); + throw Exceptions.invalidParameterValueException("vm.addnic.network.sharedfs.account.scope", + Map.of("instance", vmInstance, "network", network)); } } @@ -1502,13 +1583,15 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV macAddress = validateOrReplaceMacAddress(macAddress, network); if (_nicDao.findByNetworkIdAndMacAddress(networkId, macAddress) != null) { - throw new CloudRuntimeException("A NIC with this MAC address exists for network: " + network.getUuid()); + throw Exceptions.cloudRuntimeException("vm.addnic.network.nic.mac.exists", + Map.of("instance", vmInstance, "network", network, "macAddress", macAddress)); } NicProfile profile = new NicProfile(ipAddress, null, macAddress); if (ipAddress != null) { if (!(NetUtils.isValidIp4(ipAddress) || NetUtils.isValidIp6(ipAddress))) { - throw new InvalidParameterValueException("Invalid format for IP address parameter: " + ipAddress); + throw Exceptions.cloudRuntimeException("vm.addnic.ip.invalid", + Map.of("instance", vmInstance, "network", network, "ipAddress", ipAddress)); } } @@ -1520,18 +1603,17 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV //ensure network belongs in zone if (network.getDataCenterId() != vmInstance.getDataCenterId()) { - throw new CloudRuntimeException(String.format("%s is in zone: %s but %s is in zone: %s", - vmInstance, dc, network, dataCenterDao.findById(network.getDataCenterId()))); + throw Exceptions.cloudRuntimeException("vm.addnic.zone.mismatch", Map.of("instance", vmInstance, "instanceZone", dc, "network", network, "networkZone", dataCenterDao.findById(network.getDataCenterId()))); } - if (_networkModel.getNicInNetwork(vmInstance.getId(),network.getId()) != null) { + if (_networkModel.getNicInNetwork(vmInstance.getId(), network.getId()) != null) { logger.debug("Instance {} already in network {} going to add another NIC", vmInstance, network); } else { //* get all vms hostNames in the network List hostNames = _vmInstanceDao.listDistinctHostNames(network.getId()); //* verify that there are no duplicates if (hostNames.contains(vmInstance.getHostName())) { - throw new CloudRuntimeException("Network " + network.getName() + " already has an Instance with host name: " + vmInstance.getHostName()); + throw Exceptions.cloudRuntimeException("vm.addnic.hostname.duplicate", Map.of("network", network, "hostname", vmInstance.getHostName())); } } @@ -1546,17 +1628,17 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV _networkMgr.configureExtraDhcpOptions(network, guestNic.getId(), cmd.getDhcpOptionsMap()); cleanUp = false; } catch (ResourceUnavailableException e) { - throw new CloudRuntimeException("Unable to add NIC to " + vmInstance + ": " + e); + throw Exceptions.cloudRuntimeException("vm.addnic.resource.unavailable", Map.of("instance", vmInstance, "error", e.getMessage())); } catch (InsufficientCapacityException e) { - throw new CloudRuntimeException("Insufficient capacity when adding NIC to " + vmInstance + ": " + e); + throw Exceptions.cloudRuntimeException("vm.addnic.insufficient.capacity", Map.of("instance", vmInstance, "error", e.getMessage())); } catch (ConcurrentOperationException e) { - throw new CloudRuntimeException("Concurrent operations on adding NIC to " + vmInstance + ": " + e); + throw Exceptions.cloudRuntimeException("vm.addnic.concurrent.operation", Map.of("instance", vmInstance, "error", e.getMessage())); } finally { if (cleanUp) { try { _itMgr.removeVmFromNetwork(vmInstance, network, null); } catch (ResourceUnavailableException e) { - throw new CloudRuntimeException("Error while cleaning up NIC " + e); + throw Exceptions.cloudRuntimeException("vm.addnic.cleanup.failed", Map.of("error", e.getMessage())); } } } @@ -1572,7 +1654,7 @@ private void validateVmZoneTypeForAddNic(UserVmVO vmInstance, DataCenter dc) { if (_nicDao.countByVmId(vmInstance.getId()) == 0) { return; } - throw new CloudRuntimeException(String.format("Zone %s, has a NetworkType of Basic. Can't add a new NIC to a Instance on a Basic Network", dc)); + throw Exceptions.cloudRuntimeException("", Map.of("zone", dc)); } /** @@ -1594,7 +1676,7 @@ private void checkIfNetExistsForVM(VirtualMachine virtualMachine, Network networ List allNics = _nicDao.listByVmId(virtualMachine.getId()); for (NicVO nic : allNics) { if (nic.getNetworkId() == network.getId()) { - throw new CloudRuntimeException("A NIC already exists for VM:" + virtualMachine.getInstanceName() + " in network: " + network.getUuid()); + throw Exceptions.cloudRuntimeException("vm.addnic.already.exists", Map.of("instance", virtualMachine, "network", network)); } } } @@ -1607,7 +1689,7 @@ protected String validateOrReplaceMacAddress(String macAddress, NetworkVO networ try { macAddress = _networkModel.getNextAvailableMacAddressInNetwork(network.getId()); } catch (InsufficientAddressCapacityException e) { - throw new CloudRuntimeException(String.format("A MAC address cannot be generated for this NIC in the network [%s] ", network)); + throw Exceptions.cloudRuntimeException("vm.addnic.mac.generation.failed", Map.of("network", network)); } } return macAddress; @@ -1632,22 +1714,25 @@ public UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd) throws Invalid UserVmVO vmInstance = _vmDao.findById(vmId); if (vmInstance == null) { - throw new InvalidParameterValueException("Unable to find an Instance with ID " + vmId); + throw Exceptions.invalidParameterValueException("vm.removenic.vm.not.found"); } // Check that Vm does not have VM Snapshots - if (_vmSnapshotDao.findByVm(vmId).size() > 0) { - throw new InvalidParameterValueException("NIC cannot be removed from Instance with Instance Snapshots"); + if (!_vmSnapshotDao.findByVm(vmId).isEmpty()) { + throw Exceptions.invalidParameterValueException("vm.removenic.vm.having.snapshots", + Map.of("instance", vmInstance)); } NicVO nic = _nicDao.findById(nicId); if (nic == null) { - throw new InvalidParameterValueException("Unable to find a NIC with ID " + nicId); + throw Exceptions.invalidParameterValueException("vm.removenic.nic.not.found", + Map.of("instance", vmInstance)); } NetworkVO network = _networkDao.findById(nic.getNetworkId()); if (network == null) { - throw new InvalidParameterValueException("Unable to find a Network with ID " + nic.getNetworkId()); + throw Exceptions.invalidParameterValueException("vm.removenic.network.not.found", + Map.of("nic", nic, "instance", vmInstance)); } // Perform permission check on VM @@ -1656,36 +1741,43 @@ public UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd) throws Invalid // Verify that zone is not Basic DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterId()); if (dc.getNetworkType() == DataCenter.NetworkType.Basic) { - throw new InvalidParameterValueException(String.format("Zone %s, has a NetworkType of Basic. Can't remove a NIC from a VM on a Basic Network", dc)); + throw Exceptions.invalidParameterValueException("vm.removenic.zone.basic.not.allowed", + Map.of("zone", dc)); } // check to see if nic is attached to VM if (nic.getInstanceId() != vmId) { - throw new InvalidParameterValueException(nic + " is not a NIC on " + vmInstance); + throw Exceptions.invalidParameterValueException("vm.removenic.nic.not.attached", + Map.of("nic", nic, "instance", vmInstance)); } // don't delete default NIC on a user VM if (nic.isDefaultNic() && vmInstance.getType() == VirtualMachine.Type.User) { - throw new InvalidParameterValueException("Unable to remove NIC from " + vmInstance + " in " + network + ", NIC is default."); + throw Exceptions.invalidParameterValueException("vm.removenic.default.nic", + Map.of("nic", nic, "instance", vmInstance, "network", network)); } // if specified nic is associated with PF/LB/Static NAT - if (_rulesMgr.listAssociatedRulesForGuestNic(nic).size() > 0) { - throw new InvalidParameterValueException("Unable to remove NIC from " + vmInstance + " in " + network + ", NIC has associated Port forwarding or Load balancer or Static NAT rules."); + if (!_rulesMgr.listAssociatedRulesForGuestNic(nic).isEmpty()) { + throw Exceptions.invalidParameterValueException("vm.removenic.nic.with.rules", + Map.of("nic", nic, "instance", vmInstance, "network", network)); } boolean nicremoved = false; try { nicremoved = _itMgr.removeNicFromVm(vmInstance, nic); } catch (ResourceUnavailableException e) { - throw new CloudRuntimeException("Unable to remove " + network + " from " + vmInstance + ": " + e); + throw Exceptions.cloudRuntimeException("vm.removenic.resource.unavailable", + Map.of("nic", nic, "network", network, "instance", vmInstance, "error", e.getMessage())); } catch (ConcurrentOperationException e) { - throw new CloudRuntimeException("Concurrent operations on removing " + network + " from " + vmInstance + ": " + e); + throw Exceptions.cloudRuntimeException("vm.removenic.concurrent.operation", + Map.of("nic", nic, "network", network, "instance", vmInstance, "error", e.getMessage())); } if (!nicremoved) { - throw new CloudRuntimeException("Unable to remove " + network + " from " + vmInstance); + throw Exceptions.cloudRuntimeException("vm.removenic.failed", + Map.of("nic", nic, "network", network, "instance", vmInstance)); } logger.debug("Successful removal of " + network + " from " + vmInstance); @@ -1701,21 +1793,24 @@ public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) th UserVmVO vmInstance = _vmDao.findById(vmId); if (vmInstance == null) { - throw new InvalidParameterValueException("Unable to find an Instance with ID " + vmId); + throw Exceptions.invalidParameterValueException("vm.updatenic.vm.not.found"); } // Check that Vm does not have VM Snapshots - if (_vmSnapshotDao.findByVm(vmId).size() > 0) { - throw new InvalidParameterValueException("NIC cannot be updated for Instance with Instance Snapshots"); + if (!_vmSnapshotDao.findByVm(vmId).isEmpty()) { + throw Exceptions.invalidParameterValueException("vm.updatenic.vm.having.snapshots", + Map.of("instance", vmInstance)); } NicVO nic = _nicDao.findById(nicId); if (nic == null) { - throw new InvalidParameterValueException("Unable to find a NIC with ID " + nicId); + throw Exceptions.invalidParameterValueException("vm.updatenic.nic.not.found", + Map.of("instance", vmInstance)); } NetworkVO network = _networkDao.findById(nic.getNetworkId()); if (network == null) { - throw new InvalidParameterValueException("Unable to find a Network with ID " + nic.getNetworkId()); + throw Exceptions.invalidParameterValueException("vm.updatenic.nic.network.not.found", + Map.of("nic", nic, "instance", vmInstance)); } // Perform permission check on VM @@ -1724,7 +1819,8 @@ public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) th // Verify that zone is not Basic DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterId()); if (dc.getNetworkType() == DataCenter.NetworkType.Basic) { - throw new CloudRuntimeException(String.format("Zone %s, has a NetworkType of Basic. Can't change default NIC on a Basic Network", dc)); + throw Exceptions.cloudRuntimeException("vm.updatenic.zone.basic.not.allowed", + Map.of("zone", dc)); } // no need to check permissions for network, we'll enumerate the ones they already have access to @@ -1732,16 +1828,18 @@ public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) th //check to see if nic is attached to VM if (nic.getInstanceId() != vmId) { - throw new InvalidParameterValueException(nic + " is not a NIC on " + vmInstance); + throw Exceptions.invalidParameterValueException("vm.updatenic.nic.not.attached", + Map.of("nic", nic, "instance", vmInstance)); } // if current default equals chosen new default, Throw an exception if (nic.isDefaultNic()) { - throw new CloudRuntimeException("refusing to set default NIC because chosen NIC is already the default"); + throw Exceptions.cloudRuntimeException("vm.updatenic.nic.already.default"); } //make sure the VM is Running or Stopped - if ((vmInstance.getState() != State.Running) && (vmInstance.getState() != State.Stopped)) { - throw new CloudRuntimeException("refusing to set default " + vmInstance + " is not Running or Stopped"); + if (!State.Running.equals(vmInstance.getState()) && !State.Stopped.equals(vmInstance.getState())) { + throw Exceptions.cloudRuntimeException("vm.updatenic.vm.not.running.or.stopped", + Map.of("nic", nic, "instance", vmInstance)); } NicProfile existing = null; @@ -1754,7 +1852,7 @@ public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) th if (existing == null) { logger.warn("Failed to update default NIC, no NIC profile found for existing default Network"); - throw new CloudRuntimeException("Failed to find a NIC profile for the existing default Network. This is bad and probably means some sort of configuration corruption"); + throw Exceptions.cloudRuntimeException("vm.updatenic.nic.profile.not.found"); } Network oldDefaultNetwork = null; @@ -1785,9 +1883,9 @@ public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) th newdefault = _networkModel.getDefaultNetworkForVm(vmId); if (newdefault.getId() == existingdefaultnet.getId()) { - throw new CloudRuntimeException("Setting a default nic failed, and we had no default nic, but we were able to set it back to the original"); + throw Exceptions.cloudRuntimeException("vm.updatenic.default.update.failed", Collections.emptyMap()); } - throw new CloudRuntimeException("Failed to change default nic to " + nic + " and now we have no default"); + throw Exceptions.cloudRuntimeException("vm.updatenic.default.no.default", Map.of("nic", nic)); } else if (newdefault.getId() == nic.getNetworkId()) { logger.debug("successfully set default network to " + network + " for " + vmInstance); String nicIdString = Long.toString(nic.getId()); @@ -1816,7 +1914,8 @@ public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) th return _vmDao.findById(vmInstance.getId()); } - throw new CloudRuntimeException(String.format("something strange happened, new default network(%s) is not null, and is not equal to the network(%d) of the chosen nic", newdefault, nic.getNetworkId())); + throw Exceptions.cloudRuntimeException("vm.updatenic.default.unknown.error", + Map.of("newDefaultNetwork", newdefault, "networkId", nic.getNetworkId())); } @Override @@ -1828,35 +1927,35 @@ public UserVm updateNicIpForVirtualMachine(UpdateVmNicIpCmd cmd) { //check whether the nic belongs to user vm. NicVO nicVO = _nicDao.findById(nicId); if (nicVO == null) { - throw new InvalidParameterValueException("There is no nic for the " + nicId); + throw Exceptions.invalidParameterValueException("vm.updatenicip.nic.not.found"); } if (nicVO.getVmType() != VirtualMachine.Type.User) { - throw new InvalidParameterValueException("The nic is not belongs to user vm"); + throw Exceptions.invalidParameterValueException("vm.updatenicip.nic.not.user.vm", + Map.of("nic", nicVO)); } UserVm vm = _vmDao.findById(nicVO.getInstanceId()); if (vm == null) { - throw new InvalidParameterValueException("There is no vm with the nic"); + throw Exceptions.invalidParameterValueException("vm.updatenicip.vm.not.found"); } Network network = _networkDao.findById(nicVO.getNetworkId()); if (network == null) { - throw new InvalidParameterValueException("There is no network with the nic"); + throw Exceptions.invalidParameterValueException("vm.updatenicip.network.not.found"); } // Don't allow to update vm nic ip if network is not in Implemented/Setup/Allocated state if (!(network.getState() == Network.State.Allocated || network.getState() == Network.State.Implemented || network.getState() == Network.State.Setup)) { - throw new InvalidParameterValueException("Network is not in the right state to update vm nic ip. Correct states are: " + Network.State.Allocated + ", " + Network.State.Implemented + ", " - + Network.State.Setup); + throw Exceptions.invalidParameterValueException("vm.updatenicip.network.not.right.state", Map.of("allowedStates", Network.State.Allocated + ", " + Network.State.Implemented + ", " + Network.State.Setup)); } NetworkOfferingVO offering = _networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId()); if (offering == null) { - throw new InvalidParameterValueException("There is no network offering with the network"); + throw Exceptions.invalidParameterValueException("vm.updatenicip.offering.not.found"); } if (!_networkModel.listNetworkOfferingServices(offering.getId()).isEmpty() && vm.getState() != State.Stopped) { - InvalidParameterValueException ex = new InvalidParameterValueException( - "VM is not Stopped, unable to update the vm nic having the specified id"); + InvalidParameterValueException ex = Exceptions.invalidParameterValueException( + "vm.updatenicip.vm.running"); ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } @@ -1869,16 +1968,16 @@ public UserVm updateNicIpForVirtualMachine(UpdateVmNicIpCmd cmd) { logger.debug("Calling the IP allocation ..."); DataCenter dc = _dcDao.findById(network.getDataCenterId()); if (dc == null) { - throw new InvalidParameterValueException("There is no dc with the NIC"); + throw Exceptions.invalidParameterValueException("vm.updatenicip.datacenter.not.found"); } if (dc.getNetworkType() == NetworkType.Advanced && network.getGuestType() == Network.GuestType.Isolated) { try { ipaddr = _ipAddrMgr.allocateGuestIP(network, ipaddr); } catch (InsufficientAddressCapacityException e) { - throw new InvalidParameterValueException(String.format("Allocating IP to guest NIC %s failed, for insufficient address capacity", nicVO)); + throw Exceptions.invalidParameterValueException("vm.updatenicip.allocation.failed.capacity", Map.of("nic", nicVO)); } if (ipaddr == null) { - throw new InvalidParameterValueException(String.format("Allocating IP to guest NIC %s failed, please choose another IP", nicVO)); + throw Exceptions.invalidParameterValueException("vm.updatenicip.allocation.failed.choose.another", Map.of("nic", nicVO)); } if (nicVO.getIPv4Address() != null) { @@ -1887,21 +1986,21 @@ public UserVm updateNicIpForVirtualMachine(UpdateVmNicIpCmd cmd) { updatePortForwardingRulesVmIp(vm.getId(), network.getId(), nicVO.getIPv4Address(), ipaddr); } - } else if (dc.getNetworkType() == NetworkType.Basic || network.getGuestType() == Network.GuestType.Shared) { + } else if (dc.getNetworkType() == NetworkType.Basic || network.getGuestType() == Network.GuestType.Shared) { //handle the basic networks here //for basic zone, need to provide the podId to ensure proper ip alloation Long podId = null; if (dc.getNetworkType() == NetworkType.Basic) { podId = vm.getPodIdToDeployIn(); if (podId == null) { - throw new InvalidParameterValueException("Instance Pod ID is null in Basic zone; can't decide the range for IP allocation"); + throw Exceptions.invalidParameterValueException("vm.updatenicip.pod.id.null"); } } try { ipaddr = _ipAddrMgr.allocatePublicIpForGuestNic(network, podId, ipOwner, ipaddr); if (ipaddr == null) { - throw new InvalidParameterValueException("Allocating IP to guest NIC " + nicVO.getUuid() + " failed, please choose another IP"); + throw Exceptions.invalidParameterValueException("vm.updatenicip.allocation.failed.basic", Map.of("nicId", nicVO.getUuid())); } final IPAddressVO newIp = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), ipaddr); @@ -1924,7 +2023,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { return null; } } else { - throw new InvalidParameterValueException("UpdateVmNicIpCmd is not supported in L2 network"); + throw Exceptions.invalidParameterValueException("vm.updatenicip.not.supported.l2.network"); } logger.debug("Updating IPv4 address of NIC " + nicVO + " to " + ipaddr + "/" + nicVO.getIPv4Netmask() + " with gateway " + nicVO.getIPv4Gateway()); @@ -1942,21 +2041,23 @@ public UserVm updateVirtualMachineNic(UpdateVmNicCmd cmd) { NicVO nic = _nicDao.findById(nicId); if (nic == null) { - throw new InvalidParameterValueException("Unable to find the specified NIC."); + throw Exceptions.invalidParameterValueException("vm.updatenic.nic.not.found"); } UserVmVO vmInstance = _vmDao.findById(nic.getInstanceId()); if (vmInstance == null) { - throw new InvalidParameterValueException("Unable to find a virtual machine associated with the specified NIC."); + throw Exceptions.invalidParameterValueException("vm.updatenic.vm.not.found"); } if (vmInstance.getHypervisorType() != HypervisorType.KVM) { - throw new InvalidParameterValueException("Updating the VM NIC is only supported by the KVM hypervisor."); + throw Exceptions.invalidParameterValueException("vm.updatenic.vm.hypervisor.not.kvm", + Map.of("nic", nic, "instance", vmInstance)); } NetworkVO network = _networkDao.findById(nic.getNetworkId()); if (network == null) { - throw new InvalidParameterValueException("Unable to find NIC's network."); + throw Exceptions.invalidParameterValueException("vm.updatenic.nic.network.not.found", + Map.of("nic", nic, "instance", vmInstance)); } _accountMgr.checkAccess(caller, null, true, vmInstance); @@ -1969,7 +2070,7 @@ public UserVm updateVirtualMachineNic(UpdateVmNicCmd cmd) { try { success = _itMgr.updateVmNic(vmInstance, nic, isNicEnabled); } catch (ResourceUnavailableException e) { - throw new CloudRuntimeException(String.format("Unable to update NIC %s of VM %s in network %s due to: %s.", nic, vmInstance, network.getUuid(), e.getMessage())); + throw Exceptions.cloudRuntimeException(String.format("Unable to update NIC %s of VM %s in network %s due to: %s.", nic, vmInstance, network.getUuid(), e.getMessage())); } catch (ConcurrentOperationException e) { throw new CloudRuntimeException(String.format("Concurrent operations while updating NIC %s for VM %s: %s.", nic, vmInstance, e.getMessage())); } @@ -2027,19 +2128,18 @@ private void updatePortForwardingRulesVmIp(long vmId, long networkId, String old @Override @ActionEvent(eventType = EventTypes.EVENT_VM_UPGRADE, eventDescription = "Upgrading VM", async = true) public UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, - VirtualMachineMigrationException { + VirtualMachineMigrationException { Long vmId = cmd.getId(); Long newServiceOfferingId = cmd.getServiceOfferingId(); VirtualMachine vm = (VirtualMachine) this._entityMgr.findById(VirtualMachine.class, vmId); if (vm == null) { - throw new InvalidParameterValueException("Unable to find VM's UUID"); + throw Exceptions.invalidParameterValueException("vm.upgrade.vm.uuid.not.found"); } if (Hypervisor.HypervisorType.External.equals(vm.getHypervisorType())) { logger.error("Scale VM not supported for {} as it is {} hypervisor instance", vm, Hypervisor.HypervisorType.External.name()); - throw new InvalidParameterValueException(String.format("Operation not supported for instance: %s", - vm.getName())); + throw Exceptions.invalidParameterValueException("vm.upgrade.external.hypervisor.not.supported", Map.of("instance", vm)); } CallContext.current().setEventDetails("Vm Id: " + vm.getUuid()); @@ -2056,13 +2156,13 @@ public UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableEx } return vmInstance; } else { - throw new CloudRuntimeException("Failed to scale the VM"); + throw Exceptions.cloudRuntimeException("vm.upgrade.failed"); } } @Override public boolean upgradeVirtualMachine(Long vmId, Long newServiceOfferingId, Map customParameters) throws ResourceUnavailableException, - ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { + ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId); @@ -2096,7 +2196,7 @@ public boolean upgradeVirtualMachine(Long vmId, Long newServiceOfferingId, Map customParameters) throws ResourceUnavailableException, - ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { + ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { Account caller = CallContext.current().getCallingAccount(); VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId); @@ -2113,7 +2213,7 @@ private boolean upgradeRunningVirtualMachine(Long vmId, Long newServiceOfferingI if (!supportedHypervisorTypes.contains(vmHypervisorType)) { String message = String.format("Scaling the VM dynamically is not supported for VMs running on Hypervisor [%s].", vmInstance.getHypervisorType()); logger.info(message); - throw new InvalidParameterValueException(message); + throw Exceptions.invalidParameterValueException("vm.upgrade.hypervisor.not.supported", Map.of("hypervisorType", vmInstance.getHypervisorType())); } _accountMgr.checkAccess(caller, null, true, vmInstance); @@ -2131,7 +2231,7 @@ private boolean upgradeRunningVirtualMachine(Long vmId, Long newServiceOfferingI ServiceOfferingVO currentServiceOffering = serviceOfferingDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); if (newServiceOffering.isDynamicScalingEnabled() != currentServiceOffering.isDynamicScalingEnabled()) { - throw new InvalidParameterValueException("Unable to Scale VM: since dynamic scaling enabled flag is not same for new service offering and old service offering"); + throw Exceptions.invalidParameterValueException("vm.upgrade.dynamic.scaling.flag.mismatch"); } validateDiskOfferingChecks(currentServiceOffering, newServiceOffering); @@ -2149,16 +2249,15 @@ private boolean upgradeRunningVirtualMachine(Long vmId, Long newServiceOfferingI boolean scalingDown = newSpeed < currentSpeed || newMemory < currentMemory || newCpu < currentCpu; if (scalingDown) { - throw new InvalidParameterValueException(String.format("Scaling down is not supported while the VM is running. The new service offering attributes " + - "{\"memory\": %s, \"CPU speed\": %s, \"vCPUs\": %s} must not be lower than the current values {\"memory\": %s, \"CPU speed\": %s, \"vCPUs\": %s}.", - newMemory, newSpeed, newCpu, currentMemory, currentSpeed, currentCpu)); + throw Exceptions.invalidParameterValueException("vm.upgrade.vm.sunning.scale.down.unsupported", Map.of( + "newMemory", newMemory, "newSpeed", newSpeed, "newCpu", newCpu, + "currentMemory", currentMemory, "currentSpeed", currentSpeed, "currentCpu", currentCpu)); } boolean sameAmountOfResourcesAsThePreviousOffering = newSpeed == currentSpeed && newMemory == currentMemory && newCpu == currentCpu; boolean cpuCapChange = cpuCapEnabledForTheCurrentOffering != cpuCapEnabledForTheNewOffering; if (sameAmountOfResourcesAsThePreviousOffering && (vmHypervisorType != HypervisorType.KVM || !cpuCapChange)) { - throw new InvalidParameterValueException("While the VM is running, scaling to a service offering with the same attributes (memory, CPU speed and vCPUs) " + - "is only allowed when the CPU cap is changed."); + throw Exceptions.invalidParameterValueException("vm.scale.kvm.fixed.offering.not.supported"); } serviceOfferingDao.loadDetails(currentServiceOffering); @@ -2170,7 +2269,7 @@ private boolean upgradeRunningVirtualMachine(Long vmId, Long newServiceOfferingI String newVgpuType = newDetails.get("vgpuType"); if (currentVgpuType != null && (newVgpuType == null || !newVgpuType.equalsIgnoreCase(currentVgpuType))) { - throw new InvalidParameterValueException(String.format("Dynamic scaling of vGPU type is not supported. VM has vGPU Type: [%s].", currentVgpuType)); + throw Exceptions.invalidParameterValueException("vm.scale.vgpu.not.supported", Map.of("vgpuType", currentVgpuType)); } VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); @@ -2190,19 +2289,19 @@ private boolean upgradeRunningVirtualMachine(Long vmId, Long newServiceOfferingI // Check zone wide flag boolean enableDynamicallyScaleVm = EnableDynamicallyScaleVm.valueIn(vmInstance.getDataCenterId()); if (!enableDynamicallyScaleVm) { - throw new PermissionDeniedException("Dynamically scaling Instances is disabled for this zone, please contact your admin."); + throw Exceptions.permissionDeniedException("vm.scale.dynamic.scaling.disabled"); } // Check vm flag if (!vmInstance.isDynamicallyScalable()) { - throw new CloudRuntimeException(String.format("Unable to scale %s as it does not have tools to support dynamic scaling.", vmInstance)); + throw Exceptions.cloudRuntimeException("vm.scale.dynamic.scaling.not.supported", Map.of("instance", vmInstance)); } // Check disable threshold for cluster is not crossed HostVO host = _hostDao.findById(vmInstance.getHostId()); _hostDao.loadDetails(host); if (_capacityMgr.checkIfClusterCrossesThreshold(host.getClusterId(), cpuDiff, memoryDiff)) { - throw new CloudRuntimeException(String.format("Unable to scale %s due to insufficient resources.", vmInstance)); + throw Exceptions.cloudRuntimeException("vm.scale.insufficient.resources", Map.of("instance", vmInstance)); } while (retry-- != 0) { // It's != so that it can match -1. @@ -2270,11 +2369,11 @@ private boolean upgradeRunningVirtualMachine(Long vmId, Long newServiceOfferingI protected void validateDiskOfferingChecks(ServiceOfferingVO currentServiceOffering, ServiceOfferingVO newServiceOffering) { if (currentServiceOffering.getDiskOfferingStrictness() != newServiceOffering.getDiskOfferingStrictness()) { - throw new InvalidParameterValueException("Unable to Scale VM, since disk offering strictness flag is not same for new service offering and old service offering"); + throw Exceptions.invalidParameterValueException("vm.upgrade.disk.offering.strictness.mismatch"); } if (currentServiceOffering.getDiskOfferingStrictness() && !currentServiceOffering.getDiskOfferingId().equals(newServiceOffering.getDiskOfferingId())) { - throw new InvalidParameterValueException("Unable to Scale VM, since disk offering id associated with the old service offering is not same for new service offering"); + throw Exceptions.invalidParameterValueException("vm.upgrade.disk.offering.id.not.same"); } _volService.validateChangeDiskOfferingEncryptionType(currentServiceOffering.getDiskOfferingId(), newServiceOffering.getDiskOfferingId()); @@ -2293,7 +2392,7 @@ private void changeDiskOfferingForRootVolume(Long vmId, DiskOfferingVO newDiskOf for (final VolumeVO rootVolumeOfVm : vols) { DiskOfferingVO currentRootDiskOffering = _diskOfferingDao.findById(rootVolumeOfVm.getDiskOfferingId()); - Long rootDiskSize= null; + Long rootDiskSize = null; Long rootDiskSizeBytes = null; if (customParameters.containsKey(ApiConstants.ROOT_DISK_SIZE)) { rootDiskSize = Long.parseLong(customParameters.get(ApiConstants.ROOT_DISK_SIZE)); @@ -2333,16 +2432,16 @@ private void changeDiskOfferingForRootVolume(Long vmId, DiskOfferingVO newDiskOf } Volume result = _volumeService.changeDiskOfferingForVolume(changeOfferingForVolumeCmd); if (result == null) { - throw new CloudRuntimeException("Failed to change disk offering of the root volume"); + throw Exceptions.cloudRuntimeException("vm.scale.root.volume.change.failed"); } } else if (newDiskOffering.getDiskSize() > 0 && currentRootDiskOffering.getDiskSize() != newDiskOffering.getDiskSize()) { - throw new InvalidParameterValueException("Hypervisor " + hypervisorType + " does not support volume resize"); + throw Exceptions.invalidParameterValueException("vm.scale.hypervisor.simulator.root.resize.not.supported", Map.of("hypervisorType", hypervisorType)); } } } @Override - public HashMap getVolumeStatistics(long clusterId, String poolUuid, StoragePoolType poolType, int timeout) { + public HashMap getVolumeStatistics(long clusterId, String poolUuid, StoragePoolType poolType, int timeout) { List neighbors = _resourceMgr.listHostsInClusterByStatus(clusterId, Status.Up); StoragePoolVO storagePool = _storagePoolDao.findPoolByUUID(poolUuid); HashMap volumeStatsByUuid = new HashMap<>(); @@ -2370,14 +2469,14 @@ public HashMap getVolumeStatistics(long clusterId, Str answer = storageManager.getVolumeStats(storagePool, cmd); } else { if (timeout > 0) { - cmd.setWait(timeout/1000); + cmd.setWait(timeout / 1000); } answer = _agentMgr.easySend(neighbor.getId(), cmd); } if (answer != null && answer instanceof GetVolumeStatsAnswer) { - GetVolumeStatsAnswer volstats = (GetVolumeStatsAnswer)answer; + GetVolumeStatsAnswer volstats = (GetVolumeStatsAnswer) answer; if (volstats.getVolumeStats() != null) { volumeStatsByUuid.putAll(volstats.getVolumeStats()); } @@ -2408,29 +2507,29 @@ public UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationE final UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { - throw new InvalidParameterValueException("Unable to find an Instance with id " + vmId); + throw Exceptions.invalidParameterValueException("vm.recover.vm.not.found", Map.of("vmId", vmId)); } if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) { - throw new InvalidParameterValueException("Operation not supported on Shared FileSystem Instance"); + throw Exceptions.invalidParameterValueException("vm.recover.sharedfs.not.supported"); } // When trying to expunge, permission is denied when the caller is not an admin and the AllowUserExpungeRecoverVm is false for the caller. if (!_accountMgr.isAdmin(userId) && !AllowUserExpungeRecoverVm.valueIn(userId)) { - throw new PermissionDeniedException("Recovering a vm can only be done by an Admin. Or when the allow.user.expunge.recover.vm key is set."); + throw Exceptions.permissionDeniedException("vm.recover.permission.denied"); } if (vm.getRemoved() != null) { if (logger.isDebugEnabled()) { logger.debug("Unable to find vm. vm is removed: {}", vm); } - throw new InvalidParameterValueException("Unable to find vm by id " + vm.getUuid()); + throw Exceptions.invalidParameterValueException("vm.recover.vm.removed", Map.of("vmUuid", vm.getUuid())); } if (vm.getState() != State.Destroyed) { if (logger.isDebugEnabled()) { logger.debug("vm {} is not in the Destroyed state. current sate: {}", vm, vm.getState()); } - throw new InvalidParameterValueException("Vm with id " + vm.getUuid() + " is not in the right state"); + throw Exceptions.invalidParameterValueException("vm.recover.vm.not.destroyed", Map.of("vmUuid", vm.getUuid())); } if (logger.isDebugEnabled()) { @@ -2438,13 +2537,14 @@ public UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationE } Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { - @Override public void doInTransactionWithoutResult(TransactionStatus status) throws ResourceAllocationException { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) throws ResourceAllocationException { Account account = _accountDao.lockRow(vm.getAccountId(), true); // if the account is deleted, throw error if (account.getRemoved() != null) { - throw new CloudRuntimeException("Unable to recover Instance as the Account is deleted"); + throw Exceptions.cloudRuntimeException("vm.recover.account.deleted"); } // Get serviceOffering for Virtual Machine @@ -2464,10 +2564,10 @@ public UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationE try { if (!_itMgr.stateTransitTo(vm, VirtualMachine.Event.RecoveryRequested, null)) { logger.debug("Unable to recover the vm {} because it is not in the correct state. current state: {}", vm, vm.getState()); - throw new InvalidParameterValueException(String.format("Unable to recover the vm %s because it is not in the correct state. current state: %s", vm, vm.getState())); + throw Exceptions.invalidParameterValueException("vm.recover.state.transition.failed", Map.of("instance", vm, "instanceState", vm.getState())); } } catch (NoTransitionException e) { - throw new InvalidParameterValueException(String.format("Unable to recover the vm %s because it is not in the correct state. current state: %s", vm, vm.getState())); + throw Exceptions.invalidParameterValueException("vm.recover.state.transition.failed", Map.of("instance", vm, "instanceState", vm.getState())); } // Recover the VM's disks @@ -2531,10 +2631,10 @@ public boolean configure(String name, Map params) throws Configu String vmIpWorkers = configs.get(VmIpFetchTaskWorkers.value()); int vmipwrks = NumbersUtil.parseInt(vmIpWorkers, 10); - _vmIpFetchExecutor = Executors.newScheduledThreadPool(vmipwrks, new NamedThreadFactory("UserVm-ipfetch")); + _vmIpFetchExecutor = Executors.newScheduledThreadPool(vmipwrks, new NamedThreadFactory("UserVm-ipfetch")); String aggregationRange = configs.get("usage.stats.job.aggregation.range"); - int _usageAggregationRange = NumbersUtil.parseInt(aggregationRange, 1440); + int _usageAggregationRange = NumbersUtil.parseInt(aggregationRange, 1440); int HOURLY_TIME = 60; final int DAILY_TIME = 60 * 24; if (_usageAggregationRange == DAILY_TIME) { @@ -2578,7 +2678,7 @@ private void loadVmDetailsInMapForExternalDhcpIp() { List networks = _networkDao.listByGuestType(Network.GuestType.Shared); networks.addAll(_networkDao.listByGuestType(Network.GuestType.L2)); - for (NetworkVO network: networks) { + for (NetworkVO network : networks) { if (GuestType.L2.equals(network.getGuestType()) || _networkModel.isSharedNetworkWithoutServices(network.getId())) { List nics = _nicDao.listByNetworkId(network.getId()); @@ -2900,7 +3000,7 @@ protected void runInContext() { protected void verifyVmLimits(UserVmVO vmInstance, Map details) { Account owner = _accountDao.findById(vmInstance.getAccountId()); if (owner == null) { - throw new InvalidParameterValueException("The owner of " + vmInstance + " does not exist: " + vmInstance.getAccountId()); + throw Exceptions.invalidParameterValueException("vm.update.owner.not.found", Map.of("instance", vmInstance)); } long newCpu = NumberUtils.toLong(details.get(VmDetailConstants.CPU_NUMBER)); @@ -2919,8 +3019,7 @@ protected void verifyVmLimits(UserVmVO vmInstance, Map details) } else { if (details.containsKey(VmDetailConstants.CPU_NUMBER) || details.containsKey(VmDetailConstants.MEMORY) || details.containsKey(VmDetailConstants.CPU_SPEED)) { - throw new InvalidParameterValueException("CPU number, Memory and CPU speed cannot be updated for a " + - "non-dynamic offering"); + throw Exceptions.invalidParameterValueException("vm.update.non.dynamic.cpu.memory.not.updatable"); } } if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) { @@ -2945,7 +3044,7 @@ protected void verifyVmLimits(UserVmVO vmInstance, Map details) } } catch (ResourceAllocationException e) { logger.error(String.format("Failed to updated VM due to: %s", e.getLocalizedMessage())); - throw new InvalidParameterValueException(e.getLocalizedMessage()); + throw Exceptions.invalidParameterValueException(e.getLocalizedMessage()); } finally { ReservationHelper.closeAll(reservations); } @@ -2962,7 +3061,7 @@ protected void updateVmExtraConfig(UserVmVO userVm, String extraConfig, boolean logger.info("Adding extra configuration to user vm: {}", userVm.getUuid()); addExtraConfig(userVm, extraConfig); } else { - throw new InvalidParameterValueException("attempted setting extraconfig but enable.additional.vm.configuration is disabled"); + throw Exceptions.invalidParameterValueException("vm.update.extraconfig.disabled"); } } } @@ -2991,7 +3090,7 @@ public UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableEx UserVmVO userVm = _vmDao.findById(cmd.getId()); if (userVm != null && UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) { - throw new InvalidParameterValueException("Operation not supported on Shared FileSystem Instance"); + throw Exceptions.invalidParameterValueException("vm.update.sharedfs.not.supported"); } String userData = cmd.getUserData(); @@ -3024,7 +3123,7 @@ public UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableEx List existingDetails = vmInstanceDetailsDao.listDetails(id); if (cleanupDetails) { if (template != null && template.isDeployAsIs()) { - throw new InvalidParameterValueException("Detail settings are read from OVA, it cannot be cleaned up by API call."); + throw Exceptions.invalidParameterValueException("vm.update.ova.details.cleanup.not.allowed"); } if (caller != null && caller.getType() == Account.Type.ADMIN) { for (final VMInstanceDetailVO detail : existingDetails) { @@ -3047,11 +3146,11 @@ public UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableEx if (details.containsKey(VmDetailConstants.INSTANCE_LEASE_EXECUTION) || details.containsKey(VmDetailConstants.INSTANCE_LEASE_EXPIRY_DATE) || details.containsKey(VmDetailConstants.INSTANCE_LEASE_EXPIRY_ACTION)) { - throw new InvalidParameterValueException("lease parameters should not be included in details as key"); + throw Exceptions.invalidParameterValueException("vm.update.lease.params.in.details"); } if (details.containsKey("extraconfig")) { - throw new InvalidParameterValueException("'extraconfig' should not be included in details as key"); + throw Exceptions.invalidParameterValueException("vm.update.extraconfig.in.details"); } if (template != null && template.isDeployAsIs()) { @@ -3067,7 +3166,7 @@ public UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableEx if (detailVO != null && ObjectUtils.allNotNull(detailVO.getValue(), details.get(detailKey)) && detailVO.getValue().equals(details.get(detailKey))) { continue; } - throw new InvalidParameterValueException("Detail settings are read from OVA, it cannot be changed by API call."); + throw Exceptions.invalidParameterValueException("vm.update.ova.details.change.not.allowed"); } } @@ -3077,13 +3176,13 @@ public UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableEx // Ensure denied or read-only detail is not passed by non-root-admin user for (final String detailName : details.keySet()) { if (userDenyListedSettings.contains(detailName)) { - throw new InvalidParameterValueException("You're not allowed to add or edit the restricted setting: " + detailName); + throw Exceptions.invalidParameterValueException("vm.update.detail.restricted", Map.of("detailName", detailName)); } if (userReadOnlySettings.contains(detailName)) { - throw new InvalidParameterValueException("You're not allowed to add or edit the read-only setting: " + detailName); + throw Exceptions.invalidParameterValueException("vm.update.detail.readonly", Map.of("detailName", detailName)); } if (existingDetails.stream().anyMatch(d -> Objects.equals(d.getName(), detailName) && !d.isDisplay())) { - throw new InvalidParameterValueException("You're not allowed to add or edit the non-displayable setting: " + detailName); + throw Exceptions.invalidParameterValueException("vm.update.detail.nondisplayable", Map.of("detailName", detailName)); } } // Add any existing user denied or read-only details. We do it here because admins would already provide these (or can delete them). @@ -3154,7 +3253,7 @@ protected void updateDisplayVmFlag(Boolean isDisplayVm, Long id, UserVmVO vmInst protected void validateInputsAndPermissionForUpdateVirtualMachineCommand(UpdateVMCmd cmd) { UserVmVO vmInstance = _vmDao.findById(cmd.getId()); if (vmInstance == null) { - throw new InvalidParameterValueException("unable to find virtual machine with id: " + cmd.getId()); + throw Exceptions.invalidParameterValueException("vm.update.vm.not.found"); } validateGuestOsIdForUpdateVirtualMachineCommand(cmd); Account caller = CallContext.current().getCallingAccount(); @@ -3166,7 +3265,7 @@ protected void validateGuestOsIdForUpdateVirtualMachineCommand(UpdateVMCmd cmd) if (osTypeId != null) { GuestOSVO guestOS = _guestOSDao.findById(osTypeId); if (guestOS == null) { - throw new InvalidParameterValueException("Please specify a valid guest OS ID."); + throw Exceptions.invalidParameterValueException("vm.update.guestos.not.found"); } } } @@ -3225,19 +3324,19 @@ public UserVm updateVirtualMachine(long id, String displayName, String group, Bo ) throws ResourceUnavailableException, InsufficientCapacityException { UserVmVO vm = _vmDao.findById(id); if (vm == null) { - throw new CloudRuntimeException("Unable to find virtual machine with id " + id); + throw Exceptions.cloudRuntimeException("vm.update.vm.not.found"); } if (instanceName != null) { VMInstanceVO vmInstance = _vmInstanceDao.findVMByInstanceName(instanceName); if (vmInstance != null && vmInstance.getId() != id) { - throw new CloudRuntimeException("Instance name : " + instanceName + " is not unique"); + throw Exceptions.cloudRuntimeException("vm.update.instance.name.not.unique", Map.of("instanceName", instanceName)); } } if (vm.getState() == State.Error || vm.getState() == State.Expunging) { logger.error("vm {} is not in the correct state. current state: {}", vm, vm.getState()); - throw new InvalidParameterValueException(String.format("Vm %s is not in the right state", vm)); + throw Exceptions.invalidParameterValueException("vm.update.vm.not.right.state", Map.of("instance", vm)); } if (displayName == null) { @@ -3250,7 +3349,7 @@ public UserVm updateVirtualMachine(long id, String displayName, String group, Bo ServiceOffering offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); if (!offering.isOfferHA() && ha) { - throw new InvalidParameterValueException("Can't enable ha for the vm as it's created from the Service offering having HA disabled"); + throw Exceptions.invalidParameterValueException("vm.update.ha.offering.disabled"); } if (isDisplayVmEnabled == null) { @@ -3294,14 +3393,14 @@ public UserVm updateVirtualMachine(long id, String displayName, String group, Bo if (isDynamicallyScalable == true) { VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); if (!template.isDynamicallyScalable()) { - throw new InvalidParameterValueException("Dynamic Scaling cannot be enabled for the Instance since its Template does not have dynamic scaling enabled"); + throw Exceptions.invalidParameterValueException("vm.update.dynamic.scaling.template.not.supported"); } if (!offering.isDynamicScalingEnabled()) { - throw new InvalidParameterValueException("Dynamic Scaling cannot be enabled for the Instance since its service offering does not have dynamic scaling enabled"); + throw Exceptions.invalidParameterValueException("vm.update.dynamic.scaling.offering.not.supported"); } if (!UserVmManager.EnableDynamicallyScaleVm.valueIn(vm.getDataCenterId())) { logger.debug("Dynamic Scaling cannot be enabled for the VM {} since the global setting enable.dynamic.scale.vm is set to false", vm); - throw new InvalidParameterValueException("Dynamic Scaling cannot be enabled for the VM since corresponding global setting is set to false"); + throw Exceptions.invalidParameterValueException("vm.update.dynamic.scaling.global.setting.disabled"); } } } @@ -3359,7 +3458,7 @@ private void checkAndUpdateSecurityGroupForVM(List securityGroupIdList, Us boolean isVMware = (vm.getHypervisorType() == HypervisorType.VMware); if (securityGroupIdList != null && isVMware) { - throw new InvalidParameterValueException("Security group feature is not supported for VMware hypervisor"); + throw Exceptions.invalidParameterValueException("vm.update.securitygroup.vmware.not.supported"); } else if (securityGroupIdList != null) { DataCenterVO zone = _dcDao.findById(vm.getDataCenterId()); List networkIds = new ArrayList<>(); @@ -3393,7 +3492,7 @@ private void updateSecurityGroup(UserVmVO vm, List securityGroupIdList) { // Add instance in provided groups _securityGroupMgr.addInstanceToGroups(vm, securityGroupIdList); } else { - throw new InvalidParameterValueException(String.format("VM %s must be stopped prior to update security groups", vm.getUuid())); + throw Exceptions.invalidParameterValueException("vm.update.securitygroup.vm.not.stopped", Map.of("instance", vm)); } } @@ -3402,7 +3501,7 @@ protected void updateUserData(UserVm vm) throws ResourceUnavailableException, In if (result) { logger.debug("User data successfully updated for vm id: {}", vm); } else { - throw new CloudRuntimeException("Failed to reset userdata for the virtual machine "); + throw Exceptions.cloudRuntimeException("vm.update.userdata.failed"); } } @@ -3421,17 +3520,17 @@ private void updateDns(UserVmVO vm, String hostName) throws ResourceUnavailableE Commands commands = new Commands(Command.OnError.Stop); commandSetupHelper.createDhcpEntryCommand(router, vm, nic, false, commands); if (!nwHelper.sendCommandsToRouter(router, commands)) { - throw new CloudRuntimeException(String.format("Unable to send commands to virtual router: %s", router.getHostId())); + throw Exceptions.cloudRuntimeException("vm.update.dns.router.send.failed", Map.of("router", router.getHostId())); } Answer answer = commands.getAnswer("dhcp"); if (answer == null || !answer.getResult()) { - throw new CloudRuntimeException("Failed to update hostname"); + throw Exceptions.cloudRuntimeException("vm.update.dns.hostname.update.failed"); } updateUserData(vm); } } } catch (CloudRuntimeException e) { - throw new CloudRuntimeException(String.format("Failed to update hostname of VM %s to %s", vm.getInstanceName(), vm.getHostName())); + throw Exceptions.cloudRuntimeException("vm.update.dns.hostname.failed", Map.of("instance", vm.getInstanceName(), "hostname", vm.getHostName())); } } } @@ -3461,7 +3560,7 @@ protected boolean applyUserData(HypervisorType hyperVisorType, UserVm vm, Nic ni if (_networkModel.areServicesSupportedByNetworkOffering(network.getNetworkOfferingId(), Service.UserData)) { UserDataServiceProvider element = _networkModel.getUserDataUpdateProvider(network); if (element == null) { - throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for UserData update"); + throw Exceptions.cloudRuntimeException("vm.update.userdata.network.element.not.found"); } boolean result = element.saveUserData(network, nicProfile, vmProfile); if (!result) { @@ -3519,12 +3618,11 @@ public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityE // Verify input parameters UserVmVO vmInstance = _vmDao.findById(vmId); if (vmInstance == null) { - throw new InvalidParameterValueException("Unable to find a Instance with ID " + vmId); + throw Exceptions.invalidParameterValueException("vm.reboot.vm.not.found"); } if (vmInstance.getState() != State.Running) { - throw new InvalidParameterValueException(String.format("The Instance %s (%s) is not running, unable to reboot it", - vmInstance.getUuid(), vmInstance.getDisplayNameOrHostName())); + throw Exceptions.invalidParameterValueException("vm.reboot.vm.not.running", Map.of("instance", vmInstance)); } _accountMgr.checkAccess(caller, null, true, vmInstance); @@ -3539,12 +3637,12 @@ public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityE return restoreVMInternal(caller, vmInstance); } } else { - throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId + " corresponding to the Instance"); + throw Exceptions.invalidParameterValueException("vm.reboot.serviceoffering.not.found"); } Boolean enterSetup = cmd.getBootIntoSetup(); if (enterSetup != null && enterSetup && !HypervisorType.VMware.equals(vmInstance.getHypervisorType())) { - throw new InvalidParameterValueException("Booting into a hardware setup menu is not implemented on " + vmInstance.getHypervisorType()); + throw Exceptions.invalidParameterValueException("vm.reboot.setup.menu.hypervisor.not.supported", Map.of("hypervisorType", vmInstance.getHypervisorType())); } UserVm userVm = rebootVirtualMachine(CallContext.current().getCallingUserId(), vmId, enterSetup == null ? false : cmd.getBootIntoSetup(), cmd.isForced()); @@ -3575,13 +3673,13 @@ protected void checkExpungeVmPermission(Account callingAccount, String apiKey) { logger.debug(String.format("Checking if [%s] has permission for expunging VMs.", callingAccount)); if (!_accountMgr.isAdmin(callingAccount.getId()) && !getConfigAllowUserExpungeRecoverVm(callingAccount.getId())) { logger.error(String.format("Parameter [%s] can only be passed by Admin accounts or when the allow.user.expunge.recover.vm key is true.", ApiConstants.EXPUNGE)); - throw new PermissionDeniedException("Account does not have permission for expunging."); + throw Exceptions.permissionDeniedException("vm.expunge.permission.denied"); } try { _accountMgr.checkApiAccess(callingAccount, BaseCmd.getCommandNameByClass(ExpungeVMCmd.class), apiKey); } catch (PermissionDeniedException ex) { logger.error(String.format("Role [%s] of [%s] does not have permission for expunging VMs.", callingAccount.getRoleId(), callingAccount)); - throw new PermissionDeniedException("Account does not have permission for expunging."); + throw Exceptions.permissionDeniedException("vm.expunge.permission.denied"); } } @@ -3614,20 +3712,19 @@ public UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, C UserVmVO vm = _vmDao.findById(vmId); if (vm == null || vm.getRemoved() != null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + throw Exceptions.invalidParameterValueException("vm.destroy.vm.not.found"); } if (Arrays.asList(State.Destroyed, State.Expunging).contains(vm.getState()) && !expunge) { logger.debug("Vm {} is already destroyed", vm); return vm; } - validateVmDestroyAllowed(vm, forced); // check if there are active volume snapshots tasks logger.debug("Checking if there are any ongoing Snapshots on the ROOT volumes associated with Instance {}", vm); if (checkStatusOfVolumeSnapshots(vm, Volume.Type.ROOT)) { - throw new CloudRuntimeException("There is/are unbacked up Snapshot(s) on ROOT volume, Instance destroy is not permitted, please try again later."); + throw Exceptions.cloudRuntimeException("vm.destroy.unbacked.snapshots"); } logger.debug("Found no ongoing Snapshots on volume of type ROOT, for the Instance {}", vm); @@ -3661,7 +3758,7 @@ public UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, C } if (!expunged) { transitionExpungingToError(vm.getId()); - throw new CloudRuntimeException("Failed to expunge VM " + vm.getUuid() + (StringUtils.isNotBlank(errorMsg) ? " due to: " + errorMsg : "")); + throw Exceptions.cloudRuntimeException("vm.destroy.expunge.failed", Map.of("instance", vm)); } } @@ -3686,12 +3783,10 @@ private void validateVmDestroyAllowed(UserVmVO vm, boolean forced) { return; } if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) { - throw new InvalidParameterValueException("Operation not supported on Shared FileSystem Instance"); + throw Exceptions.invalidParameterValueException("vm.destroy.sharedfs.not.supported"); } if (vm.isDeleteProtection()) { - throw new InvalidParameterValueException(String.format( - "Instance [id = %s, name = %s] has delete protection enabled and cannot be deleted.", - vm.getUuid(), vm.getName())); + throw Exceptions.invalidParameterValueException("vm.destroy.delete.protection.enabled", Map.of("instance", vm)); } // check if vm belongs to AutoScale vm group in Disabled state @@ -3708,7 +3803,7 @@ private List getVolumesFromIds(DestroyVMCmd cmd) { VolumeVO vol = _volsDao.findById(volId); if (vol == null) { - throw new InvalidParameterValueException("Unable to find volume with ID: " + volId); + throw Exceptions.invalidParameterValueException("vm.destroy.volume.not.found"); } volumes.add(vol); } @@ -3732,7 +3827,7 @@ public InstanceGroupVO createVmGroup(CreateVMGroupCmd cmd) { boolean isNameInUse = _vmGroupDao.isNameInUse(accountId, groupName); if (isNameInUse) { - throw new InvalidParameterValueException(String.format("Unable to create Instance group, a group with name %s already exists for Account %s", groupName, owner)); + throw Exceptions.invalidParameterValueException("vm.group.create.name.exists", Map.of("groupName", groupName, "account", owner)); } return createVmGroup(groupName, accountId); @@ -3773,7 +3868,7 @@ public boolean deleteVmGroup(DeleteVMGroupCmd cmd) { // Verify input parameters InstanceGroupVO group = _vmGroupDao.findById(groupId); if ((group == null) || (group.getRemoved() != null)) { - throw new InvalidParameterValueException("unable to find a vm group with id " + groupId); + throw Exceptions.invalidParameterValueException("vm.group.delete.not.found"); } _accountMgr.checkAccess(caller, null, true, group); @@ -3826,7 +3921,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { InstanceGroupVO ngrpLock = _vmGroupDao.lockRow(groupFinal.getId(), false); if (ngrpLock == null) { logger.warn("Failed to acquire lock on Instance group {}", groupFinal); - throw new CloudRuntimeException(String.format("Failed to acquire lock on Instance group %s", groupFinal)); + throw Exceptions.cloudRuntimeException("vm.group.add.lock.failed", Map.of("group", groupFinal)); } // Currently don't allow to assign a vm to more than one group @@ -3923,7 +4018,7 @@ public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOff Network defaultNetwork = _networkModel.getExclusiveGuestNetwork(zone.getId()); if (defaultNetwork == null) { - throw new InvalidParameterValueException("Unable to find a default network to start a vm"); + throw Exceptions.invalidParameterValueException("vm.deploy.basic.network.not.found"); } else { if (!_itMgr.isBlankInstance(template)) { networkList.add(_networkDao.findById(defaultNetwork.getId())); @@ -3945,7 +4040,7 @@ private List validateAndUpdateSecurityGroupsIfNeeded(List securityGr boolean isVmWare, Network defaultNetwork, VirtualMachineTemplate template) { if (securityGroupIdList != null && isVmWare) { - throw new InvalidParameterValueException("Security group feature is not supported for VMware hypervisor"); + throw Exceptions.invalidParameterValueException("vm.deploy.securitygroup.vmware.not.supported"); } else if (!isVmWare && _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork) && _networkModel.canAddDefaultSecurityGroup()) { //add the default securityGroup only if no security group is specified if (CollectionUtils.isEmpty(securityGroupIdList) && !_itMgr.isBlankInstance(template)) { @@ -3995,7 +4090,7 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service } else if (networkIdList == null || networkIdList.isEmpty()) { Network networkWithSecurityGroup = _networkModel.getNetworkWithSGWithFreeIPs(owner, zone.getId()); if (networkWithSecurityGroup == null) { - throw new InvalidParameterValueException("No network with security enabled is found in zone id=" + zone.getUuid()); + throw Exceptions.invalidParameterValueException("vm.deploy.sg.network.not.found", Map.of("zone", zone)); } networkList.add(_networkDao.findById(networkWithSecurityGroup.getId())); @@ -4003,11 +4098,11 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service } else if (securityGroupIdList != null && !securityGroupIdList.isEmpty()) { if (isVmWare) { - throw new InvalidParameterValueException("Security group feature is not supported for VMware hypervisor"); + throw Exceptions.invalidParameterValueException("vm.deploy.securitygroup.vmware.not.supported"); } // Only one network can be specified, and it should be security group enabled if (networkIdList.size() > 1 && template.getHypervisorType() != HypervisorType.KVM && hypervisor != HypervisorType.KVM) { - throw new InvalidParameterValueException("Only support one network per VM if security group enabled"); + throw Exceptions.invalidParameterValueException("vm.deploy.sg.multiple.networks.not.supported"); } for (Long networkId : networkIdList) { @@ -4015,11 +4110,11 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service NetworkOffering ntwkOffering = _networkOfferingDao.findById(network.getNetworkOfferingId()); if (network == null) { - throw new InvalidParameterValueException("Unable to find network by id " + networkId); + throw Exceptions.invalidParameterValueException("vm.deploy.network.not.found"); } if (!_networkModel.isSecurityGroupSupportedInNetwork(network) && (ntwkOffering.getGuestType() != GuestType.L2)) { - throw new InvalidParameterValueException(String.format("Network is not security group enabled or not L2 network: %s", network)); + throw Exceptions.invalidParameterValueException("vm.deploy.sg.network.not.sg.enabled", Map.of("network", network)); } _accountMgr.checkAccess(owner, AccessType.UseEntry, false, network); @@ -4034,7 +4129,7 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service NetworkVO network = _networkDao.findById(networkId); if (network == null) { - throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); + throw Exceptions.invalidParameterValueException("vm.deploy.network.not.found"); } boolean isSecurityGroupEnabled = _networkModel.isSecurityGroupSupportedInNetwork(network); @@ -4043,7 +4138,7 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service } if (network.getTrafficType() != TrafficType.Guest || !Arrays.asList(GuestType.Shared, GuestType.L2).contains(network.getGuestType())) { - throw new InvalidParameterValueException("Can specify only Shared or L2 Guest networks when deploy vm in Advance Security Group enabled zone"); + throw Exceptions.invalidParameterValueException("vm.deploy.sg.advanced.network.type.invalid"); } _accountMgr.checkAccess(owner, AccessType.UseEntry, false, network); @@ -4116,17 +4211,17 @@ public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serv for (Long networkId : networkIdList) { NetworkVO network = _networkDao.findById(networkId); if (network == null) { - throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); + throw Exceptions.invalidParameterValueException("vm.deploy.network.not.found"); } if (network.getVpcId() != null) { // Only ISOs, XenServer, KVM, and VmWare template types are // supported for vpc networks if (template.getFormat() != ImageFormat.ISO && !vpcSupportedHTypes.contains(template.getHypervisorType())) { - throw new InvalidParameterValueException("Can't create Instance from Template with hypervisor " + template.getHypervisorType() + " in VPC Network " + network); + throw Exceptions.invalidParameterValueException("vm.deploy.advanced.vpc.template.hypervisor.not.supported", Map.of("hypervisorType", template.getHypervisorType(), "network", network)); } else if (template.getFormat() == ImageFormat.ISO && !vpcSupportedHTypes.contains(hypervisor)) { // Only XenServer, KVM, and VMware hypervisors are supported // for vpc networks - throw new InvalidParameterValueException("Can't create Instance of hypervisor type " + hypervisor + " in VPC Network"); + throw Exceptions.invalidParameterValueException("vm.deploy.advanced.vpc.hypervisor.not.supported", Map.of("hypervisorType", hypervisor)); } } @@ -4136,7 +4231,7 @@ public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serv // don't allow to use system networks NetworkOffering networkOffering = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); if (networkOffering.isSystemOnly()) { - throw new InvalidParameterValueException(String.format("Network id=%s is system only and can't be used for vm deployment", network.getUuid())); + throw Exceptions.invalidParameterValueException("vm.deploy.network.system.only", Map.of("network", network)); } networkList.add(network); } @@ -4164,17 +4259,17 @@ private NetworkVO getNetworkToAddToNetworkList(VirtualMachineTemplate template, List vpcSupportedHTypes, Long networkId) { NetworkVO network = _networkDao.findById(networkId); if (network == null) { - throw new InvalidParameterValueException("Unable to find network by id " + networkId); + throw Exceptions.invalidParameterValueException("vm.deploy.network.not.found"); } if (network.getVpcId() != null) { // Only ISOs, XenServer, KVM, and VmWare template types are // supported for vpc networks if (template.getFormat() != ImageFormat.ISO && !vpcSupportedHTypes.contains(template.getHypervisorType())) { - throw new InvalidParameterValueException("Can't create Instance from Template with hypervisor " + template.getHypervisorType() + " in VPC Network " + network); + throw Exceptions.invalidParameterValueException("vm.deploy.advanced.vpc.template.hypervisor.not.supported", Map.of("hypervisorType", template.getHypervisorType(), "network", network)); } else if (template.getFormat() == ImageFormat.ISO && !vpcSupportedHTypes.contains(hypervisor)) { // Only XenServer, KVM, and VMware hypervisors are supported // for vpc networks - throw new InvalidParameterValueException("Can't create Instance of hypervisor type " + hypervisor + " in VPC Network"); + throw Exceptions.invalidParameterValueException("vm.deploy.advanced.vpc.hypervisor.not.supported", Map.of("hypervisorType", hypervisor)); } } @@ -4183,7 +4278,7 @@ private NetworkVO getNetworkToAddToNetworkList(VirtualMachineTemplate template, // don't allow to use system networks NetworkOffering networkOffering = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); if (networkOffering.isSystemOnly()) { - throw new InvalidParameterValueException(String.format("Network id=%s is system only and can't be used for vm deployment", network.getUuid())); + throw Exceptions.invalidParameterValueException("vm.deploy.network.system.only", Map.of("network", network)); } return network; } @@ -4201,25 +4296,24 @@ private NetworkVO getDefaultNetwork(DataCenter zone, Account owner, boolean sele List requiredOfferings = _networkOfferingDao.listByAvailability(Availability.Required, false); if (requiredOfferings.size() < 1) { - throw new InvalidParameterValueException("Unable to find network offering with availability=" + Availability.Required - + " to automatically create the network as a part of vm creation"); + throw Exceptions.invalidParameterValueException("vm.deploy.network.offering.required.not.found"); } if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { // get Virtual networks List virtualNetworks = _networkModel.listNetworksForAccount(owner.getId(), zone.getId(), Network.GuestType.Isolated); if (virtualNetworks == null) { - throw new InvalidParameterValueException("No (virtual) networks are found for Account " + owner); + throw Exceptions.invalidParameterValueException("vm.deploy.network.none.found.for.account", Map.of("account", owner)); } if (virtualNetworks.isEmpty()) { defaultNetwork = createDefaultNetworkForAccount(zone, owner, requiredOfferings); } else if (virtualNetworks.size() > 1 && !selectAny) { - throw new InvalidParameterValueException("More than 1 default Isolated networks are found for Account " + owner + "; please specify networkIds"); + throw Exceptions.invalidParameterValueException("vm.deploy.network.multiple.default.found", Map.of("account", owner)); } else { defaultNetwork = _networkDao.findById(virtualNetworks.get(0).getId()); } } else { - throw new InvalidParameterValueException(String.format("Required network offering %s is not in %s", requiredOfferings.get(0), NetworkOffering.State.Enabled)); + throw Exceptions.invalidParameterValueException("vm.deploy.network.offering.required.not.enabled", Map.of("offering", requiredOfferings.get(0))); } return defaultNetwork; @@ -4232,8 +4326,7 @@ private NetworkVO createDefaultNetworkForAccount(DataCenter zone, Account owner, // Validate physical network PhysicalNetwork physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); if (physicalNetwork == null) { - throw new InvalidParameterValueException("Unable to find physical network with id: " + physicalNetworkId + " and tag: " - + requiredOfferings.get(0).getTags()); + throw Exceptions.invalidParameterValueException("vm.deploy.physical.network.not.found", Map.of("physicalNetworkId", physicalNetworkId, "tag", requiredOfferings.get(0).getTags())); } logger.debug("Creating Network for Account {} from the network offering {} as a part of deployVM process", owner, requiredOfferings.get(0)); Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", @@ -4257,7 +4350,7 @@ private void verifyExtraDhcpOptionsNetwork(Map> dhc } if (!networkFound) { - throw new InvalidParameterValueException("VM does not has a nic in the Network (" + networkUuid + ") that is specified in the extra dhcp options."); + throw Exceptions.invalidParameterValueException("vm.deploy.dhcp.options.network.no.nic", Map.of("network", networkUuid)); } } } @@ -4265,8 +4358,7 @@ private void verifyExtraDhcpOptionsNetwork(Map> dhc public void checkNameForRFCCompliance(String name) { if (!NetUtils.verifyDomainNameLabel(name, true)) { - throw new InvalidParameterValueException("Invalid name. Vm name can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit"); + throw Exceptions.invalidParameterValueException("vm.deploy.hostname.invalid"); } } @@ -4282,7 +4374,7 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe _accountMgr.checkAccess(caller, null, true, owner); if (owner.getState() == Account.State.DISABLED) { - throw new PermissionDeniedException("The owner of vm to deploy is disabled: " + owner); + throw Exceptions.permissionDeniedException("vm.deploy.owner.disabled", Map.of("owner", owner)); } VMTemplateVO template = _templateDao.findByIdIncludingRemoved(tmplt.getId()); if (template != null) { @@ -4292,12 +4384,12 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe HypervisorType hypervisorType = null; if (template.getHypervisorType() == null || template.getHypervisorType() == HypervisorType.None) { if (hypervisor == null || hypervisor == HypervisorType.None) { - throw new InvalidParameterValueException("Hypervisor parameter is needed to deploy VM or the hypervisor parameter value passed is invalid"); + throw Exceptions.invalidParameterValueException("vm.deploy.hypervisor.missing.or.invalid"); } hypervisorType = hypervisor; } else { if (hypervisor != null && hypervisor != HypervisorType.None && hypervisor != template.getHypervisorType()) { - throw new InvalidParameterValueException("Hypervisor passed to the deployVm call, is different from the hypervisor type of the Template"); + throw Exceptions.invalidParameterValueException("vm.deploy.hypervisor.mismatch"); } hypervisorType = template.getHypervisorType(); } @@ -4308,8 +4400,7 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) { - throw new PermissionDeniedException( - String.format("Cannot perform this operation, Zone is currently disabled: %s", zone)); + throw Exceptions.permissionDeniedException("vm.deploy.zone.disabled", Map.of("zone", zone)); } // check if zone is dedicated @@ -4317,7 +4408,7 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe if (dedicatedZone != null) { DomainVO domain = _domainDao.findById(dedicatedZone.getDomainId()); if (domain == null) { - throw new CloudRuntimeException("Unable to find the domain " + zone.getDomainId() + " for the zone: " + zone); + throw Exceptions.cloudRuntimeException("vm.deploy.zone.domain.not.found", Map.of("zone", zone)); } // check that caller can operate with domain _configMgr.checkZoneAccess(caller, zone); @@ -4342,7 +4433,7 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe if (diskOfferingId == null) { DiskOfferingVO diskOffering = _diskOfferingDao.findById(rootDiskOfferingId); if (diskOffering.isComputeOnly() && !_itMgr.isBlankInstance(template)) { - throw new InvalidParameterValueException("Installing from ISO requires a disk offering to be specified for the root disk."); + throw Exceptions.invalidParameterValueException("vm.deploy.iso.disk.offering.required"); } } else { rootDiskOfferingId = diskOfferingId; @@ -4360,7 +4451,7 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe long volumesSize = configureCustomRootDiskSize(customParameters, template, hypervisorType, rootDiskOffering); if (rootDiskOffering.getEncrypt() && hypervisorType != HypervisorType.KVM) { - throw new InvalidParameterValueException("Root volume encryption is not supported for hypervisor type " + hypervisorType); + throw Exceptions.invalidParameterValueException("vm.deploy.root.encryption.hypervisor.not.supported", Map.of("hypervisorType", hypervisorType)); } kmsManager.checkKmsKeyForVolumeEncryption(owner, rootDiskKmsKeyId, zone.getId()); @@ -4468,7 +4559,7 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri for (Long securityGroupId : securityGroupIdList) { SecurityGroup sg = _securityGroupDao.findById(securityGroupId); if (sg == null) { - throw new InvalidParameterValueException("Unable to find security group by id " + securityGroupId); + throw Exceptions.invalidParameterValueException("vm.deploy.security.group.not.found"); } else { // verify permissions _accountMgr.checkAccess(caller, null, true, owner, sg); @@ -4483,20 +4574,20 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri if (dataDiskTemplate == null || (!dataDiskTemplate.getTemplateType().equals(TemplateType.DATADISK)) && (dataDiskTemplate.getState().equals(VirtualMachineTemplate.State.Active))) { - throw new InvalidParameterValueException("Invalid Template ID specified for Datadisk Template" + datadiskTemplateToDiskOffering.getKey()); + throw Exceptions.invalidParameterValueException("vm.deploy.datadisk.template.invalid"); } long dataDiskTemplateId = datadiskTemplateToDiskOffering.getKey(); if (!dataDiskTemplate.getParentTemplateId().equals(template.getId())) { - throw new InvalidParameterValueException(String.format("Invalid Datadisk Template. Specified Datadisk Template %s doesn't belong to Template %s", dataDiskTemplate, template)); + throw Exceptions.invalidParameterValueException("vm.deploy.datadisk.template.not.child", Map.of("dataDiskTemplate", dataDiskTemplate, "template", template)); } if (dataDiskOffering == null) { - throw new InvalidParameterValueException(String.format("Invalid disk offering %s specified for datadisk Template %s", datadiskTemplateToDiskOffering.getValue(), dataDiskTemplate)); + throw Exceptions.invalidParameterValueException("vm.deploy.datadisk.template.offering.invalid", Map.of("diskOffering", datadiskTemplateToDiskOffering.getValue(), "dataDiskTemplate", dataDiskTemplate)); } if (dataDiskOffering.isCustomized()) { - throw new InvalidParameterValueException(String.format("Invalid disk offering %s specified for datadisk Template %s. Custom Disk offerings are not supported for Datadisk Templates", dataDiskOffering, dataDiskTemplate)); + throw Exceptions.invalidParameterValueException("vm.deploy.datadisk.template.custom.offering.not.supported", Map.of("diskOffering", dataDiskOffering, "dataDiskTemplate", dataDiskTemplate)); } if (dataDiskOffering.getDiskSize() < dataDiskTemplate.getSize()) { - throw new InvalidParameterValueException(String.format("Invalid disk offering %s specified for datadisk Template %s. Disk offering size should be greater than or equal to the Template size", dataDiskOffering, dataDiskTemplate)); + throw Exceptions.invalidParameterValueException("vm.deploy.datadisk.template.offering.too.small", Map.of("diskOffering", dataDiskOffering, "dataDiskTemplate", dataDiskTemplate)); } _templateDao.loadDetails(dataDiskTemplate); } @@ -4507,10 +4598,9 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri for (Long affinityGroupId : affinityGroupIdList) { AffinityGroupVO ag = _affinityGroupDao.findById(affinityGroupId); if (ag == null) { - throw new InvalidParameterValueException("Unable to find affinity group " + ag); + throw Exceptions.invalidParameterValueException("vm.deploy.affinity.group.not.found"); } else if (!_affinityGroupService.isAffinityGroupProcessorAvailable(ag.getType())) { - throw new InvalidParameterValueException("Affinity group type is not supported for group: " + ag + " ,type: " + ag.getType() - + " , Please try again after removing the affinity group"); + throw Exceptions.invalidParameterValueException("vm.deploy.affinity.group.type.not.supported", Map.of("group", ag, "groupType", ag.getType())); } else { // verify permissions if (ag.getAclType() == ACLType.Domain) { @@ -4520,7 +4610,7 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri // make sure the owner of these entities is same if (caller.getId() == Account.ACCOUNT_ID_SYSTEM || _accountMgr.isRootAdmin(caller.getId())) { if (!_affinityGroupService.isAffinityGroupAvailableInDomain(ag.getId(), owner.getDomainId())) { - throw new PermissionDeniedException("Affinity Group " + ag + " does not belong to the VM's domain"); + throw Exceptions.permissionDeniedException("vm.deploy.affinity.group.domain.mismatch", Map.of("group", ag)); } } } else { @@ -4530,7 +4620,7 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri // make sure the owner of these entities is same if (caller.getId() == Account.ACCOUNT_ID_SYSTEM || _accountMgr.isRootAdmin(caller.getId())) { if (ag.getAccountId() != owner.getAccountId()) { - throw new PermissionDeniedException("Affinity Group " + ag + " does not belong to the VM's account"); + throw Exceptions.permissionDeniedException("vm.deploy.affinity.group.account.mismatch", Map.of("group", ag)); } } } @@ -4548,27 +4638,27 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri if (TemplateType.SYSTEM.equals(template.getTemplateType()) && !CKS_NODE.equals(vmType) && !SHAREDFSVM.equals(vmType) && !_itMgr.isBlankInstance(template)) { - throw new InvalidParameterValueException(String.format("Unable to use system template %s to deploy a user vm", template)); + throw Exceptions.invalidParameterValueException("vm.deploy.system.template.not.allowed", Map.of("template", template)); } if (volume != null) { if (zone.getId() != volume.getDataCenterId()) { - throw new InvalidParameterValueException(String.format("The volume's zone [%s] is not the same as the provided zone [%s]", volume.getDataCenterId(), zone.getId())); + throw Exceptions.invalidParameterValueException("vm.deploy.volume.zone.mismatch", Map.of("volumeZone", volume.getDataCenterId(), "zone", zone.getId())); } } else if (snapshot != null) { List snapshotsOnZone = snapshotDataFactory.getSnapshots(snapshot.getId(), zone.getId()); if (CollectionUtils.isEmpty(snapshotsOnZone)) { - throw new InvalidParameterValueException("The snapshot does not exist on zone " + zone.getId()); + throw Exceptions.invalidParameterValueException("vm.deploy.snapshot.zone.mismatch", Map.of("zone", zone)); } } else if (!_itMgr.isBlankInstanceDefaultTemplate(template)) { List listZoneTemplate = _templateZoneDao.listByZoneTemplate(zone.getId(), template.getId()); if (listZoneTemplate == null || listZoneTemplate.isEmpty()) { - throw new InvalidParameterValueException("The template " + template.getId() + " is not available for use"); + throw Exceptions.invalidParameterValueException("vm.deploy.template.not.available", Map.of("template", template)); } } if (isIso && !template.isBootable()) { - throw new InvalidParameterValueException(String.format("Installing from ISO requires an ISO that is bootable: %s", template)); + throw Exceptions.invalidParameterValueException("vm.deploy.iso.not.bootable", Map.of("iso", template)); } // Check templates permissions @@ -4584,7 +4674,7 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri if (!sshKeyPairs.isEmpty()) { List pairs = _sshKeyPairDao.findByNames(owner.getAccountId(), owner.getDomainId(), sshKeyPairs); if (pairs == null || pairs.size() != sshKeyPairs.size()) { - throw new InvalidParameterValueException("Not all specified keypairs exist"); + throw Exceptions.invalidParameterValueException("vm.deploy.keypairs.not.found"); } sshPublicKeys = pairs.stream().map(p -> p.getPublicKey()).collect(Collectors.joining("\n")); @@ -4599,7 +4689,7 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri for (NetworkVO network : networkList) { if ((network.getDataCenterId() != zone.getId())) { if (!network.isStrechedL2Network()) { - throw new InvalidParameterValueException(String.format("Network %s doesn't belong to zone %s", network, zone)); + throw Exceptions.invalidParameterValueException("vm.deploy.network.zone.mismatch", Map.of("network", network, "zone", zone)); } NetworkOffering ntwkOffering = _networkOfferingDao.findById(network.getNetworkOfferingId()); @@ -4607,8 +4697,7 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri String provider = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.Connectivity); if (!_networkModel.isProviderEnabledInPhysicalNetwork(physicalNetworkId, provider)) { - throw new InvalidParameterValueException("Network in which is VM getting deployed could not be" + - " streched to the zone, as we could not find a valid physical network"); + throw Exceptions.invalidParameterValueException("vm.deploy.network.provider.not.enabled.in.zone"); } } @@ -4640,15 +4729,15 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri profile.setDefaultNic(true); if (!_networkModel.areServicesSupportedInNetwork(network.getId(), new Service[]{Service.UserData})) { if ((userData != null) && (!userData.isEmpty())) { - throw new InvalidParameterValueException(String.format("Unable to deploy VM as UserData is provided while deploying the VM, but there is no support for %s service in the default network %s/%s.", Service.UserData.getName(), network.getName(), network.getUuid())); + throw Exceptions.invalidParameterValueException("vm.deploy.userdata.network.service.not.found", Map.of("network", network)); } if ((sshPublicKeys != null) && (!sshPublicKeys.isEmpty())) { - throw new InvalidParameterValueException(String.format("Unable to deploy VM as SSH keypair is provided while deploying the VM, but there is no support for %s service in the default network %s/%s", Service.UserData.getName(), network.getName(), network.getUuid())); + throw Exceptions.invalidParameterValueException("vm.deploy.sshkey.network.service.not.found", Map.of("network", network)); } if (template.isEnablePassword()) { - throw new InvalidParameterValueException(String.format("Unable to deploy VM as template %s is password enabled, but there is no support for %s service in the default network %s/%s", template, Service.UserData.getName(), network.getName(), network.getUuid())); + throw Exceptions.invalidParameterValueException("vm.deploy.template.password.network.service.not.found", Map.of("template", template, "network", network)); } } } @@ -4666,7 +4755,7 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri } if (CollectionUtils.isNotEmpty(securityGroupIdList) && !securityGroupEnabled) { - throw new InvalidParameterValueException("Unable to deploy vm with security groups as SecurityGroup service is not enabled for the vm's network"); + throw Exceptions.invalidParameterValueException("vm.deploy.securitygroup.service.not.enabled"); } // Verify network information - network default network has to be set; @@ -4678,10 +4767,10 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri if (_itMgr.isBlankInstance(template)) { logger.debug("Template is a dummy template for hypervisor {}, vm can be created without a default network", hypervisorType); } else { - throw new InvalidParameterValueException("At least 1 default network has to be specified for the vm"); + throw Exceptions.invalidParameterValueException("vm.deploy.default.network.missing"); } } else if (defaultNetworkNumber > 1) { - throw new InvalidParameterValueException("Only 1 default network per vm is supported"); + throw Exceptions.invalidParameterValueException("vm.deploy.default.network.multiple"); } long id = _vmDao.getNextInSequence(Long.class, "id"); @@ -4709,7 +4798,7 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri // In case of VMware since VM name must be unique within a DC, check if VM with the same hostname already exists in the zone. VMInstanceVO vmByHostName = _vmInstanceDao.findVMByHostNameInZone(hostName, zone.getId()); if (vmByHostName != null && vmByHostName.getState() != State.Expunging) { - throw new InvalidParameterValueException("There already exists a VM by the name: " + hostName + "."); + throw Exceptions.invalidParameterValueException("vm.deploy.hostname.exists", Map.of("hostname", hostName)); } } else { if (hostName == null) { @@ -4730,7 +4819,7 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri // Check if VM with instanceName already exists. VMInstanceVO vmObj = _vmInstanceDao.findVMByInstanceName(instanceName); if (vmObj != null && vmObj.getState() != State.Expunging) { - throw new InvalidParameterValueException("There already exists a VM by the display name supplied"); + throw Exceptions.invalidParameterValueException("vm.deploy.displayname.exists"); } checkIfHostNameUniqueInNtwkDomain(hostName, networkList); @@ -4767,22 +4856,22 @@ private void assignInstanceToGroup(String group, long id) { if (group != null) { boolean addToGroup = addInstanceToGroup(Long.valueOf(id), group); if (!addToGroup) { - throw new CloudRuntimeException("Unable to assign Vm to the group " + group); + throw Exceptions.cloudRuntimeException("vm.deploy.group.assign.failed", Map.of("group", group)); } } } catch (Exception ex) { - throw new CloudRuntimeException("Unable to assign Vm to the group " + group); + throw Exceptions.cloudRuntimeException("vm.deploy.group.assign.failed", Map.of("group", group != null ? group : ""), ex); } } private long verifyAndGetDiskSize(DiskOffering diskOffering, Long diskSize) { long size = 0l; if (diskOffering == null) { - throw new InvalidParameterValueException("Specified disk offering cannot be found"); + throw Exceptions.invalidParameterValueException("vm.deploy.diskoffering.not.found"); } if (diskOffering.isCustomized() && !diskOffering.isComputeOnly()) { if (diskSize == null) { - throw new InvalidParameterValueException("This disk offering requires a custom size specified"); + throw Exceptions.invalidParameterValueException("vm.deploy.disk.offering.custom.size.required"); } _volumeService.validateCustomDiskOfferingSizeRange(diskSize); size = diskSize * GiB_TO_BYTES; @@ -4813,7 +4902,7 @@ protected long configureCustomRootDiskSize(Map customParameters, if (customParameters.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { rootDiskSizeCustomParam = NumbersUtil.parseLong(customParameters.get(VmDetailConstants.ROOT_DISK_SIZE), -1); if (rootDiskSizeCustomParam <= 0) { - throw new InvalidParameterValueException("Root disk size should be a positive number."); + throw Exceptions.invalidParameterValueException("vm.deploy.root.disk.size.not.positive"); } } long rootDiskSizeInBytes = verifyAndGetDiskSize(rootDiskOffering, rootDiskSizeCustomParam); @@ -4827,7 +4916,7 @@ protected long configureCustomRootDiskSize(Map customParameters, if (customParameters.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { long rootDiskSize = NumbersUtil.parseLong(customParameters.get(VmDetailConstants.ROOT_DISK_SIZE), -1); if (rootDiskSize <= 0) { - throw new InvalidParameterValueException("Root disk size should be a positive number."); + throw Exceptions.invalidParameterValueException("vm.deploy.root.disk.size.not.positive"); } rootDiskSize = rootDiskSizeCustomParam * GiB_TO_BYTES; _volumeService.validateVolumeSizeInBytes(rootDiskSize); @@ -4848,7 +4937,7 @@ protected long configureCustomRootDiskSize(Map customParameters, */ protected void verifyIfHypervisorSupportsRootdiskSizeOverride(HypervisorType hypervisorType) { if (!hypervisorType.isFunctionalitySupported(Functionality.RootDiskSizeOverride)) { - throw new InvalidParameterValueException("Hypervisor " + hypervisorType + " does not support rootdisksize override"); + throw Exceptions.invalidParameterValueException("vm.deploy.root.disk.size.override.not.supported", Map.of("hypervisorType", hypervisorType)); } } @@ -4926,8 +5015,7 @@ private void checkIfHostNameUniqueInNtwkDomain(String hostName, List List hostNames = _vmInstanceDao.listDistinctHostNames(ntwkId); // * verify that there are no duplicates if (hostNames.contains(hostName)) { - throw new InvalidParameterValueException("The vm with hostName " + hostName + " already exists in the network domain: " + ntwkDomain.getKey() + "; network=" - + ((_networkModel.getNetwork(ntwkId) != null) ? _networkModel.getNetwork(ntwkId).getName() : "")); + throw Exceptions.invalidParameterValueException("vm.deploy.hostname.network.domain.exists", Map.of("hostname", hostName)); } } } @@ -4989,7 +5077,7 @@ private UserVmVO commitUserVm(final boolean isImport, final DataCenter zone, fin VMTemplateVO templateVO = _templateDao.findById(template.getId()); if (templateVO == null) { - InvalidParameterValueException ipve = new InvalidParameterValueException("Unable to look up template by id " + template.getId()); + InvalidParameterValueException ipve = Exceptions.invalidParameterValueException("vm.deploy.template.not.found.by.id", Map.of("id", template.getId())); ipve.add(VirtualMachine.class, vm.getUuid()); throw ipve; } @@ -5257,14 +5345,14 @@ public void validateRootDiskResize(final HypervisorType hypervisorType, Long roo if ((rootDiskSize << 30) < templateVO.getSize()) { String error = String.format("Unsupported: rootdisksize override (%s GB) is smaller than template size %s", rootDiskSize, toHumanReadableSize(templateVO.getSize())); logger.error(error); - throw new InvalidParameterValueException(error); + throw Exceptions.invalidParameterValueException("vm.deploy.root.disk.size.smaller.than.template", Map.of("rootDiskSize", rootDiskSize)); } else if ((rootDiskSize << 30) > templateVO.getSize()) { if (hypervisorType == HypervisorType.VMware && (vm.getDetails() == null || vm.getDetails().get(VmDetailConstants.ROOT_DISK_CONTROLLER) == null)) { logger.warn("If Root disk controller parameter is not overridden, then Root disk resize may fail because current Root disk controller value is NULL."); } else if (hypervisorType == HypervisorType.VMware && vm.getDetails().get(VmDetailConstants.ROOT_DISK_CONTROLLER).toLowerCase().contains("ide") && !isIso) { String error = String.format("Found unsupported root disk controller [%s].", vm.getDetails().get(VmDetailConstants.ROOT_DISK_CONTROLLER)); logger.error(error); - throw new InvalidParameterValueException(error); + throw Exceptions.invalidParameterValueException("vm.deploy.root.disk.controller.invalid", Map.of("diskController", vm.getDetails().get(VmDetailConstants.ROOT_DISK_CONTROLLER))); } else { logger.debug("Rootdisksize override validation successful. Template root disk size " + toHumanReadableSize(templateVO.getSize()) + " Root disk size specified " + rootDiskSize + " GB"); } @@ -5769,7 +5857,7 @@ 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."); + throw Exceptions.permissionDeniedException("vm.stop.force.permission.denied"); } } @@ -5782,12 +5870,12 @@ public UserVm stopVirtualMachine(long vmId, boolean forced) throws ConcurrentOpe // if account is removed, return error if (caller != null && caller.getRemoved() != null) { - throw new PermissionDeniedException("The account " + caller.getUuid() + " is removed"); + throw Exceptions.permissionDeniedException("vm.stop.account.removed"); } UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + throw Exceptions.invalidParameterValueException("vm.stop.vm.not.found"); } if (forced) { @@ -5812,9 +5900,9 @@ public UserVm stopVirtualMachine(long vmId, boolean forced) throws ConcurrentOpe return null; } } catch (ResourceUnavailableException e) { - throw new CloudRuntimeException("Unable to contact the agent to stop the virtual machine " + vm, e); + throw Exceptions.cloudRuntimeException("vm.stop.agent.no.contact", Map.of("instance", vm), e); } catch (CloudException e) { - throw new CloudRuntimeException("Unable to contact the agent to stop the virtual machine " + vm, e); + throw Exceptions.cloudRuntimeException("vm.stop.agent.no.contact", Map.of("instance", vm), e); } } @@ -5905,7 +5993,7 @@ private Pair> startVirtualMac } logger.info(errorMsg); if (!AllowDeployVmIfGivenHostFails.value()) { - throw new InvalidParameterValueException(errorMsg); + throw Exceptions.invalidParameterValueException("vm.start.host.capacity.insufficient", Map.of("host", destinationHost)); } } else { plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); @@ -5933,7 +6021,7 @@ private Pair> startVirtualMac _vmDao.loadDetails(vm); String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template); if (!validPassword(password)) { - throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); + throw Exceptions.invalidParameterValueException("vm.start.password.not.provided"); } // Check if an SSH key pair was selected for the instance and if so // use it to encrypt & save the vm password @@ -5943,7 +6031,7 @@ private Pair> startVirtualMac if (additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) { if (!HypervisorType.VMware.equals(vm.getHypervisorType())) { - throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType()); + throw Exceptions.invalidParameterValueException("vm.start.setup.menu.hypervisor.not.applicable", Map.of("hypervisorType", vm.getHypervisorType())); } Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup); if (logger.isTraceEnabled()) { @@ -5960,7 +6048,7 @@ private Pair> startVirtualMac // the service offering planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse); if (planner == null) { - throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse); + throw Exceptions.invalidParameterValueException("vm.start.planner.not.found", Map.of("plannerName", deploymentPlannerToUse)); } } vmEntity.setParamsToEntity(additionalParams); @@ -5993,17 +6081,16 @@ public Pair> startVirtualMach // if account is removed, return error if (callerAccount == null || callerAccount.getRemoved() != null) { - throw new InvalidParameterValueException(String.format("The account %s is removed", callerAccount)); + throw Exceptions.invalidParameterValueException("vm.start.account.removed", Map.of("account", callerAccount)); } UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + throw Exceptions.invalidParameterValueException("vm.start.vm.not.found"); } if (vm.getState() == State.Running) { - throw new InvalidParameterValueException(String.format("The virtual machine %s (%s) is already running", - vm.getUuid(), vm.getDisplayNameOrHostName())); + throw Exceptions.invalidParameterValueException("vm.start.vm.already.running", Map.of("instance", vm)); } _accountMgr.checkAccess(callerAccount, null, true, vm); @@ -6011,11 +6098,11 @@ public Pair> startVirtualMach Account owner = _accountDao.findById(vm.getAccountId()); if (owner == null) { - throw new InvalidParameterValueException("The owner of " + vm + " does not exist: " + vm.getAccountId()); + throw Exceptions.invalidParameterValueException("vm.start.owner.not.found", Map.of("instance", vm)); } if (owner.getState() == Account.State.DISABLED) { - throw new PermissionDeniedException(String.format("The owner of %s is disabled: %s", vm, owner)); + throw Exceptions.permissionDeniedException("vm.start.owner.disabled", Map.of("instance", vm)); } boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId()); @@ -6089,12 +6176,11 @@ private Pod getDestinationPod(Long podId, boolean isRootAdmin) { Pod destinationPod = null; if (podId != null) { if (!isRootAdmin) { - throw new PermissionDeniedException( - "Parameter " + ApiConstants.POD_ID + " can only be specified by a Root Admin, permission denied"); + throw Exceptions.permissionDeniedException("vm.start.pod.root.admin.only"); } destinationPod = _podDao.findById(podId); if (destinationPod == null) { - throw new InvalidParameterValueException("Unable to find the pod to deploy the VM, pod id=" + podId); + throw Exceptions.invalidParameterValueException("vm.start.pod.not.found"); } } return destinationPod; @@ -6104,12 +6190,11 @@ private Cluster getDestinationCluster(Long clusterId, boolean isRootAdmin) { Cluster destinationCluster = null; if (clusterId != null) { if (!isRootAdmin) { - throw new PermissionDeniedException( - "Parameter " + ApiConstants.CLUSTER_ID + " can only be specified by a Root Admin, permission denied"); + throw Exceptions.permissionDeniedException("vm.start.cluster.root.admin.only"); } destinationCluster = _clusterDao.findById(clusterId); if (destinationCluster == null) { - throw new InvalidParameterValueException("Unable to find the cluster to deploy the VM, cluster id=" + clusterId); + throw Exceptions.invalidParameterValueException("vm.start.cluster.not.found"); } } return destinationCluster; @@ -6119,14 +6204,13 @@ private HostVO getDestinationHost(Long hostId, boolean isRootAdmin, boolean isEx HostVO destinationHost = null; if (hostId != null) { if (isExplicitHost && !isRootAdmin) { - throw new PermissionDeniedException( - "Parameter " + ApiConstants.HOST_ID + " can only be specified by a Root Admin, permission denied"); + throw Exceptions.permissionDeniedException("vm.start.host.root.admin.only"); } destinationHost = _hostDao.findById(hostId); if (destinationHost == null) { - throw new InvalidParameterValueException("Unable to find the host to deploy the VM, host id=" + hostId); + throw Exceptions.invalidParameterValueException("vm.start.host.not.found"); } else if (destinationHost.getResourceState() != ResourceState.Enabled || destinationHost.getStatus() != Status.Up ) { - throw new InvalidParameterValueException("Unable to deploy the VM as the host: " + destinationHost.getName() + " is not in the right state"); + throw Exceptions.invalidParameterValueException("vm.start.host.not.right.state", Map.of("host", destinationHost)); } } return destinationHost; @@ -6137,8 +6221,7 @@ public UserVm destroyVm(long vmId, boolean expunge) throws ResourceUnavailableEx // Verify input parameters UserVmVO vm = _vmDao.findById(vmId); if (vm == null || vm.getRemoved() != null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find a virtual machine with specified vmId"); - throw ex; + throw Exceptions.invalidParameterValueException("vm.destroy.vm.not.found"); } if (vm.getState() == State.Destroyed || vm.getState() == State.Expunging) { @@ -6165,9 +6248,7 @@ public UserVm destroyVm(long vmId, boolean expunge) throws ResourceUnavailableEx VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); status = vmEntity.destroy(expunge); } catch (CloudException e) { - CloudRuntimeException ex = new CloudRuntimeException("Unable to destroy with specified vmId", e); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; + throw Exceptions.cloudRuntimeException("vm.destroy.failed", Map.of(), e); } if (status) { @@ -6188,12 +6269,10 @@ public UserVm destroyVm(long vmId, boolean expunge) throws ResourceUnavailableEx } return _vmDao.findById(vmId); } else { - CloudRuntimeException ex = new CloudRuntimeException("Failed to destroy vm with specified vmId"); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; + throw Exceptions.cloudRuntimeException("vm.destroy.failed"); } } catch (Exception e) { - throw new CloudRuntimeException("Failed to destroy vm with specified vmId", e); + throw Exceptions.cloudRuntimeException("vm.destroy.failed", Map.of(), e); } } @@ -6346,12 +6425,10 @@ public UserVm expungeVm(long vmId) throws ResourceUnavailableException, Concurre // Verify input parameters UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find a virtual machine with specified vmId"); - ex.addProxyObject(String.valueOf(vmId), "vmId"); - throw ex; + throw Exceptions.invalidParameterValueException("vm.expunge.vm.not.found"); } if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) { - throw new InvalidParameterValueException("Operation not supported on Shared FileSystem Instance"); + throw Exceptions.invalidParameterValueException("vm.expunge.sharedfs.not.supported"); } if (vm.getRemoved() != null) { @@ -6360,14 +6437,12 @@ public UserVm expungeVm(long vmId) throws ResourceUnavailableException, Concurre } if (!(vm.getState() == State.Destroyed || vm.getState() == State.Expunging || vm.getState() == State.Error)) { - CloudRuntimeException ex = new CloudRuntimeException("Please destroy vm with specified vmId before expunge"); - ex.addProxyObject(String.valueOf(vmId), "vmId"); - throw ex; + throw Exceptions.cloudRuntimeException("vm.expunge.destroy.first"); } // When trying to expunge, permission is denied when the caller is not an admin and the AllowUserExpungeRecoverVm is false for the caller. if (!_accountMgr.isAdmin(callerId) && !AllowUserExpungeRecoverVm.valueIn(callerId)) { - throw new PermissionDeniedException("Expunging a vm can only be done by an Admin. Or when the allow.user.expunge.recover.vm key is set."); + throw Exceptions.permissionDeniedException("vm.expunge.admin.only"); } // check if vm belongs to AutoScale vm group in Disabled state @@ -6381,9 +6456,7 @@ public UserVm expungeVm(long vmId) throws ResourceUnavailableException, Concurre if (status) { return _vmDao.findByIdIncludingRemoved(vmId); } else { - CloudRuntimeException ex = new CloudRuntimeException("Failed to expunge vm with specified vmId"); - ex.addProxyObject(String.valueOf(vmId), "vmId"); - throw ex; + throw Exceptions.cloudRuntimeException("vm.expunge.failed"); } } @@ -6392,9 +6465,7 @@ public UserVm expungeVm(long vmId) throws ResourceUnavailableException, Concurre public HypervisorType getHypervisorTypeOfUserVM(long vmId) { UserVmVO userVm = _vmDao.findById(vmId); if (userVm == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("unable to find a virtual machine with specified id"); - ex.addProxyObject(String.valueOf(vmId), "vmId"); - throw ex; + throw Exceptions.invalidParameterValueException("vm.gethypervisortype.vm.not.found"); } return userVm.getHypervisorType(); @@ -6407,14 +6478,14 @@ public String finalizeUserData(String userData, Long userDataId, VirtualMachineT } if (userDataId != null && StringUtils.isNotEmpty(userData)) { - throw new InvalidParameterValueException("Both userdata and userdata ID inputs are not allowed, please provide only one"); + throw Exceptions.invalidParameterValueException("vm.deploy.userdata.both.inputs"); } if (template != null && template.getUserDataId() != null) { switch (template.getUserDataOverridePolicy()) { case DENYOVERRIDE: if (StringUtils.isNotEmpty(userData) || userDataId != null) { String msg = String.format("UserData input is not allowed here since template %s is configured to deny any userdata", template.getName()); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.deploy.userdata.override.denied", Map.of("template", template)); } case ALLOWOVERRIDE: if (userDataId != null) { @@ -6426,7 +6497,7 @@ public String finalizeUserData(String userData, Long userDataId, VirtualMachineT UserData templateUserDataVO = userDataDao.findById(template.getUserDataId()); if (templateUserDataVO == null) { String msg = String.format("UserData linked to the template %s is not found", template.getName()); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.deploy.userdata.template.linked.not.found", Map.of("template", template)); } return templateUserDataVO.getUserData(); } @@ -6434,7 +6505,7 @@ public String finalizeUserData(String userData, Long userDataId, VirtualMachineT UserData templateUserDataVO = userDataDao.findById(template.getUserDataId()); if (templateUserDataVO == null) { String msg = String.format("UserData linked to the template %s is not found", template.getName()); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.deploy.userdata.template.linked.not.found", Map.of("template", template)); } if (userDataId != null) { UserData apiUserDataVO = userDataDao.findById(userDataId); @@ -6446,7 +6517,7 @@ public String finalizeUserData(String userData, Long userDataId, VirtualMachineT } default: String msg = String.format("This userdataPolicy %s is not supported for use with this feature", template.getUserDataOverridePolicy().toString()); - throw new CloudRuntimeException(msg); } + throw Exceptions.cloudRuntimeException("vm.deploy.userdata.policy.not.supported", Map.of("policy", template.getUserDataOverridePolicy())); } } else { if (userDataId != null) { UserData apiUserDataVO = userDataDao.findById(userDataId); @@ -6459,19 +6530,20 @@ public String finalizeUserData(String userData, Long userDataId, VirtualMachineT } private void verifyServiceOffering(BaseDeployVMCmd cmd, ServiceOffering serviceOffering) { + CallContext.current().putErrorContextParameter("serviceOffering", serviceOffering); if (ServiceOffering.State.Inactive.equals(serviceOffering.getState())) { - throw new InvalidParameterValueException(String.format("Service offering is inactive: [%s].", serviceOffering.getUuid())); + throw Exceptions.invalidParameterValueException("vm.deploy.serviceoffering.inactive", Collections.emptyMap()); } 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 Exceptions.invalidParameterValueException("vm.deploy.serviceoffering.override.not.allowed", Collections.emptyMap()); } 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 Exceptions.invalidParameterValueException("vm.deploy.serviceoffering.fixed.parameters.not.allowed", Map.of("cpuNumberKey", VmDetailConstants.CPU_NUMBER, "cpuSpeedKey", VmDetailConstants.CPU_NUMBER, "memoryKey", VmDetailConstants.CPU_NUMBER)); } } } @@ -6483,23 +6555,23 @@ private void verifyTemplate(BaseDeployVMCmd cmd, VirtualMachineTemplate template vnfTemplateManager.validateVnfApplianceNics(template, cmd.getNetworkIds(), cmd.getVmNetworkMap()); } } else if (cmd instanceof DeployVnfApplianceCmd) { - throw new InvalidParameterValueException("Can't deploy VNF appliance from a non-VNF template"); + throw Exceptions.invalidParameterValueException("vm.deploy.template.not.vnf"); } ServiceOfferingJoinVO svcOffering = serviceOfferingJoinDao.findById(serviceOfferingId); if (template.isDeployAsIs()) { if (svcOffering != null && svcOffering.getRootDiskSize() != null && svcOffering.getRootDiskSize() > 0) { - throw new InvalidParameterValueException("Failed to deploy Virtual Machine as a service offering with root disk size specified cannot be used with a deploy as-is template"); + throw Exceptions.invalidParameterValueException("vm.deploy.template.deploy.as.is.root.disk.size.not.allowed"); } if (cmd.getDetails().get("rootdisksize") != null) { - throw new InvalidParameterValueException("Overriding root disk size isn't supported for VMs deployed from deploy as-is Templates"); + throw Exceptions.invalidParameterValueException("vm.deploy.template.deploy.as.is.override.disk.size.not.supported"); } // Bootmode and boottype are not supported on VMWare dpeloy-as-is templates (since 4.15) if ((cmd.getBootMode() != null || cmd.getBootType() != null)) { - throw new InvalidParameterValueException("Boot type and boot mode are not supported on VMware for Templates registered as deploy-as-is, as we honour what is defined in the template."); + throw Exceptions.invalidParameterValueException("vm.deploy.template.deploy.as.is.boot.not.supported"); } } } @@ -6516,18 +6588,18 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId); if (zone == null) { - throw new InvalidParameterValueException("Unable to find zone by id=" + zoneId); + throw Exceptions.invalidParameterValueException("vm.deploy.zone.not.found"); } Long serviceOfferingId = cmd.getServiceOfferingId(); if (serviceOfferingId == null) { - throw new InvalidParameterValueException("Unable to execute API command deployvirtualmachine due to missing parameter serviceofferingid"); + throw Exceptions.invalidParameterValueException("vm.deploy.serviceoffering.not.specified"); } Long overrideDiskOfferingId = cmd.getOverrideDiskOfferingId(); ServiceOffering serviceOffering = _entityMgr.findById(ServiceOffering.class, serviceOfferingId); if (serviceOffering == null) { - throw new InvalidParameterValueException("Unable to find service offering: " + serviceOffering.getId()); + throw Exceptions.invalidParameterValueException("vm.deploy.serviceoffering.not.found"); } verifyServiceOffering(cmd, serviceOffering); @@ -6541,35 +6613,39 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE if (cmd.getVolumeId() != null) { volume = getVolume(cmd.getVolumeId(), templateId, false); if (volume == null) { - throw new InvalidParameterValueException("Could not find volume with id=" + cmd.getVolumeId()); + throw Exceptions.invalidParameterValueException("vm.deploy.volume.not.found"); } _accountMgr.checkAccess(caller, null, true, volume); templateId = volume.getTemplateId(); } else if (cmd.getSnapshotId() != null) { snapshot = _snapshotDao.findById(cmd.getSnapshotId()); if (snapshot == null) { - throw new InvalidParameterValueException("Could not find snapshot with id=" + cmd.getSnapshotId()); + throw Exceptions.invalidParameterValueException("vm.deploy.snapshot.not.found"); } _accountMgr.checkAccess(caller, null, true, snapshot); VolumeInfo volumeOfSnapshot = getVolume(snapshot.getVolumeId(), templateId, true); if (volumeOfSnapshot != null) { templateId = volumeOfSnapshot.getTemplateId(); } else if (templateId == null) { - throw new InvalidParameterValueException( - "Could not determine template from snapshot id=" + cmd.getSnapshotId() + - "; the source volume no longer exists. Please specify a templateId."); + throw Exceptions.invalidParameterValueException("vm.deploy.snapshot.template.not.determinable"); } } VirtualMachineTemplate template = null; if (volume != null || snapshot != null) { template = _entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, templateId); + if (template == null) { + throw Exceptions.invalidParameterValueException("vm.deploy.template.associated.not.usable"); + } } else { template = _entityMgr.findById(VirtualMachineTemplate.class, templateId); + if (template == null) { + throw Exceptions.invalidParameterValueException("vm.deploy.template.not.found"); + } } if (cmd.isVolumeOrSnapshotProvided() && (!(HypervisorType.KVM.equals(template.getHypervisorType()) || HypervisorType.KVM.equals(cmd.getHypervisor())))) { - throw new InvalidParameterValueException("Deploying a virtual machine with existing volume/snapshot is supported only from KVM hypervisors"); + throw Exceptions.invalidParameterValueException("vm.deploy.hypervisor.volume.snapshot.not.supported"); } boolean blankInstance = cmd.isBlankInstance(); if (blankInstance) { @@ -6583,7 +6659,7 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE } // Make sure a valid template ID was specified if (template == null) { - throw new InvalidParameterValueException("Unable to use template " + templateId); + throw Exceptions.invalidParameterValueException("Unable to use template " + templateId); } verifyTemplate(cmd, template, serviceOfferingId); @@ -6592,25 +6668,25 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE if (diskOfferingId != null) { diskOffering = _entityMgr.findById(DiskOffering.class, diskOfferingId); if (diskOffering == null) { - throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); + throw Exceptions.invalidParameterValueException("vm.deploy.diskoffering.not.found"); } if (diskOffering.isComputeOnly()) { - throw new InvalidParameterValueException(String.format("The disk offering %s provided is directly mapped to a service offering, please provide an individual disk offering", diskOffering)); + throw Exceptions.invalidParameterValueException("vm.deploy.diskoffering.compute.only", Map.of("diskOffering", diskOffering)); } } List dataDiskInfoList = cmd.getDataDiskInfoList(); if (dataDiskInfoList != null && diskOfferingId != null) { - new InvalidParameterValueException("Cannot specify both disk offering id and data disk offering details"); + throw Exceptions.invalidParameterValueException("vm.deploy.diskoffering.with.diskoffering.details", Collections.emptyMap()); } if (!zone.isLocalStorageEnabled()) { DiskOffering diskOfferingMappedInServiceOffering = _entityMgr.findById(DiskOffering.class, serviceOffering.getDiskOfferingId()); if (diskOfferingMappedInServiceOffering.isUseLocalStorage()) { - throw new InvalidParameterValueException("Zone is not configured to use local storage but disk offering " + diskOfferingMappedInServiceOffering.getName() + " mapped in service offering uses it"); + throw Exceptions.invalidParameterValueException("vm.deploy.serviceoffering.local.storage.zone.unsupported", Map.of("serviceOffering", serviceOffering)); } if (diskOffering != null && diskOffering.isUseLocalStorage()) { - throw new InvalidParameterValueException("Zone is not configured to use local storage but disk offering " + diskOffering.getName() + " uses it"); + throw Exceptions.invalidParameterValueException("vm.deploy.diskoffering.local.storage.zone.unsupported", Map.of("diskOffering", diskOffering)); } } @@ -6639,7 +6715,7 @@ private UserVm createVirtualMachine(BaseDeployVMCmd cmd, DataCenter zone, Accoun List sshKeyPairNames = new ArrayList<>(); if (cmd instanceof CreateVMFromBackupCmd) { if (cmd.getUserData() != null) { - throw new InvalidParameterValueException("User data not supported for instance created from backup"); + throw Exceptions.invalidParameterValueException("vm.deploy.backup.userdata.not.supported"); } } else { userData = cmd.getUserData(); @@ -6678,7 +6754,7 @@ private UserVm createVirtualMachine(BaseDeployVMCmd cmd, DataCenter zone, Accoun final String instanceType = cmd.getInstanceType(); if (zone.getNetworkType() == NetworkType.Basic) { if (networkIds != null) { - throw new InvalidParameterValueException("Can't specify network Ids in Basic zone"); + throw Exceptions.invalidParameterValueException("vm.deploy.basic.zone.network.ids.not.allowed"); } else { vm = createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(cmd, zone, template, owner), owner, name, displayName, diskOfferingId, size , dataDiskInfoList, group , hypervisor, cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, ipToNetworkMap, addrs, displayVm , keyboard , cmd.getAffinityGroupIdList(), @@ -6695,7 +6771,7 @@ private UserVm createVirtualMachine(BaseDeployVMCmd cmd, DataCenter zone, Accoun } else { if (cmd.getSecurityGroupIdList() != null && !cmd.getSecurityGroupIdList().isEmpty()) { - throw new InvalidParameterValueException("Can't create vm with security groups; security group feature is not enabled per zone"); + throw Exceptions.invalidParameterValueException("vm.deploy.securitygroup.zone.not.enabled"); } vm = createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, name, displayName, diskOfferingId, size, dataDiskInfoList, group, hypervisor, cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, ipToNetworkMap, addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(), @@ -6722,7 +6798,7 @@ private UserVm createVirtualMachine(BaseDeployVMCmd cmd, DataCenter zone, Accoun logger.info("Adding extra configuration to user vm: {}", vm); addExtraConfig(vm, extraConfig); } else { - throw new InvalidParameterValueException("attempted setting extraconfig but enable.additional.vm.configuration is disabled"); + throw Exceptions.invalidParameterValueException("vm.update.extraconfig.disabled"); } } @@ -6758,11 +6834,11 @@ protected void validateLeaseProperties(Integer leaseDuration, VMLeaseManager.Exp // any one of them have value // validate leaseduration if (leaseDuration == null || leaseDuration < 1 || leaseDuration > VMLeaseManager.MAX_LEASE_DURATION_DAYS) { - throw new InvalidParameterValueException("Invalid leaseduration: must be a natural number (>=1) or -1, max supported value is 36500"); + throw Exceptions.invalidParameterValueException("vm.deploy.lease.duration.invalid"); } if (leaseExpiryAction == null) { - throw new InvalidParameterValueException("Provide values for both: leaseduration and leaseexpiryaction"); + throw Exceptions.invalidParameterValueException("vm.deploy.lease.params.both.required"); } } @@ -6803,13 +6879,13 @@ protected void applyLeaseOnUpdateInstance(UserVm instance, Integer leaseDuration if (StringUtils.isEmpty(leaseExpiryDate)) { String errorMsg = "Lease can't be applied on instance with id: " + instanceUuid + ", it doesn't have lease associated during deployment"; logger.debug(errorMsg); - throw new CloudRuntimeException(errorMsg); + throw Exceptions.cloudRuntimeException("vm.deploy.lease.no.active.lease", Map.of("instance", instanceUuid)); } if (!VMLeaseManager.LeaseActionExecution.PENDING.name().equals(leaseExecution)) { String errorMsg = "Lease can't be applied on instance with id: " + instanceUuid + ", it doesn't have active lease"; logger.debug(errorMsg); - throw new CloudRuntimeException(errorMsg); + throw Exceptions.cloudRuntimeException("vm.deploy.lease.not.pending", Map.of("instance", instanceUuid)); } // proceed if lease is yet to expire @@ -6824,7 +6900,7 @@ protected void applyLeaseOnUpdateInstance(UserVm instance, Integer leaseDuration } if (leaseExpiryTimeDiff < 0) { logger.debug("Lease has expired for instance with id: {}, can't modify lease information", instanceUuid); - throw new CloudRuntimeException("Lease is not allowed to be redefined on expired leased instance"); + throw Exceptions.cloudRuntimeException("vm.deploy.lease.expired.redefinition"); } if (leaseDuration < 1) { @@ -6860,11 +6936,11 @@ private VolumeInfo getVolume(long id, Long templateId, boolean isSnapshot) { if (!isSnapshot && (volume.getDataStore() == null || !ScopeType.ZONE.equals(volume.getDataStore().getScope().getScopeType()))) { - throw new InvalidParameterValueException("Deployment of virtual machine is supported only for Zone-wide storage pools"); + throw Exceptions.invalidParameterValueException("vm.deploy.volume.storage.pool.zone.required"); } checkIfVolumeTemplateIsTheSameAsTheProvided(volume, templateId); if (volume.getInstanceId() != null && !isSnapshot) { - throw new InvalidParameterValueException(String.format("The volume %s is already attached to a VM %s", volume, volume.getInstanceId())); + throw Exceptions.invalidParameterValueException("vm.deploy.volume.already.attached", Map.of("volume", volume)); } } return volume; @@ -6873,10 +6949,10 @@ private VolumeInfo getVolume(long id, Long templateId, boolean isSnapshot) { private void checkIfVolumeTemplateIsTheSameAsTheProvided(VolumeInfo volume, Long templateId) { if (volume.getTemplateId() != null) { if (templateId != null && !volume.getTemplateId().equals(templateId)) { - throw new InvalidParameterValueException(String.format("The volume's template %s is not the same as the provided one %s", volume.getTemplateId(), templateId)); + throw Exceptions.invalidParameterValueException("vm.deploy.volume.template.mismatch"); } } else { - throw new InvalidParameterValueException("The provided volume/snapshot doesn't have a template to deploy a VM"); + throw Exceptions.invalidParameterValueException("vm.deploy.volume.snapshot.no.template"); } } @@ -6896,11 +6972,11 @@ protected void persistExtraConfigVmware(String decodedUrl, UserVm vm) { if (validXenOrVmwareConfiguration && paramArray.length == 2) { vmInstanceDetailsDao.addDetail(vm.getId(), paramArray[0].trim(), paramArray[1].trim(), true); } else { - throw new CloudRuntimeException("Extra config " + cfg + " is not on the list of allowed keys for VMware hypervisor hosts."); + throw Exceptions.cloudRuntimeException("vm.extraconfig.vmware.key.not.allowed", Map.of("key", cfg)); } } } else { - throw new CloudRuntimeException("The passed extra config string " + decodedUrl + "contains an invalid key/value pair pattern"); + throw Exceptions.cloudRuntimeException("vm.extraconfig.vmware.invalid.format"); } } @@ -6926,12 +7002,12 @@ protected void persistExtraConfigXenServer(String decodedUrl, UserVm vm) { vmInstanceDetailsDao.addDetail(vm.getId(), extraConfigKey + String.valueOf(i), cfg, true); i++; } else { - throw new CloudRuntimeException("Extra config " + cfg + " is not on the list of allowed keys for XenServer hypervisor hosts."); + throw Exceptions.cloudRuntimeException("vm.extraconfig.xenserver.key.not.allowed", Map.of("key", cfg)); } } } else { String msg = String.format("The passed extra config string '%s' contains an invalid key/value pair pattern", decodedUrl); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.extraconfig.xenserver.invalid.format"); } } @@ -6979,7 +7055,7 @@ protected boolean isValidXenOrVmwareConfiguration(String cfg, String[] allowedKe } } else { String msg = String.format("An incorrect configuration %s has been passed", cfg); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.extraconfig.config.incorrect", Map.of("config", cfg)); } return false; } @@ -7017,7 +7093,7 @@ protected void persistExtraConfigKvm(String decodedUrl, UserVm vm) { @Override public void validateExtraConfig(long accountId, HypervisorType hypervisorType, String extraConfig) { if (!EnableAdditionalVmConfig.valueIn(accountId)) { - throw new CloudRuntimeException("Additional VM configuration is not enabled for this account"); + throw Exceptions.cloudRuntimeException("vm.extraconfig.account.not.enabled"); } if (HypervisorType.KVM.equals(hypervisorType)) { validateKvmExtraConfig(extraConfig, accountId); @@ -7051,11 +7127,11 @@ protected void validateKvmExtraConfig(String decodedUrl, long accountId) { } } if (!isValidConfig) { - throw new CloudRuntimeException(String.format("Extra config '%s' is not on the list of allowed keys for KVM hypervisor hosts", currentConfig)); + throw Exceptions.cloudRuntimeException("vm.extraconfig.kvm.key.not.allowed", Map.of("key", currentConfig)); } } } catch (ParserConfigurationException | IOException | SAXException e) { - throw new CloudRuntimeException("Failed to parse additional XML configuration: " + e.getMessage()); + throw Exceptions.cloudRuntimeException("vm.extraconfig.kvm.xml.parse.failed"); } } } @@ -7076,7 +7152,7 @@ protected void addExtraConfig(UserVm vm, String extraConfig) { persistExtraConfigVmware(decodedUrl, vm); } else { String msg = String.format("This hypervisor %s is not supported for use with this feature", hypervisorType); - throw new CloudRuntimeException(msg); + throw Exceptions.cloudRuntimeException("vm.extraconfig.hypervisor.not.supported", Map.of("hypervisorType", hypervisorType)); } } @@ -7090,14 +7166,14 @@ protected String decodeExtraConfig(String encodeString) { try { decodedUrl = URLDecoder.decode(encodeString, "UTF-8"); } catch (UnsupportedEncodingException e) { - throw new CloudRuntimeException("Failed to provided decode URL string: " + e.getMessage()); + throw Exceptions.cloudRuntimeException("vm.extraconfig.url.decode.failed"); } return decodedUrl; } protected List getSecurityGroupIdList(SecurityGroupAction cmd) { if (cmd.getSecurityGroupNameList() != null && cmd.getSecurityGroupIdList() != null) { - throw new InvalidParameterValueException("securitygroupids parameter is mutually exclusive with securitygroupnames parameter"); + throw Exceptions.invalidParameterValueException("vm.deploy.security.group.id.and.name.exclusive"); } //transform group names to ids here @@ -7106,7 +7182,7 @@ protected List getSecurityGroupIdList(SecurityGroupAction cmd) { for (String groupName : cmd.getSecurityGroupNameList()) { SecurityGroup sg = _securityGroupMgr.getSecurityGroup(groupName, cmd.getEntityOwnerId()); if (sg == null) { - throw new InvalidParameterValueException("Unable to find group by name " + groupName); + throw Exceptions.invalidParameterValueException("vm.deploy.security.group.name.not.found", Map.of("groupName", groupName)); } else { securityGroupIds.add(sg.getId()); } @@ -7147,12 +7223,12 @@ private void verifyDetails(Map details) { verifyMinAndMaxIops(minIops, maxIops); if (details.containsKey("extraconfig")) { - throw new InvalidParameterValueException("'extraconfig' should not be included in details as key"); + throw Exceptions.invalidParameterValueException("vm.update.extraconfig.in.details"); } for (String detailName : details.keySet()) { if (isExtraConfig(detailName)) { - throw new InvalidParameterValueException("detail name should not start with extraconfig"); + throw Exceptions.invalidParameterValueException("vm.deploy.detail.extraconfig.prefix.not.allowed"); } } } @@ -7160,7 +7236,7 @@ private void verifyDetails(Map details) { private void verifyMinAndMaxIops(String minIops, String maxIops) { if ((minIops != null && maxIops == null) || (minIops == null && maxIops != null)) { - throw new InvalidParameterValueException("Either 'Min IOPS' and 'Max IOPS' must both be specified or neither be specified."); + throw Exceptions.invalidParameterValueException("vm.deploy.iops.both.or.neither"); } long lMinIops; @@ -7174,7 +7250,7 @@ private void verifyMinAndMaxIops(String minIops, String maxIops) { } } catch (NumberFormatException ex) { - throw new InvalidParameterValueException("'Min IOPS' must be a whole number."); + throw Exceptions.invalidParameterValueException("vm.deploy.iops.min.not.integer"); } long lMaxIops; @@ -7188,11 +7264,11 @@ private void verifyMinAndMaxIops(String minIops, String maxIops) { } } catch (NumberFormatException ex) { - throw new InvalidParameterValueException("'Max IOPS' must be a whole number."); + throw Exceptions.invalidParameterValueException("vm.deploy.iops.max.not.integer"); } if (lMinIops > lMaxIops) { - throw new InvalidParameterValueException("'Min IOPS' must be less than or equal to 'Max IOPS'."); + throw Exceptions.invalidParameterValueException("vm.deploy.iops.min.greater.than.max"); } } @@ -7213,38 +7289,34 @@ private VMInstanceVO preVmStorageMigrationCheck(Long vmId) { if (logger.isDebugEnabled()) { logger.debug("Caller is not a root admin, permission denied to migrate the VM"); } - throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!"); + throw Exceptions.permissionDeniedException("vm.migrate.storage.permission.denied"); } VMInstanceVO vm = _vmInstanceDao.findById(vmId); if (vm == null) { - throw new InvalidParameterValueException("Unable to find the VM by id=" + vmId); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.vm.not.found"); } if (vm.getState() != State.Stopped) { - InvalidParameterValueException ex = new InvalidParameterValueException("VM is not Stopped, unable to migrate the vm having the specified id"); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; + throw Exceptions.invalidParameterValueException("vm.migrate.storage.vm.not.stopped"); } HypervisorType hypervisorType = vm.getHypervisorType(); List supportedHypervisorsForNonUserVMStorageMigration = HypervisorType.getListOfHypervisorsSupportingFunctionality(Functionality.VmStorageMigration) .stream().filter(hypervisor -> !hypervisor.equals(HypervisorType.XenServer)).collect(Collectors.toList()); if (vm.getType() != VirtualMachine.Type.User && !supportedHypervisorsForNonUserVMStorageMigration.contains(hypervisorType)) { - throw new InvalidParameterValueException(String.format( - "Unable to migrate storage of non-user VMs for hypervisor [%s]. Operation only supported for the following hypervisors: [%s].", - hypervisorType, supportedHypervisorsForNonUserVMStorageMigration)); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.nonuser.hypervisor.not.supported", Map.of("hypervisorType", hypervisorType, "supportedHypervisors", supportedHypervisorsForNonUserVMStorageMigration)); } List vols = _volsDao.findByInstance(vm.getId()); if (vols.size() > 1 && !(HypervisorType.VMware.equals(hypervisorType) || HypervisorType.KVM.equals(hypervisorType))) { - throw new InvalidParameterValueException("Data disks attached to the vm, can not migrate. Need to detach data disks first"); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.data.disks.attached"); } // Check that Vm does not have VM Snapshots if (_vmSnapshotDao.findByVm(vmId).size() > 0) { - throw new InvalidParameterValueException("Instance's disk cannot be migrated, please remove all the Instance Snapshots for this Instance"); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.snapshots.exist"); } return vm; @@ -7275,12 +7347,12 @@ public VirtualMachine vmStorageMigration(Long vmId, StoragePool destPool) { if (destPoolPodId != null && (ScopeType.CLUSTER.equals(pool.getScope()) || ScopeType.HOST.equals(pool.getScope())) && !destPoolPodId.equals(pool.getPodId())) { - throw new InvalidParameterValueException("Storage migration of non-user VMs cannot be done between storage pools of different pods"); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.nonuser.different.pods"); } } Pair checkResult = storageManager.checkIfReadyVolumeFitsInStoragePoolWithStorageAccessGroups(destPool, volume); if (!checkResult.first()) { - throw new CloudRuntimeException(String.format("Storage suitability check failed for volume %s with error, %s", volume, checkResult.second())); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.suitability.failed", Map.of("volume", volume, "error", checkResult.second())); } volumeToPoolIds.put(volume.getId(), destPool.getId()); } @@ -7300,7 +7372,7 @@ public VirtualMachine vmStorageMigration(Long vmId, Map volumeTo if (poolClusterId != null && (ScopeType.CLUSTER.equals(pool.getScope()) || ScopeType.HOST.equals(pool.getScope())) && !poolClusterId.equals(pool.getClusterId())) { - throw new InvalidParameterValueException("VM's disk cannot be migrated, input destination storage pools belong to different clusters"); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.different.clusters"); } if (pool.getClusterId() != null) { poolClusterId = pool.getClusterId(); @@ -7308,7 +7380,7 @@ public VirtualMachine vmStorageMigration(Long vmId, Map volumeTo checkDestinationHypervisorType(pool, vm); Pair checkResult = storageManager.checkIfReadyVolumeFitsInStoragePoolWithStorageAccessGroups(pool, volume); if (!checkResult.first()) { - throw new CloudRuntimeException(String.format("Storage suitability check failed for volume %s with error %s", volume, checkResult.second())); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.suitability.failed", Map.of("volume", volume, "error", checkResult.second())); } volumeToPoolIds.put(volume.getId(), pool.getId()); @@ -7322,7 +7394,7 @@ private void checkIfDestinationPoolHasSameStorageAccessGroups(StoragePool destPo if (hostId != null) { Host host = _hostDao.findById(hostId); if (!storageManager.checkIfHostAndStoragePoolHasCommonStorageAccessGroups(host, destPool)) { - throw new InvalidParameterValueException(String.format("Destination pool %s does not have matching storage access groups as host %s", destPool.getName(), host.getName())); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.access.groups.mismatch", Map.of("pool", destPool, "host", host)); } } } @@ -7335,7 +7407,7 @@ private void checkDestinationHypervisorType(StoragePool destPool, VMInstanceVO v } if (vm.getHypervisorType() != destHypervisorType && destHypervisorType != HypervisorType.Any) { - throw new InvalidParameterValueException("hypervisor is not compatible: dest: " + destHypervisorType.toString() + ", vm: " + vm.getHypervisorType().toString()); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.hypervisor.mismatch", Map.of("destHypervisor", destHypervisorType, "vmHypervisor", vm.getHypervisorType())); } } @@ -7355,48 +7427,46 @@ public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) thr if (logger.isDebugEnabled()) { logger.debug("Caller is not a root admin, permission denied to migrate the VM"); } - throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!"); + throw Exceptions.permissionDeniedException("vm.migrate.permission.denied"); } VMInstanceVO vm = _vmInstanceDao.findById(vmId); if (vm == null) { - throw new InvalidParameterValueException("Unable to find the VM by id=" + vmId); + throw Exceptions.invalidParameterValueException("vm.migrate.vm.not.found"); } // business logic if (vm.getState() != State.Running) { if (logger.isDebugEnabled()) { logger.debug("VM is not Running, unable to migrate the vm " + vm); } - InvalidParameterValueException ex = new InvalidParameterValueException("VM is not Running, unable to migrate the vm with specified id"); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; + throw Exceptions.invalidParameterValueException("vm.migrate.vm.not.running"); } checkIfHostOfVMIsInPrepareForMaintenanceState(vm, "Migrate"); if (serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) { - throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported"); + throw Exceptions.invalidParameterValueException("vm.migrate.gpu.not.supported"); } if (!isOnSupportedHypevisorForMigration(vm)) { logger.error(vm + " is not XenServer/VMware/KVM/Ovm/Hyperv, cannot migrate this VM from hypervisor type " + vm.getHypervisorType()); - throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support XenServer/VMware/KVM/Ovm/Hyperv/Ovm3 only"); + throw Exceptions.invalidParameterValueException("vm.migrate.hypervisor.not.supported"); } if (vm.getType().equals(VirtualMachine.Type.User) && vm.getHypervisorType().equals(HypervisorType.LXC)) { - throw new InvalidParameterValueException("Unsupported Hypervisor Type for User VM migration, we support XenServer/VMware/KVM/Ovm/Hyperv/Ovm3 only"); + throw Exceptions.invalidParameterValueException("vm.migrate.hypervisor.not.supported"); } if (isVMUsingLocalStorage(vm)) { logger.error(vm + " is using Local Storage, cannot migrate this VM."); - throw new InvalidParameterValueException("Unsupported operation, VM uses Local storage, cannot migrate"); + throw Exceptions.invalidParameterValueException("vm.migrate.local.storage.not.supported"); } // check if migrating to same host long srcHostId = vm.getHostId(); Host srcHost = _resourceMgr.getHost(srcHostId); if (srcHost == null) { - throw new InvalidParameterValueException("Cannot migrate VM, host with id: " + srcHostId + " for VM not found"); + throw Exceptions.invalidParameterValueException("vm.migrate.source.host.not.found"); } DeployDestination dest = null; @@ -7408,7 +7478,7 @@ public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) thr // If no suitable destination found then throw exception if (dest == null) { - throw new CloudRuntimeException("Unable to find suitable destination to migrate VM " + vm.getInstanceName()); + throw Exceptions.cloudRuntimeException("vm.migrate.destination.not.found", Map.of("instance", vm)); } logger.info("Starting migration of VM {} from host {} to host {} ", vm.getInstanceName(), srcHostId, dest.getHost().getId()); @@ -7430,9 +7500,9 @@ private DeployDestination chooseVmMigrationDestination(VMInstanceVO vm, Host src return _planningMgr.planDeployment(profile, plan, excludes, null); } catch (final AffinityConflictException e2) { logger.warn("Unable to create deployment, affinity rules associated to the VM conflict", e2); - throw new CloudRuntimeException("Unable to create deployment, affinity rules associated to the VM conflict"); + throw Exceptions.cloudRuntimeException("vm.migrate.affinity.conflict"); } catch (final InsufficientServerCapacityException e3) { - throw new CloudRuntimeException("Unable to find a server to migrate the vm to"); + throw Exceptions.cloudRuntimeException("vm.migrate.server.not.found"); } } @@ -7459,11 +7529,11 @@ protected void validateStorageAccessGroupsOnHosts(Host srcHost, Host destination } if (CollectionUtils.isEmpty(destHostStorageAccessGroupsList)) { - throw new CloudRuntimeException("Source host has storage access groups, but destination host has none."); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.access.groups.src.has.dest.none"); } if (!destHostStorageAccessGroupsList.containsAll(srcHostStorageAccessGroupsList)) { - throw new CloudRuntimeException("Storage access groups on the source and destination hosts did not match."); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.access.groups.mismatch.hosts"); } } @@ -7476,8 +7546,7 @@ protected void validateStrictHostTagCheck(VMInstanceVO vm, HostVO host) { logger.error("Cannot deploy VM: {} to host : {} due to tag mismatch. host tags: {}, " + "strict host tags: {} serviceOffering tags: {}, template tags: {}, missing tags: {}", vm, host, host.getHostTags(), UserVmManager.getStrictHostTags(), serviceOffering.getHostTag(), template.getTemplateTag(), missingTags); - throw new InvalidParameterValueException(String.format("Cannot deploy VM, destination host: %s " + - "is not compatible for the VM", host.getName())); + throw Exceptions.invalidParameterValueException("vm.migrate.host.tags.mismatch", Map.of("host", host)); } } @@ -7487,24 +7556,23 @@ private DeployDestination checkVmMigrationDestination(VMInstanceVO vm, Host srcH return null; } if (destinationHost.getId() == srcHost.getId()) { - throw new InvalidParameterValueException("Cannot migrate VM, VM is already present on this host, please specify valid destination host to migrate the VM"); + throw Exceptions.invalidParameterValueException("vm.migrate.vm.already.on.host"); } // check if host is UP if (destinationHost.getState() != com.cloud.host.Status.Up || destinationHost.getResourceState() != ResourceState.Enabled) { - throw new InvalidParameterValueException("Cannot migrate VM, destination host is not in correct state, has status: " + destinationHost.getState() + ", state: " - + destinationHost.getResourceState()); + throw Exceptions.invalidParameterValueException("vm.migrate.destination.host.not.right.state", Map.of("host", destinationHost)); } if (vm.getType() != VirtualMachine.Type.User) { // for System VMs check that the destination host is within the same pod if (srcHost.getPodId() != null && !srcHost.getPodId().equals(destinationHost.getPodId())) { - throw new InvalidParameterValueException("Cannot migrate the VM, destination host is not in the same pod as current host of the VM"); + throw Exceptions.invalidParameterValueException("vm.migrate.destination.different.pod"); } } if (dpdkHelper.isVMDpdkEnabled(vm.getId()) && !dpdkHelper.isHostDpdkEnabled(destinationHost.getId())) { - throw new CloudRuntimeException("Cannot migrate VM, VM is DPDK enabled VM but destination host is not DPDK enabled"); + throw Exceptions.cloudRuntimeException("vm.migrate.dpdk.host.not.supported"); } HostVO destinationHostVO = _hostDao.findById(destinationHost.getId()); @@ -7531,7 +7599,7 @@ private DeployDestination checkVmMigrationDestination(VMInstanceVO vm, Host srcH Long vmId = vm.getId(); logger.debug("Checking if there are any ongoing snapshots volumes associated with VM {}", vm); if (checkStatusOfVolumeSnapshots(vm, null)) { - throw new CloudRuntimeException("There is/are unbacked up snapshot(s) on volume(s) attached to this VM, VM Migration is not permitted, please try again later."); + throw Exceptions.cloudRuntimeException("vm.migrate.unbacked.snapshots"); } logger.debug("Found no ongoing snapshots on volumes associated with the vm {}", vm); @@ -7569,7 +7637,7 @@ private void checkIfHostOfVMIsInPrepareForMaintenanceState(VirtualMachine vm, St } logger.debug("Host is in PrepareForMaintenance state - {} VM operation on the VM: {} is not allowed", operation, vm); - throw new InvalidParameterValueException(String.format("%s VM operation on the VM: %s is not allowed as host is preparing for maintenance mode", operation, vm)); + throw Exceptions.invalidParameterValueException("vm.migrate.host.maintenance.mode", Map.of("operation", operation, "instance", vm)); } private Long accountOfDedicatedHost(HostVO host) { @@ -7809,7 +7877,7 @@ private Pair getHostsForMigrateVmWithStorage(VMInstanceVO vm, Host d Host srcHost = _resourceMgr.getHost(srcHostId); if (srcHost == null) { - throw new InvalidParameterValueException("Cannot migrate VM, host with ID: " + srcHostId + " for VM not found"); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.source.host.not.found"); } if (destinationHost == null) { @@ -7818,8 +7886,7 @@ private Pair getHostsForMigrateVmWithStorage(VMInstanceVO vm, Host d // Check if source and destination hosts are valid and migrating to same host if (destinationHost.getId() == srcHostId) { - throw new InvalidParameterValueException(String.format("Cannot migrate VM as it is already present on host %s (ID: %s), please specify valid destination host to migrate the VM", - destinationHost.getName(), destinationHost.getUuid())); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.vm.already.on.host"); } String srcHostVersion = srcHost.getHypervisorVersion(); @@ -7827,15 +7894,13 @@ private Pair getHostsForMigrateVmWithStorage(VMInstanceVO vm, Host d // Check if the source and destination hosts are of the same type and support storage motion. if (!srcHost.getHypervisorType().equals(destinationHost.getHypervisorType())) { - throw new CloudRuntimeException("The source and destination hosts are not of the same type and version. Source hypervisor type and version: " + - srcHost.getHypervisorType().toString() + " " + srcHostVersion + ", Destination hypervisor type and version: " + - destinationHost.getHypervisorType().toString() + " " + destHostVersion); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.hypervisor.version.mismatch"); } if (!VirtualMachine.Type.User.equals(vm.getType())) { // for System VMs check that the destination host is within the same pod if (srcHost.getPodId() != null && !srcHost.getPodId().equals(destinationHost.getPodId())) { - throw new InvalidParameterValueException("Cannot migrate the VM, destination host is not in the same pod as current host of the VM"); + throw Exceptions.invalidParameterValueException("vm.migrate.destination.different.pod"); } } @@ -7850,19 +7915,18 @@ private Pair getHostsForMigrateVmWithStorage(VMInstanceVO vm, Host d } if (!_hypervisorCapabilitiesDao.isStorageMotionSupported(srcHost.getHypervisorType(), srcHostVersion)) { - throw new CloudRuntimeException(String.format("Migration with storage isn't supported for source host %s (ID: %s) on hypervisor %s with version %s", srcHost.getName(), srcHost.getUuid(), srcHost.getHypervisorType(), srcHost.getHypervisorVersion())); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.source.hypervisor.not.supported", Map.of("host", srcHost, "hypervisorType", srcHost.getHypervisorType(), "version", srcHost.getHypervisorVersion())); } if (srcHostVersion == null || !srcHostVersion.equals(destHostVersion)) { if (!_hypervisorCapabilitiesDao.isStorageMotionSupported(destinationHost.getHypervisorType(), destHostVersion)) { - throw new CloudRuntimeException(String.format("Migration with storage isn't supported for target host %s (ID: %s) on hypervisor %s with version %s", destinationHost.getName(), destinationHost.getUuid(), destinationHost.getHypervisorType(), destinationHost.getHypervisorVersion())); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.destination.hypervisor.not.supported", Map.of("host", destinationHost, "hypervisorType", destinationHost.getHypervisorType(), "version", destinationHost.getHypervisorVersion())); } } // Check if destination host is up. if (destinationHost.getState() != com.cloud.host.Status.Up || destinationHost.getResourceState() != ResourceState.Enabled) { - throw new CloudRuntimeException(String.format("Cannot migrate VM, destination host %s (ID: %s) is not in correct state, has status: %s, state: %s", - destinationHost.getName(), destinationHost.getUuid(), destinationHost.getState(), destinationHost.getResourceState())); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.destination.host.not.right.state", Map.of("host", destinationHost)); } // Check max guest vm limit for the destinationHost. @@ -7880,7 +7944,7 @@ private List getVmVolumesForMigrateVmWithStorage(VMInstanceVO vm) { List vmVolumes = _volsDao.findUsableVolumesForInstance(vm.getId()); for (VolumeVO volume : vmVolumes) { if (volume.getState() != Volume.State.Ready) { - throw new CloudRuntimeException(String.format("Volume %s (ID: %s) of the VM is not in Ready state. Cannot migrate the VM %s (ID: %s) with its volumes", volume.getName(), volume.getUuid(), vm.getInstanceName(), vm.getUuid())); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.volume.not.ready", Map.of("volume", volume)); } } return vmVolumes; @@ -7897,30 +7961,28 @@ private Map getVolumePoolMappingForMigrateVmWithStorage(VMInstanceVO VolumeVO volume = _volsDao.findByUuid(entry.getKey()); StoragePoolVO pool = _storagePoolDao.findByUuid(entry.getValue()); if (volume == null) { - throw new InvalidParameterValueException("There is no volume present with the given id " + entry.getKey()); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.volume.not.found"); } else if (pool == null) { - throw new InvalidParameterValueException("There is no storage pool present with the given id " + entry.getValue()); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.pool.not.found"); } else if (pool.isInMaintenance()) { - throw new InvalidParameterValueException("Cannot migrate volume " + volume + "to the destination storage pool " + pool.getName() + - " as the storage pool is in maintenance mode."); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.volume.pool.incompatible", Map.of("volume", volume, "pool", pool)); } else { // Verify the volume given belongs to the vm. if (!vmVolumes.contains(volume)) { - throw new InvalidParameterValueException(String.format("Volume " + volume + " doesn't belong to the VM %s (ID: %s) that has to be migrated", vm.getInstanceName(), vm.getUuid())); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.volume.vm.mismatch", Map.of("volume", volume, "instance", vm)); } volToPoolObjectMap.put(volume.getId(), pool.getId()); } HostVO host = _hostDao.findById(vm.getHostId()); if (!storageManager.checkIfHostAndStoragePoolHasCommonStorageAccessGroups(host, pool)) { - throw new InvalidParameterValueException(String.format("Destination pool %s for the volume %s does not have matching storage access groups as host %s", pool.getName(), volume.getName(), host.getName())); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.volume.pool.access.groups.mismatch", Map.of("pool", pool, "volume", volume, "host", host)); } HypervisorType hypervisorType = _volsDao.getHypervisorType(volume.getId()); try { snapshotHelper.checkKvmVolumeSnapshotsOnlyInPrimaryStorage(volume, hypervisorType); } catch (CloudRuntimeException ex) { - throw new CloudRuntimeException(String.format("Unable to migrate %s to the destination storage pool [%s] due to [%s]", volume, - new ToStringBuilder(pool, ToStringStyle.JSON_STYLE).append("uuid", pool.getUuid()).append("name", pool.getName()).toString(), ex.getMessage()), ex); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.volume.pool.unsuitable", Map.of("volume", volume, "pool", pool, "error", ex.getMessage()), ex); } if (hypervisorType.equals(HypervisorType.VMware)) { @@ -7930,10 +7992,10 @@ private Map getVolumePoolMappingForMigrateVmWithStorage(VMInstanceVO Pair volumeDiskProfilePair = new Pair<>(volume, diskProfile); boolean isStoragePoolStoragepolicyCompliance = storageManager.isStoragePoolCompliantWithStoragePolicy(Arrays.asList(volumeDiskProfilePair), pool); if (!isStoragePoolStoragepolicyCompliance) { - throw new CloudRuntimeException(String.format("Storage pool %s is not storage policy compliance with the volume %s", pool.getUuid(), volume.getUuid())); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.volume.policy.noncompliant", Map.of("pool", pool, "volume", volume)); } } catch (StorageUnavailableException e) { - throw new CloudRuntimeException(String.format("Could not verify storage policy compliance against storage pool %s due to exception %s", pool.getUuid(), e.getMessage())); + throw Exceptions.cloudRuntimeException("vm.migrate.storage.volume.policy.verify.failed", Map.of("pool", pool, "error", e.getMessage())); } } } @@ -7955,7 +8017,7 @@ protected Host chooseVmMigrationDestinationUsingVolumePoolMap(VMInstanceVO vm, H } DeployDestination deployDestination = chooseVmMigrationDestination(vm, srcHost, poolId); if (deployDestination == null || deployDestination.getHost() == null) { - throw new CloudRuntimeException("Unable to find suitable destination to migrate VM " + vm.getInstanceName()); + throw Exceptions.cloudRuntimeException("vm.migrate.destination.not.found", Map.of("instance", vm)); } return deployDestination.getHost(); } @@ -7970,12 +8032,12 @@ public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinatio if (logger.isDebugEnabled()) { logger.debug("Caller is not a root admin, permission denied to migrate the VM"); } - throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!"); + throw Exceptions.permissionDeniedException("vm.migrate.permission.denied"); } VMInstanceVO vm = _vmInstanceDao.findById(vmId); if (vm == null) { - throw new InvalidParameterValueException("Unable to find the VM by ID " + vmId); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.vm.not.found"); } // OfflineVmwareMigration: this would be it ;) if multiple paths exist: unify @@ -7984,25 +8046,20 @@ public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinatio if (logger.isDebugEnabled()) { logger.debug("VM is not Running, unable to migrate the vm " + vm); } - CloudRuntimeException ex = new CloudRuntimeException(String.format("Unable to migrate the VM %s (ID: %s) as it is not in Running state", vm.getInstanceName(), vm.getUuid())); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; + throw Exceptions.cloudRuntimeException("vm.migrate.storage.vm.not.running"); } if (serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) { - throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported"); + throw Exceptions.invalidParameterValueException("vm.migrate.gpu.not.supported"); } if (!vm.getHypervisorType().isFunctionalitySupported(Functionality.VmStorageMigration)) { - throw new InvalidParameterValueException( - String.format("Unsupported hypervisor: %s for VM migration, we support [%s] only", - vm.getHypervisorType(), - HypervisorType.getListOfHypervisorsSupportingFunctionality(Functionality.VmStorageMigration))); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.hypervisor.not.supported", Map.of("hypervisorType", vm.getHypervisorType())); } if (!vm.getHypervisorType().isFunctionalitySupported(Functionality.VmStorageMigrationWithSnapshots) && CollectionUtils.isNotEmpty(_vmSnapshotDao.findByVm(vmId))) { - throw new InvalidParameterValueException("Instance with Instance Snapshots cannot be migrated with storage, please remove all Instance Snapshots"); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.instance.snapshots.exist"); } Pair sourceDestinationHosts = getHostsForMigrateVmWithStorage(vm, destinationHost); @@ -8015,8 +8072,7 @@ public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinatio if (!VirtualMachine.Type.User.equals(vm.getType())) { return migrateVirtualMachine(vmId, destinationHost); } - throw new InvalidParameterValueException(String.format("Migration of the VM: %s (ID: %s) from host %s (ID: %s) to destination host %s (ID: %s) doesn't involve migrating the volumes", - vm.getInstanceName(), vm.getUuid(), srcHost.getName(), srcHost.getUuid(), destinationHost.getName(), destinationHost.getUuid())); + throw Exceptions.invalidParameterValueException("vm.migrate.storage.no.volume.migration", Map.of("instance", vm, "srcHost", srcHost, "destHost", destinationHost)); } Map volToPoolObjectMap = getVolumePoolMappingForMigrateVmWithStorage(vm, volumeToPool); @@ -8065,7 +8121,7 @@ public UserVm moveVmToUser(final AssignVMCmd cmd) throws ResourceAllocationExcep Long callerId = caller.getId(); logger.trace("Verifying if caller [{}] is root or domain admin.", caller); if (!_accountMgr.isRootAdmin(callerId) && !_accountMgr.isDomainAdmin(callerId)) { - throw new InvalidParameterValueException(String.format("Only root or domain admins are allowed to assign VMs. Caller [%s] is of type [%s].", caller, caller.getType())); + throw Exceptions.invalidParameterValueException("vm.assign.permission.denied", Map.of("caller", caller, "callerType", caller.getType())); } Long vmId = cmd.getVmId(); @@ -8084,7 +8140,7 @@ public UserVm moveVmToUser(final AssignVMCmd cmd) throws ResourceAllocationExcep logger.trace("Verifying if the provided domain ID [{}] is valid.", domainId); if (projectId != null && domainId == null) { - throw new InvalidParameterValueException("Please provide a valid domain ID; cannot assign VM to a project if domain ID is NULL."); + throw Exceptions.invalidParameterValueException("vm.assign.domain.id.null"); } validateIfVmHasNoRules(vm, vmId); @@ -8136,11 +8192,11 @@ protected void validateIfVmSupportsMigration(UserVmVO vm, Long vmId) { logger.trace("Validating if VM [{}] exists and is not in state [{}].", vmId, State.Running); if (vm == null) { - throw new InvalidParameterValueException(String.format("There is no VM by ID [%s].", vmId)); + throw Exceptions.invalidParameterValueException("vm.assign.vm.not.found"); } else if (vm.getState() == State.Running) { - throw new InvalidParameterValueException(String.format("Unable to move VM [%s] in [%s] state.", vm, vm.getState())); + throw Exceptions.invalidParameterValueException("vm.assign.vm.not.right.state", Map.of("instance", vm, "instanceState", vm.getState())); } else if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) { - throw new InvalidParameterValueException("Migration is not supported for Shared FileSystem Instances."); + throw Exceptions.invalidParameterValueException("vm.assign.sharedfs.not.supported"); } } @@ -8156,23 +8212,23 @@ protected void validateIfVmHasNoRules(UserVmVO vm, Long vmId) throws InvalidPara List portForwardingRules = _portForwardingDao.listByVm(vmId); if (CollectionUtils.isNotEmpty(portForwardingRules)) { - throw new InvalidParameterValueException(String.format("Remove any Port Forwarding rules for VM [%s] before assigning it to another user.", vm)); + throw Exceptions.invalidParameterValueException("vm.assign.portforwarding.rules.exist", Map.of("instance", vm)); } List staticNatRules = _rulesDao.listStaticNatByVmId(vmId); if (CollectionUtils.isNotEmpty(staticNatRules)) { - throw new InvalidParameterValueException(String.format("Remove the StaticNat rules for VM [%s] before assigning it to another user.", vm)); + throw Exceptions.invalidParameterValueException("vm.assign.staticnat.rules.exist", Map.of("instance", vm)); } List loadBalancerVmMaps = _loadBalancerVMMapDao.listByInstanceId(vmId); if (CollectionUtils.isNotEmpty(loadBalancerVmMaps)) { - throw new InvalidParameterValueException(String.format("Remove the Load Balancing rules for VM [%s] before assigning it to another user.", vm)); + throw Exceptions.invalidParameterValueException("vm.assign.loadbalancer.rules.exist", Map.of("instance", vm)); } List ips = _ipAddressDao.findAllByAssociatedVmId(vmId); for (IPAddressVO ip : ips) { if (ip.isOneToOneNat()) { - throw new InvalidParameterValueException(String.format("Remove the One to One Nat rule for VM [%s] for IP [%s].", vm, ip)); + throw Exceptions.invalidParameterValueException("vm.assign.onetoonat.rule.exists", Map.of("instance", vm, "ip", ip)); } } } @@ -8183,8 +8239,7 @@ protected void validateIfVolumesHaveNoSnapshots(List volumes) throws I logger.trace("Verifying snapshots for volume [{}].", volume); List snapshots = _snapshotDao.listByStatusNotIn(volume.getId(), Snapshot.State.Destroyed, Snapshot.State.Error); if (CollectionUtils.isNotEmpty(snapshots)) { - throw new InvalidParameterValueException(String.format("Snapshots exist for volume [%s]. Detach volume or remove snapshots for the volume before assigning VM to " - + "another user.", volume.getName())); + throw Exceptions.invalidParameterValueException("vm.assign.volume.snapshots.exist", Map.of("volume", volume)); } } } @@ -8220,7 +8275,7 @@ protected void validateIfNewOwnerHasAccessToTemplate(UserVmVO vm, Account newAcc logger.trace("Validating if new owner [{}] has access to the template specified for VM [{}].", newAccount, vm); if (template == null) { - throw new InvalidParameterValueException(String.format("Template for VM [%s] cannot be found.", vm.getUuid())); + throw Exceptions.invalidParameterValueException("vm.assign.template.not.found", Map.of("instance", vm)); } logger.debug("Verifying if new owner [{}] has access to the template [{}].", newAccount, template.getUuid()); @@ -8228,7 +8283,7 @@ protected void validateIfNewOwnerHasAccessToTemplate(UserVmVO vm, Account newAcc _accountMgr.checkAccess(newAccount, AccessType.UseEntry, true, template); } catch (PermissionDeniedException e) { String newMsg = String.format("New owner [%s] does not have access to the template specified for VM [%s].", newAccount, vm); - throw new PermissionDeniedException(newMsg, e); + throw Exceptions.permissionDeniedException("vm.assign.template.access.denied", Map.of("account", newAccount, "instance", vm)); } } @@ -8256,7 +8311,7 @@ protected Network ensureDestinationNetwork(AssignVMCmd cmd, UserVmVO vm, Account return null; } if (CollectionUtils.isNotEmpty(securityGroupIdList)) { - throw new InvalidParameterValueException("Cannot move VM with security groups; security group feature is not enabled in this zone."); + throw Exceptions.invalidParameterValueException("vm.assign.securitygroup.zone.not.enabled"); } LinkedHashSet applicableNetworks = new LinkedHashSet<>(); @@ -8282,13 +8337,11 @@ protected Network ensureDestinationNetwork(AssignVMCmd cmd, UserVmVO vm, Account protected NetworkOfferingVO getOfferingWithRequiredAvailabilityForNetworkCreation() { List requiredOfferings = _networkOfferingDao.listByAvailability(Availability.Required, false); if (CollectionUtils.isEmpty(requiredOfferings)) { - throw new InvalidParameterValueException(String.format("Unable to find network offering with availability [%s] to automatically create the network as a part of VM " - + "creation.", Availability.Required)); + throw Exceptions.invalidParameterValueException("vm.assign.network.offering.required.not.found"); } NetworkOfferingVO firstRequiredOffering = requiredOfferings.get(0); if (firstRequiredOffering.getState() != NetworkOffering.State.Enabled) { - throw new InvalidParameterValueException(String.format("Required network offering ID [%s] is not in [%s] state.", firstRequiredOffering.getId(), - NetworkOffering.State.Enabled)); + throw Exceptions.invalidParameterValueException("vm.assign.network.offering.required.not.enabled", Map.of("offering", firstRequiredOffering)); } return firstRequiredOffering; } @@ -8337,7 +8390,7 @@ protected void executeStepsToChangeOwnershipOfVm(AssignVMCmd cmd, Account caller try { updateVmNetwork(cmd, caller, vm, newAccount, template); } catch (InsufficientCapacityException | ResourceAllocationException e) { - throw new CloudRuntimeException(String.format("Unable to update networks when assigning VM [%s] due to [%s].", vm, e.getMessage()), e); + throw Exceptions.cloudRuntimeException("vm.assign.networks.update.failed", Map.of("instance", vm), e); } logger.trace(String.format("Incrementing new account [%s] resource count.", newAccount)); @@ -8441,19 +8494,19 @@ protected void validateOldAndNewAccounts(Account oldAccount, Account newAccount, logger.trace("Validating old [{}] and new accounts [{}].", oldAccount, newAccount); if (oldAccount == null) { - throw new InvalidParameterValueException(String.format("Invalid old account [%s] for VM in domain [%s].", oldAccountId, domainId)); + throw Exceptions.invalidParameterValueException("vm.assign.old.account.invalid", Map.of("account", oldAccountId, "domain", domainId)); } if (newAccount == null) { - throw new InvalidParameterValueException(String.format("Invalid new account [%s] for VM in domain [%s].", newAccountName, domainId)); + throw Exceptions.invalidParameterValueException("vm.assign.new.account.invalid", Map.of("account", newAccountName, "domain", domainId)); } if (newAccount.getState() == Account.State.DISABLED) { - throw new InvalidParameterValueException(String.format("The new account owner [%s] is disabled.", newAccount)); + throw Exceptions.invalidParameterValueException("vm.assign.new.account.disabled", Map.of("account", newAccount)); } if (oldAccount.getAccountId() == newAccount.getAccountId()) { - throw new InvalidParameterValueException(String.format("The new account [%s] is the same as the old account.", newAccount)); + throw Exceptions.invalidParameterValueException("vm.assign.same.account", Map.of("account", newAccount)); } } @@ -8488,7 +8541,7 @@ protected void updateBasicTypeNetworkForVm(UserVmVO vm, Account newAccount, Virt DataCenterVO zone, List networkIdList, List securityGroupIdList) throws InsufficientCapacityException { if (networkIdList != null && !networkIdList.isEmpty()) { - throw new InvalidParameterValueException("Cannot move VM with Network IDs; this is a basic zone VM."); + throw Exceptions.invalidParameterValueException("vm.assign.basic.zone.network.ids.not.allowed"); } logger.trace("Cleanup of old security groups for VM [{}]. They will be recreated for the new account once the VM is started.", vm); @@ -8549,7 +8602,7 @@ protected void updateAdvancedTypeNetworkForVm(Account caller, UserVmVO vm, Accou NetworkVO defaultNetwork = addNicsToApplicableNetworksAndReturnDefaultNetwork(applicableNetworks, requestedIPv4ForNics, requestedIPv6ForNics, networks); if (applicableNetworks.isEmpty()) { - throw new InvalidParameterValueException("No network is specified, please specify one when you move the VM. For now, please add a network to VM on NICs tab."); + throw Exceptions.invalidParameterValueException("vm.assign.advanced.sg.no.network"); } else { allocateNetworksForVm(vm, networks); } @@ -8559,7 +8612,7 @@ protected void updateAdvancedTypeNetworkForVm(Account caller, UserVmVO vm, Accou } if (securityGroupIdList != null && !securityGroupIdList.isEmpty()) { - throw new InvalidParameterValueException("Cannot move VM with security groups; security group feature is not enabled in this zone."); + throw Exceptions.invalidParameterValueException("vm.assign.securitygroup.zone.not.enabled"); } addNetworksToNetworkIdList(vm, newAccount, networkIdList, applicableNetworks, requestedIPv4ForNics, requestedIPv6ForNics); @@ -8586,7 +8639,7 @@ protected void addDefaultNetworkToNetworkList(List networkList, Netwo logger.trace("Adding default network to network list."); if (defaultNetwork == null) { - throw new InvalidParameterValueException("Unable to find a default network to start a VM."); + throw Exceptions.invalidParameterValueException("vm.assign.default.network.not.found"); } networkList.add(_networkDao.findById(defaultNetwork.getId())); @@ -8606,7 +8659,7 @@ protected void addSecurityGroupsToVm(Account newAccount, UserVmVO vm, VirtualMac boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware); if (securityGroupIdList != null && isVmWare) { - throw new InvalidParameterValueException("Security group feature is not supported for VMWare hypervisor."); + throw Exceptions.invalidParameterValueException("vm.assign.securitygroup.vmware.not.supported"); } else if (!isVmWare && (defaultNetwork == null || _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork)) && _networkModel.canAddDefaultSecurityGroup()) { if (securityGroupIdList == null) { securityGroupIdList = new ArrayList<>(); @@ -8691,10 +8744,9 @@ protected void selectApplicableNetworkToCreateVm(Account newAccount, DataCenterV NetworkVO defaultNetwork; List virtualNetworks = _networkModel.listNetworksForAccount(newAccount.getId(), zone.getId(), Network.GuestType.Isolated); if (virtualNetworks.isEmpty()) { - throw new CloudRuntimeException(String.format("Could not find an applicable network to create virtual machine for account [%s].", newAccount)); + throw Exceptions.cloudRuntimeException("vm.assign.applicable.network.not.found", Map.of("account", newAccount)); } else if (virtualNetworks.size() > 1) { - throw new InvalidParameterValueException(String.format("More than one default isolated network has been found for account [%s]; please specify networkIDs.", - newAccount)); + throw Exceptions.invalidParameterValueException("vm.assign.multiple.default.networks", Map.of("account", newAccount)); } else { defaultNetwork = _networkDao.findById(virtualNetworks.get(0).getId()); } @@ -8787,16 +8839,14 @@ protected void addAdditionalNetworksToVm(UserVmVO vm, Account newAccount, List rootVols = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT); if (rootVols.isEmpty()) { - InvalidParameterValueException ex = new InvalidParameterValueException("Can not find root volume for VM " + vm.getUuid()); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; + throw Exceptions.invalidParameterValueException("vm.restore.root.volume.not.found", Map.of("instance", vm)); } if (rootVols.size() > 1 && currentTemplate != null && !currentTemplate.isDeployAsIs()) { - InvalidParameterValueException ex = new InvalidParameterValueException("There are " + rootVols.size() + " root volumes for VM " + vm.getUuid()); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; + throw Exceptions.invalidParameterValueException("vm.restore.multiple.root.volumes", Map.of("count", rootVols.size(), "instance", vm)); } // If target VM has associated VM snapshots then don't allow restore of VM List vmSnapshots = _vmSnapshotDao.findByVm(vmId); if (vmSnapshots.size() > 0) { - throw new InvalidParameterValueException("Unable to restore Instance, please remove Instance Snapshots before restoring Instance"); + throw Exceptions.invalidParameterValueException("vm.restore.snapshots.exist"); } VMTemplateVO template = getRestoreVirtualMachineTemplate(caller, newTemplateId, rootVols, vm); @@ -9120,9 +9161,7 @@ public UserVm restoreVirtualMachine(final Account caller, final long vmId, final _itMgr.stop(vm.getUuid()); } catch (ResourceUnavailableException e) { logger.debug("Stop vm {} failed", vm, e); - CloudRuntimeException ex = new CloudRuntimeException("Stop vm failed for specified vmId"); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; + throw Exceptions.cloudRuntimeException("vm.restore.stop.failed"); } } @@ -9224,7 +9263,7 @@ public Pair doInTransaction(final TransactionStatus status) th password = _mgr.generateRandomPassword(); boolean result = resetVMPasswordInternal(vmId, password); if (!result) { - throw new CloudRuntimeException("VM reset is completed but failed to reset password for the virtual machine "); + throw Exceptions.cloudRuntimeException("vm.restore.password.reset.failed"); } vm.setPassword(password); } @@ -9251,9 +9290,7 @@ public Pair doInTransaction(final TransactionStatus status) th } } catch (Exception e) { logger.debug("Unable to start VM " + vm.getUuid(), e); - CloudRuntimeException ex = new CloudRuntimeException("Unable to start VM with specified id" + e.getMessage()); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; + throw Exceptions.cloudRuntimeException("vm.restore.start.failed"); } } @@ -9262,7 +9299,7 @@ public Pair doInTransaction(final TransactionStatus status) th } catch (ResourceAllocationException e) { logger.error("Failed to restore VM {} due to {}", vm, e.getMessage(), e); - throw new CloudRuntimeException("Failed to restore VM " + vm.getUuid() + " due to " + e.getMessage(), e); + throw Exceptions.cloudRuntimeException("vm.restore.failed", Map.of("instance", vm), e); } finally { ReservationHelper.closeAll(reservations); } @@ -9370,12 +9407,12 @@ private void checkRestoreVmFromTemplate(UserVmVO vm, VMTemplateVO template, List if (!template.isDirectDownload()) { tmplStore = _templateStoreDao.findByTemplateZoneReady(template.getId(), vm.getDataCenterId()); if (tmplStore == null) { - throw new InvalidParameterValueException("Cannot restore the vm as the template " + template.getUuid() + " isn't available in the zone"); + throw Exceptions.invalidParameterValueException("vm.restore.template.not.available.in.zone", Map.of("template", template)); } } else { tmplStore = _templateStoreDao.findByTemplate(template.getId(), DataStoreRole.Image); if (tmplStore == null || (tmplStore != null && !tmplStore.getDownloadState().equals(VMTemplateStorageResourceAssoc.Status.BYPASSED))) { - throw new InvalidParameterValueException("Cannot restore the vm as the bypassed template " + template.getUuid() + " isn't available in the zone"); + throw Exceptions.invalidParameterValueException("vm.restore.template.bypassed.not.available", Map.of("template", template)); } } @@ -9455,7 +9492,7 @@ else if (host.getHypervisorType() == HypervisorType.KVM) { cmd = null; } else { - throw new CloudRuntimeException("This hypervisor type is not supported on managed storage for this command."); + throw Exceptions.cloudRuntimeException("vm.restore.managed.storage.hypervisor.not.supported"); } if (cmd != null) { @@ -9466,7 +9503,7 @@ else if (host.getHypervisorType() == HypervisorType.KVM) { try { _agentMgr.send(hostId, cmds); } catch (Exception ex) { - throw new CloudRuntimeException(ex.getMessage()); + throw Exceptions.cloudRuntimeException("vm.restore.managed.storage.send.failed", Map.of(), ex); } if (!cmds.isSuccessful()) { @@ -9474,7 +9511,7 @@ else if (host.getHypervisorType() == HypervisorType.KVM) { if (!answer.getResult()) { logger.warn("Failed to reset vm {} due to: {}", vm, answer.getDetails()); - throw new CloudRuntimeException("Unable to reset " + vm + " due to " + answer.getDetails()); + throw Exceptions.cloudRuntimeException("vm.restore.managed.storage.reset.failed", Map.of("instance", vm)); } } } @@ -9550,7 +9587,7 @@ private void encryptAndStorePassword(UserVmVO vm, String password) { } String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey(sshPublicKeys, password); if (encryptedPasswd == null) { - throw new CloudRuntimeException("Error encrypting password"); + throw Exceptions.cloudRuntimeException("vm.password.encrypt.failed"); } vm.setDetail(VmDetailConstants.ENCRYPTED_PASSWORD, encryptedPasswd); @@ -9588,7 +9625,7 @@ public ConfigKey[] getConfigKeys() { public String getVmUserData(long vmId) { UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { - throw new InvalidParameterValueException("Unable to find virtual machine with id " + vmId); + throw Exceptions.invalidParameterValueException("vm.userdata.vm.not.found"); } _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, vm); @@ -9641,7 +9678,7 @@ private void checkForUnattachedVolumes(long vmId, List volumes) { } if (!StringUtils.isEmpty(sb.toString())) { - throw new InvalidParameterValueException("The following supplied volumes are not DATADISK attached to the VM: " + sb); + throw Exceptions.invalidParameterValueException("vm.destroy.volumes.not.attached"); } } @@ -9649,12 +9686,10 @@ private void validateVolumes(List volumes) { for (VolumeVO volume : volumes) { if (!(volume.getVolumeType() == Volume.Type.ROOT || volume.getVolumeType() == Volume.Type.DATADISK)) { - throw new InvalidParameterValueException("Please specify volume of type " + Volume.Type.DATADISK + " or " + Volume.Type.ROOT); + throw Exceptions.invalidParameterValueException("vm.destroy.volume.type.invalid"); } if (volume.isDeleteProtection()) { - throw new InvalidParameterValueException(String.format( - "Volume [id = %s, name = %s] has delete protection enabled and cannot be deleted", - volume.getUuid(), volume.getName())); + throw Exceptions.invalidParameterValueException("vm.destroy.volume.delete.protection", Map.of("volume", volume)); } } } @@ -9725,10 +9760,10 @@ public UserVm importVM(final DataCenter zone, final Host host, final VirtualMach final VirtualMachine.PowerState powerState, final LinkedHashMap> networkNicMap) throws InsufficientCapacityException { return Transaction.execute((TransactionCallbackWithException) status -> { if (zone == null) { - throw new InvalidParameterValueException("Unable to import virtual machine with invalid zone"); + throw Exceptions.invalidParameterValueException("vm.import.zone.invalid"); } if (host == null && hypervisorType == HypervisorType.VMware) { - throw new InvalidParameterValueException("Unable to import virtual machine with invalid host"); + throw Exceptions.invalidParameterValueException("vm.import.host.invalid"); } final long id = _vmDao.getNextInSequence(Long.class, "id"); @@ -9756,7 +9791,7 @@ public UserVm importVM(final DataCenter zone, final Host host, final VirtualMach public Pair unmanageUserVM(Long vmId, Long paramHostId) { UserVmVO vm = _vmDao.findById(vmId); if (vm == null || vm.getRemoved() != null) { - throw new InvalidParameterValueException("Unable to find a VM with ID = " + vmId); + throw Exceptions.invalidParameterValueException("vm.unmanage.vm.not.found"); } vm = _vmDao.acquireInLockTable(vm.getId()); @@ -9765,7 +9800,7 @@ public Pair unmanageUserVM(Long vmId, Long paramHostId) { if (vm.getState() != State.Running && vm.getState() != State.Stopped) { String errorMsg = "Instance: " + vm.getName() + " is not running or stopped, cannot be unmanaged"; logger.debug(errorMsg); - throw new CloudRuntimeException(errorMsg); + throw Exceptions.cloudRuntimeException("vm.unmanage.vm.not.right.state", Map.of("instance", vm)); } if (!UnmanagedVMsManager.isSupported(vm.getHypervisorType())) { @@ -9783,7 +9818,7 @@ public Pair unmanageUserVM(Long vmId, Long paramHostId) { unmanageVMFromDB(vm.getId()); publishUnmanageVMUsageEvents(vm, volumes); } else { - throw new CloudRuntimeException("Error while unmanaging VM: " + vm.getUuid()); + throw Exceptions.cloudRuntimeException("vm.unmanage.failed", Map.of("instance", vm)); } return result; } catch (Exception e) { @@ -9807,9 +9842,7 @@ private void updateDetailsWithRootDiskAttributes(Map details, Vm private void checkRootDiskSizeAgainstBackup(Long instanceVolumeSize,DiskOffering rootDiskOffering, Long backupVolumeSize) { long instanceRootDiskSize = rootDiskOffering.isCustomized() ? instanceVolumeSize : rootDiskOffering.getDiskSize() / GiB_TO_BYTES; if (instanceRootDiskSize < backupVolumeSize) { - throw new InvalidParameterValueException( - String.format("Instance volume root disk size %d[GiB] cannot be less than the backed-up volume size %d[GiB].", - instanceVolumeSize, backupVolumeSize)); + throw Exceptions.invalidParameterValueException("vm.backup.root.disk.size.smaller.than.backup", Map.of("instanceSize", instanceVolumeSize, "backupSize", backupVolumeSize)); } } @@ -9817,22 +9850,22 @@ private void checkRootDiskSizeAgainstBackup(Long instanceVolumeSize,DiskOffering public UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws InsufficientCapacityException, ResourceAllocationException, ResourceUnavailableException { BackupVO backup = backupDao.findById(cmd.getBackupId()); if (backup == null) { - throw new InvalidParameterValueException("Backup " + cmd.getBackupId() + " does not exist"); + throw Exceptions.invalidParameterValueException("vm.backup.not.found"); } backupManager.validateBackupForZone(backup.getZoneId()); if (!backupManager.canCreateInstanceFromBackup(cmd.getBackupId())) { - throw new CloudRuntimeException("Create instance from backup is not supported for this provider."); + throw Exceptions.cloudRuntimeException("vm.backup.create.instance.not.supported"); } DataCenter targetZone = _dcDao.findById(cmd.getZoneId()); if (targetZone == null) { - throw new InvalidParameterValueException("Unable to find zone by id=" + cmd.getZoneId()); + throw Exceptions.invalidParameterValueException("vm.backup.zone.not.found"); } if (cmd.getZoneId() != backup.getZoneId() && !backupManager.canCreateInstanceFromBackupAcrossZones(cmd.getBackupId())) { - throw new CloudRuntimeException("Create Instance from Backup on another Zone is not supported by this provider or the Backup Repository."); + throw Exceptions.cloudRuntimeException("vm.backup.cross.zone.not.supported"); } backupDao.loadDetails(backup); @@ -9846,16 +9879,16 @@ public UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws Insufficien if (serviceOfferingId != null) { serviceOffering = serviceOfferingDao.findById(serviceOfferingId); if (serviceOffering == null) { - throw new InvalidParameterValueException("Unable to find service offering: " + serviceOffering.getId()); + throw Exceptions.invalidParameterValueException("vm.backup.service.offering.not.found"); } } else { String serviceOfferingUuid = backup.getDetail(ApiConstants.SERVICE_OFFERING_ID); if (serviceOfferingUuid == null) { - throw new CloudRuntimeException("Backup doesn't contain a Service Offering UUID. Please specify a valid Service Offering while creating the Instance"); + throw Exceptions.cloudRuntimeException("vm.backup.service.offering.uuid.missing"); } serviceOffering = serviceOfferingDao.findByUuid(serviceOfferingUuid); if (serviceOffering == null) { - throw new CloudRuntimeException("Unable to find Service Offering with the UUID stored in the Backup. Please specify a valid Service Offering while creating the Instance"); + throw Exceptions.cloudRuntimeException("vm.backup.service.offering.uuid.not.found"); } } verifyServiceOffering(cmd, serviceOffering); @@ -9865,16 +9898,16 @@ public UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws Insufficien Long templateId = cmd.getTemplateId(); template = _templateDao.findById(templateId); if (template == null) { - throw new InvalidParameterValueException("Unable to use template " + templateId); + throw Exceptions.invalidParameterValueException("vm.backup.template.not.found"); } } else { String templateUuid = backup.getDetail(ApiConstants.TEMPLATE_ID); if (templateUuid == null) { - throw new CloudRuntimeException("Backup doesn't contain a Template UUID. Please specify a valid Template/ISO while creating the Instance"); + throw Exceptions.cloudRuntimeException("vm.backup.template.uuid.missing"); } template = _templateDao.findByUuid(templateUuid); if (template == null) { - throw new CloudRuntimeException("Unable to find Template with the UUID stored in the Backup. Please specify a valid Template/ISO while creating the Instance"); + throw Exceptions.cloudRuntimeException("vm.backup.template.uuid.not.found"); } } verifyTemplate(cmd, template, serviceOffering.getId()); @@ -9885,14 +9918,14 @@ public UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws Insufficien boolean isIso = template.getFormat().equals(ImageFormat.ISO); if (diskOfferingId != null) { if (!isIso) { - throw new InvalidParameterValueException(ApiConstants.DISK_OFFERING_ID + " parameter is supported for creating instance from backup only for ISO. For creating VMs with templates, please use the parameter " + ApiConstants.DATADISKS_DETAILS); + throw Exceptions.invalidParameterValueException("vm.backup.disk.offering.iso.only"); } DiskOffering diskOffering = _diskOfferingDao.findById(diskOfferingId); if (diskOffering == null) { - throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); + throw Exceptions.invalidParameterValueException("vm.backup.disk.offering.not.found"); } if (diskOffering.isComputeOnly()) { - throw new InvalidParameterValueException(String.format("The disk offering %s provided is directly mapped to a service offering, please provide an individual disk offering", diskOffering)); + throw Exceptions.invalidParameterValueException("vm.backup.disk.offering.compute.only", Map.of("diskOffering", diskOffering)); } } @@ -10102,7 +10135,7 @@ private void postProcessingUnmanageVMVolumes(List volumes, UserVmVO vm void checkUnmanagingVMOngoingVolumeSnapshots(UserVmVO vm) { logger.debug("Checking if there are any ongoing snapshots on the ROOT volumes associated with VM {}", vm); if (checkStatusOfVolumeSnapshots(vm, Volume.Type.ROOT)) { - throw new CloudRuntimeException("There is/are unbacked up snapshot(s) on ROOT volume, vm unmanage is not permitted, please try again later."); + throw Exceptions.cloudRuntimeException("vm.unmanage.unbacked.snapshots"); } logger.debug("Found no ongoing snapshots on volume of type ROOT, for the vm {}", vm); } @@ -10110,9 +10143,9 @@ void checkUnmanagingVMOngoingVolumeSnapshots(UserVmVO vm) { void checkUnmanagingVMVolumes(UserVmVO vm, List volumes) { for (VolumeVO volume : volumes) { if (volume.getInstanceId() == null || !volume.getInstanceId().equals(vm.getId())) { - throw new CloudRuntimeException(String.format("Invalid state for volume %s of VM %s: it is not attached to VM", volume, vm)); + throw Exceptions.cloudRuntimeException("vm.unmanage.volume.not.attached", Map.of("volume", volume, "instance", vm)); } else if (volume.getVolumeType() != Volume.Type.ROOT && volume.getVolumeType() != Volume.Type.DATADISK) { - throw new CloudRuntimeException(String.format("Invalid type for volume %s: ROOT or DATADISK expected but got %s", volume, volume.getVolumeType())); + throw Exceptions.cloudRuntimeException("vm.unmanage.volume.type.invalid", Map.of("volume", volume, "volumeType", volume.getVolumeType())); } } } @@ -10144,12 +10177,12 @@ private Network getNetworkForOvfNetworkMapping(DataCenter zone, Account owner) t if (zone.isSecurityGroupEnabled() || _networkModel.isSecurityGroupSupportedForZone(zone.getId())) { network = _networkModel.getNetworkWithSGWithFreeIPs(owner, zone.getId()); if (network == null) { - throw new InvalidParameterValueException("No network with security enabled is found in zone ID: " + zone.getUuid()); + throw Exceptions.invalidParameterValueException("vm.deploy.ovf.sg.network.not.found", Map.of("zone", zone)); } } else { network = getDefaultNetwork(zone, owner, true); if (network == null) { - throw new InvalidParameterValueException(String.format("Default network not found for zone ID: %s and account ID: %s", zone.getUuid(), owner.getUuid())); + throw Exceptions.invalidParameterValueException("vm.deploy.ovf.default.network.not.found", Map.of("zone", zone, "account", owner)); } } return network; diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 36905fb40ec9..11c310a78857 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -17,6 +17,68 @@ package org.apache.cloudstack.vm; +import static org.apache.cloudstack.api.ApiConstants.MAX_IOPS; +import static org.apache.cloudstack.api.ApiConstants.MIN_IOPS; +import static org.apache.cloudstack.storage.volume.VolumeImportUnmanageService.AllowImportVolumeWithBackingFile; +import static org.apache.cloudstack.vm.ImportVmTask.Step.CloningInstance; +import static org.apache.cloudstack.vm.ImportVmTask.Step.Completed; +import static org.apache.cloudstack.vm.ImportVmTask.Step.ConvertingInstance; +import static org.apache.cloudstack.vm.ImportVmTask.Step.Importing; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.admin.vm.ImportUnmanagedInstanceCmd; +import org.apache.cloudstack.api.command.admin.vm.ImportVmCmd; +import org.apache.cloudstack.api.command.admin.vm.ListImportVMTasksCmd; +import org.apache.cloudstack.api.command.admin.vm.ListUnmanagedInstancesCmd; +import org.apache.cloudstack.api.command.admin.vm.ListVmsForImportCmd; +import org.apache.cloudstack.api.command.admin.vm.UnmanageVMInstanceCmd; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UnmanagedInstanceResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.error.Exceptions; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; +import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.CheckConvertInstanceAnswer; @@ -59,7 +121,6 @@ import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientVirtualNetworkCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; @@ -140,68 +201,9 @@ import com.cloud.vm.VmDetailConstants; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.VMInstanceDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.dao.VMInstanceDetailsDao; import com.google.gson.Gson; -import org.apache.cloudstack.acl.ControlledEntity; -import org.apache.cloudstack.api.ApiCommandResourceType; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.ResponseGenerator; -import org.apache.cloudstack.api.ResponseObject; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.command.admin.vm.ImportUnmanagedInstanceCmd; -import org.apache.cloudstack.api.command.admin.vm.ImportVmCmd; -import org.apache.cloudstack.api.command.admin.vm.ListImportVMTasksCmd; -import org.apache.cloudstack.api.command.admin.vm.ListUnmanagedInstancesCmd; -import org.apache.cloudstack.api.command.admin.vm.ListVmsForImportCmd; -import org.apache.cloudstack.api.command.admin.vm.UnmanageVMInstanceCmd; -import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.api.response.UnmanagedInstanceResponse; -import org.apache.cloudstack.api.response.UserVmResponse; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; -import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.framework.config.ConfigKey; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.reservation.dao.ReservationDao; -import org.apache.cloudstack.resourcelimit.Reserver; -import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; -import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; -import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.inject.Inject; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.apache.cloudstack.api.ApiConstants.MAX_IOPS; -import static org.apache.cloudstack.api.ApiConstants.MIN_IOPS; -import static org.apache.cloudstack.storage.volume.VolumeImportUnmanageService.AllowImportVolumeWithBackingFile; -import static org.apache.cloudstack.vm.ImportVmTask.Step.CloningInstance; -import static org.apache.cloudstack.vm.ImportVmTask.Step.Completed; -import static org.apache.cloudstack.vm.ImportVmTask.Step.ConvertingInstance; -import static org.apache.cloudstack.vm.ImportVmTask.Step.Importing; public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { protected Logger logger = LogManager.getLogger(UnmanagedVMsManagerImpl.class); @@ -441,10 +443,10 @@ private boolean storagePoolSupportsDiskOffering(StoragePool pool, DiskOffering d private ServiceOfferingVO getUnmanagedInstanceServiceOffering(final UnmanagedInstanceTO instance, ServiceOfferingVO serviceOffering, final Account owner, final DataCenter zone, final Map details, Hypervisor.HypervisorType hypervisorType) throws ServerApiException, PermissionDeniedException, ResourceAllocationException { if (instance == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Cannot find VM to import."); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.vm.not.found", Collections.emptyMap()); } if (serviceOffering == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Cannot find service offering used to import VM [%s].", instance.getName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.service.offering.not.found", Map.of("name", instance.getName())); } accountService.checkAccess(owner, serviceOffering, zone); final Integer cpu = instance.getCpuCores(); @@ -452,10 +454,10 @@ private ServiceOfferingVO getUnmanagedInstanceServiceOffering(final UnmanagedIns Integer cpuSpeed = instance.getCpuSpeed() == null ? 0 : instance.getCpuSpeed(); if (cpu == null || cpu == 0) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("CPU cores [%s] is not valid for importing VM [%s].", cpu, instance.getName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.invalid.cpu", Map.of("cpu", String.valueOf(cpu), "name", instance.getName())); } if (memory == null || memory == 0) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Memory [%s] is not valid for importing VM [%s].", memory, instance.getName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.invalid.memory", Map.of("memory", String.valueOf(memory), "name", instance.getName())); } if (serviceOffering.isDynamic()) { @@ -477,13 +479,13 @@ private ServiceOfferingVO getUnmanagedInstanceServiceOffering(final UnmanagedIns serviceOffering = serviceOfferingDao.getComputeOffering(serviceOffering, parameters); } else { if (!cpu.equals(serviceOffering.getCpu()) && !instance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Service offering (%s) %d CPU cores do not match VM CPU cores %d and VM is not in powered off state (Power state: %s)", serviceOffering.getUuid(), serviceOffering.getCpu(), cpu, instance.getPowerState())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.cpu.mismatch", Map.of("offering", serviceOffering.getUuid(), "offeringCpu", serviceOffering.getCpu(), "vmCpu", cpu, "powerState", instance.getPowerState())); } if (!memory.equals(serviceOffering.getRamSize()) && !instance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Service offering (%s) %dMB memory does not match VM memory %dMB and VM is not in powered off state (Power state: %s)", serviceOffering.getUuid(), serviceOffering.getRamSize(), memory, instance.getPowerState())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.memory.mismatch", Map.of("offering", serviceOffering.getUuid(), "offeringMemory", serviceOffering.getRamSize(), "vmMemory", memory, "powerState", instance.getPowerState())); } if (hypervisorType == Hypervisor.HypervisorType.VMware && cpuSpeed != null && cpuSpeed > 0 && !cpuSpeed.equals(serviceOffering.getSpeed()) && !instance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Service offering (%s) %dMHz CPU speed does not match VM CPU speed %dMHz and VM is not in powered off state (Power state: %s)", serviceOffering.getUuid(), serviceOffering.getSpeed(), cpuSpeed, instance.getPowerState())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.cpu.speed.mismatch", Map.of("offering", serviceOffering.getUuid(), "offeringSpeed", serviceOffering.getSpeed(), "vmSpeed", cpuSpeed, "powerState", instance.getPowerState())); } } return serviceOffering; @@ -499,7 +501,7 @@ private Map getNicIpAddresses(final List 1) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Multiple IP addresses (%s, %s) present for nic ID: %s. IP address cannot be assigned automatically, only single IP address auto-assigning supported", nic.getIpAddress().get(0), nic.getIpAddress().get(1), nic.getNicId())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.nic.multiple.ips", Map.of("ip1", nic.getIpAddress().get(0), "ip2", nic.getIpAddress().get(1), "nic", nic.getNicId())); } String address = nic.getIpAddress().get(0); if (NetUtils.isValidIp4(address)) { @@ -545,7 +547,7 @@ private StoragePool getStoragePool(final UnmanagedInstanceTO.Disk disk, final Da } } if (storagePool == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Storage pool for disk %s(%s) with datastore: %s not found in zone ID: %s", disk.getLabel(), disk.getDiskId(), disk.getDatastoreName(), zone.getUuid())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.disk.storage.pool.not.found", Map.of("diskLabel", disk.getLabel(), "diskId", disk.getDiskId(), "datastore", disk.getDatastoreName(), "zone", zone.getUuid())); } return storagePool; } @@ -558,9 +560,9 @@ private Pair> getRootAn Set callerDiskIds = dataDiskOfferingMap.keySet(); if (callerDiskIds.size() != disks.size() - 1) { - String msg = String.format("VM has total %d disks for which %d disk offering mappings provided. %d disks need a disk offering for import", disks.size(), callerDiskIds.size(), disks.size() - 1); - logger.error(String.format("%s. %s parameter can be used to provide disk offerings for the disks", msg, ApiConstants.DATADISK_OFFERING_LIST)); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); + logger.error("VM has total {} disks for which {} disk offering mappings provided. {} disks need a disk offering for import. {} parameter can be used to provide disk offerings for the disks", + disks.size(), callerDiskIds.size(), disks.size() - 1, ApiConstants.DATADISK_OFFERING_LIST); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.disk.offering.mapping.mismatch", Map.of("total", disks.size(), "provided", callerDiskIds.size(), "needed", disks.size() - 1)); } List diskIdsWithoutOffering = new ArrayList<>(); for (UnmanagedInstanceTO.Disk disk : disks) { @@ -577,7 +579,7 @@ private Pair> getRootAn } } if (diskIdsWithoutOffering.size() > 1 || rootDisk == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM has total %d disks, disk offering mapping not provided for %d disks. Disk IDs that may need a disk offering - %s", disks.size(), diskIdsWithoutOffering.size() - 1, String.join(", ", diskIdsWithoutOffering))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.disk.missing.offering", Map.of("total", disks.size(), "missing", diskIdsWithoutOffering.size() - 1, "diskIds", String.join(", ", diskIdsWithoutOffering))); } return new Pair<>(rootDisk, dataDisks); @@ -586,24 +588,24 @@ private Pair> getRootAn private void checkUnmanagedDiskAndOfferingForImport(String instanceName, UnmanagedInstanceTO.Disk disk, DiskOffering diskOffering, ServiceOffering serviceOffering, final Account owner, final DataCenter zone, final Cluster cluster, final boolean migrateAllowed, List reservations) throws ServerApiException, PermissionDeniedException, ResourceAllocationException { if (serviceOffering == null && diskOffering == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Disk offering for disk ID [%s] not found during VM [%s] import.", disk.getDiskId(), instanceName)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.disk.offering.not.found", Map.of("diskId", disk.getDiskId(), "instance", instanceName)); } if (diskOffering != null) { accountService.checkAccess(owner, diskOffering, zone); } if (disk.getCapacity() == null || disk.getCapacity() == 0) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Size of disk(ID: %s) is found invalid during VM import", disk.getDiskId())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.disk.size.invalid", Map.of("diskId", disk.getDiskId())); } if (diskOffering != null && !diskOffering.isCustomized() && diskOffering.getDiskSize() == 0) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Size of fixed disk offering(ID: %s) is found invalid during VM import", diskOffering.getUuid())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.disk.offering.size.invalid", Map.of("offering", diskOffering.getUuid())); } if (diskOffering != null && !diskOffering.isCustomized() && diskOffering.getDiskSize() < disk.getCapacity()) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Size of disk offering(ID: %s) %dGB is found less than the size of disk(ID: %s) %dGB during VM import", diskOffering.getUuid(), (diskOffering.getDiskSize() / Resource.ResourceType.bytesToGiB), disk.getDiskId(), (disk.getCapacity() / (Resource.ResourceType.bytesToGiB)))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.disk.offering.too.small", Map.of("offering", diskOffering.getUuid(), "offeringSize", diskOffering.getDiskSize() / Resource.ResourceType.bytesToGiB, "diskId", disk.getDiskId(), "diskSize", disk.getCapacity() / Resource.ResourceType.bytesToGiB)); } diskOffering = diskOffering != null ? diskOffering : diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); StoragePool storagePool = getStoragePool(disk, zone, cluster, diskOffering); if (diskOffering != null && !migrateAllowed && !storagePoolSupportsDiskOffering(storagePool, diskOffering)) { - throw new InvalidParameterValueException(String.format("Disk offering: %s is not compatible with storage pool: %s of unmanaged disk: %s", diskOffering.getUuid(), storagePool.getUuid(), disk.getDiskId())); + throw Exceptions.invalidParameterValueException("vm.import.disk.offering.pool.incompatible", Map.of("offering", diskOffering.getUuid(), "pool", storagePool.getUuid(), "diskId", disk.getDiskId())); } resourceLimitService.checkVolumeResourceLimit(owner, true, disk.getCapacity(), diskOffering, reservations); } @@ -613,16 +615,16 @@ private void checkUnmanagedDiskAndOfferingForImport(String intanceName, List hostNames = vmDao.listDistinctHostNames(network.getId()); if (CollectionUtils.isNotEmpty(hostNames) && hostNames.contains(hostName)) { - throw new InvalidParameterValueException(String.format("VM with Name [%s] already exists in the network [%s] domain [%s]. Cannot import another VM with the same name. Please try again with a different name.", hostName, network, network.getNetworkDomain())); + throw Exceptions.invalidParameterValueException("vm.import.hostname.duplicate", Map.of("name", hostName, "network", network.toString(), "domain", network.getNetworkDomain())); } } @@ -679,13 +681,13 @@ private void checkUnmanagedNicIpAndNetworkForImport(String instanceName, Unmanag basicNetworkChecks(instanceName, nic, network); // Check IP is assigned for non L2 networks if (!network.getGuestType().equals(Network.GuestType.L2) && (ipAddresses == null || StringUtils.isEmpty(ipAddresses.getIp4Address()))) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("NIC(ID: %s) needs a valid IP address for it to be associated with network(ID: %s). %s parameter of API can be used for this", nic.getNicId(), network.getUuid(), ApiConstants.NIC_IP_ADDRESS_LIST)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.nic.ip.required", Map.of("nic", nic.getNicId(), "network", network.getUuid(), "param", ApiConstants.NIC_IP_ADDRESS_LIST)); } // If network is non L2, IP v4 is assigned and not set to auto-assign, check it is available for network if (!network.getGuestType().equals(Network.GuestType.L2) && ipAddresses != null && StringUtils.isNotEmpty(ipAddresses.getIp4Address()) && !ipAddresses.getIp4Address().equals("auto")) { Set ips = networkModel.getAvailableIps(network, ipAddresses.getIp4Address()); if (CollectionUtils.isEmpty(ips) || !ips.contains(NetUtils.ip2Long(ipAddresses.getIp4Address()))) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("IP address %s for NIC(ID: %s) is not available in network(ID: %s)", ipAddresses.getIp4Address(), nic.getNicId(), network.getUuid())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.nic.ip.not.available", Map.of("ip", ipAddresses.getIp4Address(), "nic", nic.getNicId(), "network", network.getUuid())); } } } @@ -701,7 +703,7 @@ private Map getUnmanagedNicNetworkMap(String instanceName, List getUnmanagedNicNetworkMap(String instanceName, List result = networkOrchestrationService.importNic(nic.getMacAddress(), deviceId, network, isDefaultNic, vm, ipAddresses, dataCenterVO, forced); if (result == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("NIC ID: %s import failed", nic.getNicId())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.nic.import.failed", Map.of("nic", nic.getNicId())); } return result.first(); } @@ -877,13 +879,13 @@ private void cleanupFailedImportVM(final UserVm userVm) { private UserVm migrateImportedVM(HostVO sourceHost, VirtualMachineTemplate template, ServiceOfferingVO serviceOffering, UserVm userVm, final Account owner, List> diskProfileStoragePoolList) { UserVm vm = userVm; if (vm == null) { - logger.error(String.format("Failed to check migrations need during VM import")); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to check migrations need during VM import")); + logger.error("Failed to check migrations need during VM import"); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.migration.check.failed", Collections.emptyMap()); } if (sourceHost == null || serviceOffering == null || diskProfileStoragePoolList == null) { logger.error(String.format("Failed to check migrations need during import, VM: %s", userVm)); cleanupFailedImportVM(vm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to check migrations need during import, VM: %s", userVm.getInstanceName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.migration.check.failed.vm", Map.of("vm", userVm.getInstanceName())); } if (!hostSupportsServiceOfferingAndTemplate(sourceHost, serviceOffering, template)) { logger.debug("VM {} needs to be migrated", vm); @@ -899,11 +901,11 @@ private UserVm migrateImportedVM(HostVO sourceHost, VirtualMachineTemplate templ String errorMsg = String.format("VM import failed for Unmanaged VM [%s] during VM migration, cannot find deployment destination due to [%s].", vm, e.getMessage()); logger.warn(errorMsg, e); cleanupFailedImportVM(vm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.migration.destination.error", Map.of("vm", vm.toString(), "error", e.getMessage())); } if (dest == null) { cleanupFailedImportVM(vm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed for unmanaged vm: %s during vm migration, no deployment destination found", vm.getInstanceName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.migration.destination.not.found", Map.of("vm", vm.getInstanceName())); } try { if (vm.getState().equals(VirtualMachine.State.Stopped)) { @@ -919,7 +921,7 @@ private UserVm migrateImportedVM(HostVO sourceHost, VirtualMachineTemplate templ String errorMsg = String.format("VM import failed for Unmanaged VM [%s] during VM migration due to [%s].", vm, e.getMessage()); logger.error(errorMsg, e); cleanupFailedImportVM(vm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.migration.failed", Map.of("vm", vm.toString(), "error", e.getMessage())); } } for (Pair diskProfileStoragePool : diskProfileStoragePoolList) { @@ -945,7 +947,7 @@ private UserVm migrateImportedVM(HostVO sourceHost, VirtualMachineTemplate templ Pair, List> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(profile.getVolumeId(), null, null, null, null, false, true); if (CollectionUtils.isEmpty(poolsPair.first()) && CollectionUtils.isEmpty(poolsPair.second())) { cleanupFailedImportVM(vm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed for unmanaged vm: %s during volume ID: %s migration as no suitable pool(s) found", userVm.getInstanceName(), volumeVO.getUuid())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.volume.migration.no.pool", Map.of("vm", userVm.getInstanceName(), "volume", volumeVO.getUuid())); } List storagePools = poolsPair.second(); StoragePool storagePool = null; @@ -973,7 +975,7 @@ private UserVm migrateImportedVM(HostVO sourceHost, VirtualMachineTemplate templ } if (storagePool == null) { cleanupFailedImportVM(vm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed for unmanaged vm: %s during volume ID: %s migration as no suitable pool found", userVm.getInstanceName(), volumeVO.getUuid())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.volume.migration.no.pool.found", Map.of("vm", userVm.getInstanceName(), "volume", volumeVO.getUuid())); } else { logger.debug("Found storage pool {} for migrating the volume {} to", storagePool, volumeVO); } @@ -997,7 +999,7 @@ private UserVm migrateImportedVM(HostVO sourceHost, VirtualMachineTemplate templ } catch (Exception e) { logger.error("VM import failed for unmanaged vm: {} during volume migration", vm, e); cleanupFailedImportVM(vm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed for unmanaged vm: %s during volume migration. %s", userVm.getInstanceName(), StringUtils.defaultString(e.getMessage()))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.volume.migration.failed", Map.of("vm", userVm.getInstanceName(), "error", StringUtils.defaultString(e.getMessage()))); } } return userVm; @@ -1007,7 +1009,7 @@ private void publishVMUsageUpdateResourceCount(final UserVm userVm, ServiceOffer if (userVm == null || serviceOfferingVO == null) { logger.error("Failed to publish usage records during VM import because VM [{}] or ServiceOffering [{}] is null.", userVm, serviceOfferingVO); cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "VM import failed for Unmanaged VM during publishing Usage Records."); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.usage.publish.failed", Collections.emptyMap()); } try { if (!serviceOfferingVO.isDynamic()) { @@ -1024,7 +1026,7 @@ private void publishVMUsageUpdateResourceCount(final UserVm userVm, ServiceOffer } catch (Exception e) { logger.error("Failed to publish usage records during VM import for unmanaged VM [{}] due to [{}].", userVm, e.getMessage(), e); cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed for unmanaged vm %s during publishing usage records", userVm.getInstanceName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.usage.publish.failed.vm", Map.of("vm", userVm.getInstanceName())); } resourceLimitService.incrementVmResourceCount(userVm.getAccountId(), userVm.isDisplayVm(), serviceOfferingVO, templateVO); // Save usage event and update resource count for user vm volumes @@ -1066,7 +1068,7 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI } catch (Exception e) { String errorMsg = String.format("Failed to import Unmanaged VM [%s] because the service offering [%s] is not compatible due to [%s].", unmanagedInstance, serviceOffering, StringUtils.defaultIfEmpty(e.getMessage(), "")); logger.error(errorMsg, e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.service.offering.incompatible", Map.of("vm", unmanagedInstance.toString(), "offering", serviceOffering.toString(), "error", StringUtils.defaultIfEmpty(e.getMessage(), ""))); } String internalCSName = unmanagedInstance.getInternalCSName(); @@ -1083,18 +1085,18 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI } if (!migrateAllowed && host != null && !hostSupportsServiceOfferingAndTemplate(host, validatedServiceOffering, template)) { - throw new InvalidParameterValueException(String.format("Service offering: %s or template: %s is not compatible with host: %s of unmanaged VM: %s", serviceOffering.getUuid(), template.getUuid(), host.getUuid(), displayName)); + throw Exceptions.invalidParameterValueException("vm.import.host.offering.template.incompatible", Map.of("offering", serviceOffering.getUuid(), "template", template.getUuid(), "host", host.getUuid(), "vm", displayName)); } // Check disks and supplied disk offerings List unmanagedInstanceDisks = unmanagedInstance.getDisks(); if (CollectionUtils.isEmpty(unmanagedInstanceDisks)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("No attached disks found for the unmanaged VM: %s", displayName)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.no.disks.found", Map.of("vm", displayName)); } Pair> rootAndDataDisksPair = getRootAndDataDisks(unmanagedInstanceDisks, dataDiskOfferingMap); final UnmanagedInstanceTO.Disk rootDisk = rootAndDataDisksPair.first(); final List dataDisks = rootAndDataDisksPair.second(); if (rootDisk == null || StringUtils.isEmpty(rootDisk.getController())) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed. Unable to retrieve root disk details for VM: %s ", displayName)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.root.disk.not.found", Map.of("vm", displayName)); } if (cluster.getHypervisorType() == Hypervisor.HypervisorType.KVM) { Long rootDiskOfferingId = validatedServiceOffering.getDiskOfferingId(); @@ -1143,16 +1145,16 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI } catch (InsufficientCapacityException ice) { String errorMsg = String.format("Failed to import VM [%s] due to [%s].", displayName, ice.getMessage()); logger.error(errorMsg, ice); - throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, errorMsg); + throw Exceptions.serverApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, "vm.import.insufficient.capacity", Map.of("vm", displayName, "error", ice.getMessage())); } if (userVm == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", displayName)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.vm.create.failed", Map.of("name", displayName)); } List> diskProfileStoragePoolList = new ArrayList<>(); try { if (rootDisk.getCapacity() == null || rootDisk.getCapacity() == 0) { - throw new InvalidParameterValueException(String.format("Root disk ID: %s size is invalid", rootDisk.getDiskId())); + throw Exceptions.invalidParameterValueException("vm.import.root.disk.size.invalid", Map.of("diskId", rootDisk.getDiskId())); } Long minIops = null; if (details.containsKey(MIN_IOPS)) { @@ -1168,7 +1170,7 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI long deviceId = 1L; for (UnmanagedInstanceTO.Disk disk : dataDisks) { if (disk.getCapacity() == null || disk.getCapacity() == 0) { - throw new InvalidParameterValueException(String.format("Disk ID: %s size is invalid", rootDisk.getDiskId())); + throw Exceptions.invalidParameterValueException("vm.import.data.disk.size.invalid", Map.of("id", rootDisk.getDiskId())); } DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId())); diskProfileStoragePoolList.add(importDisk(disk, userVm, cluster, offering, Volume.Type.DATADISK, String.format("DATA-%d-%s", userVm.getId(), disk.getDiskId()), @@ -1179,7 +1181,7 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI } catch (Exception e) { logger.error(String.format("Failed to import volumes while importing vm: %s", displayName), e); cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", displayName, StringUtils.defaultString(e.getMessage()))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.volumes.failed", Map.of("vm", displayName, "error", StringUtils.defaultString(e.getMessage()))); } try { int nicIndex = 0; @@ -1192,7 +1194,7 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI } catch (Exception e) { logger.error(String.format("Failed to import NICs while importing vm: %s", displayName), e); cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import NICs while importing vm: %s. %s", displayName, StringUtils.defaultString(e.getMessage()))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.nics.failed", Map.of("vm", displayName, "error", StringUtils.defaultString(e.getMessage()))); } if (migrateAllowed) { userVm = migrateImportedVM(host, template, validatedServiceOffering, userVm, owner, diskProfileStoragePoolList); @@ -1202,7 +1204,7 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI } catch (ResourceAllocationException e) { // This will be thrown by checkUnmanagedDiskAndOfferingForImport, so the VM was not imported yet logger.error("Volume resource allocation error for owner: {}", owner, e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resource allocation error for owner: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.volume.allocation.error", Map.of("owner", owner.getUuid(), "error", StringUtils.defaultString(e.getMessage()))); } finally { ReservationHelper.closeAll(reservations); } @@ -1238,15 +1240,15 @@ protected Cluster basicAccessChecks(Long clusterId) { throw new PermissionDeniedException(String.format("Cannot perform this operation, caller account [%s] is not ROOT Admin.", caller.getUuid())); } if (clusterId == null) { - throw new InvalidParameterValueException("Cluster ID cannot be null."); + throw Exceptions.invalidParameterValueException("vm.import.cluster.id.null"); } final Cluster cluster = clusterDao.findById(clusterId); if (cluster == null) { - throw new InvalidParameterValueException(String.format("Cluster with ID [%d] cannot be found.", clusterId)); + throw Exceptions.invalidParameterValueException("vm.import.cluster.not.found", Map.of("clusterId", clusterId)); } if (!importUnmanagedInstancesSupportedHypervisors.contains(cluster.getHypervisorType())) { - throw new InvalidParameterValueException(String.format("VM import is currently not supported for hypervisor [%s].", cluster.getHypervisorType().toString())); + throw Exceptions.invalidParameterValueException("vm.import.hypervisor.not.supported", Map.of("hypervisor", cluster.getHypervisorType().toString())); } return cluster; } @@ -1299,7 +1301,7 @@ private UserVmResponse baseImportInstance(ImportUnmanagedInstanceCmd cmd) { Cluster cluster = basicAccessChecks(clusterId); if (!cluster.getAllocationState().equals(Cluster.AllocationState.Enabled)) { - throw new InvalidParameterValueException(String.format("Cluster [%s] is not enabled.", cluster)); + throw Exceptions.invalidParameterValueException("vm.import.cluster.not.enabled", Map.of("cluster", cluster.toString())); } final Account caller = CallContext.current().getCallingAccount(); @@ -1356,13 +1358,13 @@ private UserVmResponse baseImportInstance(ImportUnmanagedInstanceCmd cmd) { } catch (ResourceAllocationException e) { logger.error(String.format("VM resource allocation error for account: %s", owner.getUuid()), e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.vm.allocation.error", Map.of("owner", owner.getUuid(), "error", StringUtils.defaultString(e.getMessage()))); } if (userVm == null) { ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_VM_IMPORT, cmd.getEventDescription(), null, null, 0); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to find unmanaged vm with name: %s in cluster: %s", instanceName, cluster.getUuid())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.unmanaged.vm.not.found", Map.of("name", instanceName, "cluster", cluster.getUuid())); } ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_INFO, EventTypes.EVENT_VM_IMPORT, cmd.getEventDescription(), userVm.getId(), ApiCommandResourceType.VirtualMachine.toString(), 0); @@ -1374,13 +1376,13 @@ protected void checkExtraParamsAllowed(String extraParams) { return; } if (BooleanUtils.isFalse(ConvertVmwareInstanceToKvmExtraParamsAllowed.value())) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, - "Extra parameters for Vmware to KVM conversion are disabled by the administrator"); + throw Exceptions.serverApiException(ApiErrorCode.PARAM_ERROR, + "vm.import.extra.params.disabled", Collections.emptyMap()); } String allowedParamsStr = ConvertVmwareInstanceToKvmExtraParamsAllowedList.value(); if (StringUtils.isBlank(allowedParamsStr)) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, - "Extra parameters for Vmware to KVM conversion are enabled but the allowed list of parameters is empty"); + throw Exceptions.serverApiException(ApiErrorCode.PARAM_ERROR, + "vm.import.extra.params.list.empty", Collections.emptyMap()); } List allowedParams = Arrays.asList(allowedParamsStr.split(",")); List sanitizedParams = Arrays.asList(extraParams.split(" ")) @@ -1390,8 +1392,8 @@ protected void checkExtraParamsAllowed(String extraParams) { .collect(Collectors.toList()); for (String param : sanitizedParams) { if (!allowedParams.contains(param)) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, - String.format("The parameter %s is not allowed by the administrator", param)); + throw Exceptions.serverApiException(ApiErrorCode.PARAM_ERROR, + "vm.import.extra.param.not.allowed", Map.of("param", param)); } } } @@ -1407,10 +1409,10 @@ private long getUserIdForImportInstance(Account owner) { protected void basicParametersCheckForImportInstance(String name, Long domainId, String accountName) { if (StringUtils.isEmpty(name)) { - throw new InvalidParameterValueException("Instance name cannot be empty"); + throw Exceptions.invalidParameterValueException("vm.import.instance.name.empty"); } if (domainId != null && StringUtils.isEmpty(accountName)) { - throw new InvalidParameterValueException(String.format("%s parameter must be specified with %s parameter", ApiConstants.DOMAIN_ID, ApiConstants.ACCOUNT)); + throw Exceptions.invalidParameterValueException("vm.import.domain.account.required", Map.of("domainParam", ApiConstants.DOMAIN_ID, "accountParam", ApiConstants.ACCOUNT)); } } @@ -1421,7 +1423,7 @@ private void checkVmwareInstanceNameForImportInstance(Hypervisor.HypervisorType // In case of VMware since VM name must be unique within a DC, check if VM with the same hostname already exists in the zone. VMInstanceVO vmByHostName = vmDao.findVMByHostNameInZone(hostName, zone.getId()); if (vmByHostName != null && vmByHostName.getState() != VirtualMachine.State.Expunging) { - throw new InvalidParameterValueException(String.format("Failed to import VM: %s. There already exists a VM by the hostname: %s in zone: %s", instanceName, hostName, zone.getUuid())); + throw Exceptions.invalidParameterValueException("vm.import.hostname.exists", Map.of("instance", instanceName, "hostname", hostName, "zone", zone.getUuid())); } } } @@ -1431,12 +1433,11 @@ private String getHostNameForImportInstance(String hostName, Hypervisor.Hypervis if (StringUtils.isEmpty(hostName)) { hostName = hypervisorType == Hypervisor.HypervisorType.VMware ? instanceName : displayName; if (!NetUtils.verifyDomainNameLabel(hostName, true)) { - throw new InvalidParameterValueException("Please provide a valid hostname for the VM. VM name contains unsupported characters that cannot be used as hostname."); + throw Exceptions.invalidParameterValueException("vm.import.hostname.invalid.chars"); } } if (!NetUtils.verifyDomainNameLabel(hostName, true)) { - throw new InvalidParameterValueException("Invalid VM hostname. VM hostname can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit"); + throw Exceptions.invalidParameterValueException("vm.import.hostname.invalid"); } return hostName; } @@ -1447,11 +1448,11 @@ private String getDisplayNameForImportInstance(String displayName, String instan private ServiceOfferingVO getServiceOfferingForImportInstance(Long serviceOfferingId, Account owner, DataCenter zone) { if (serviceOfferingId == null) { - throw new InvalidParameterValueException("Service offering ID cannot be null"); + throw Exceptions.invalidParameterValueException("vm.import.service.offering.id.null"); } final ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(serviceOfferingId); if (serviceOffering == null) { - throw new InvalidParameterValueException(String.format("Service offering ID: %d cannot be found", serviceOfferingId)); + throw Exceptions.invalidParameterValueException("vm.import.service.offering.id.not.found", Map.of("id", serviceOfferingId)); } accountService.checkAccess(owner, serviceOffering, zone); return serviceOffering; @@ -1466,14 +1467,14 @@ protected VMTemplateVO getTemplateForImportInstance(Long templateId, Hypervisor. if (template == null) { template = createDefaultDummyVmImportTemplate(isKVMHypervisor); if (template == null) { - throw new InvalidParameterValueException(String.format("Default VM import template with unique name: %s for hypervisor: %s cannot be created. Please use templateid parameter for import", templateName, hypervisorType.toString())); + throw Exceptions.invalidParameterValueException("vm.import.default.template.create.failed", Map.of("templateName", templateName, "hypervisor", hypervisorType.toString())); } } } else { template = templateDao.findById(templateId); } if (template == null) { - throw new InvalidParameterValueException(String.format("Template ID: %d cannot be found", templateId)); + throw Exceptions.invalidParameterValueException("vm.import.template.not.found", Map.of("id", templateId)); } return template; } @@ -1511,11 +1512,11 @@ private UserVm importUnmanagedInstanceFromHypervisor(DataCenter zone, Cluster cl } UnmanagedInstanceTO unmanagedInstance = unmanagedInstances.get(name); if (unmanagedInstance == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to retrieve details for unmanaged VM: %s", name)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.unmanaged.vm.details.not.found", Map.of("name", name)); } if (template.getName().equals(VM_IMPORT_DEFAULT_TEMPLATE_NAME) && cluster.getHypervisorType().equals(Hypervisor.HypervisorType.KVM)) { - throw new InvalidParameterValueException("Template is needed and unable to use default template for hypervisor " + host.getHypervisorType().toString()); + throw Exceptions.invalidParameterValueException("vm.import.kvm.default.template.incompatible", Map.of("hypervisor", host.getHypervisorType().toString())); } if (template.getName().equals(VM_IMPORT_DEFAULT_TEMPLATE_NAME)) { @@ -1534,9 +1535,9 @@ private UserVm importUnmanagedInstanceFromHypervisor(DataCenter zone, Cluster cl } if (guestOSHypervisor == null) { if (guestOS != null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to find hypervisor guest OS ID: %s details for unmanaged VM: %s for hypervisor: %s version: %s. templateid parameter can be used to assign template for VM", guestOS.getUuid(), name, host.getHypervisorType().toString(), host.getHypervisorVersion())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.guest.os.hypervisor.not.found", Map.of("osId", guestOS.getUuid(), "name", name, "hypervisor", host.getHypervisorType().toString(), "version", host.getHypervisorVersion())); } - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to retrieve guest OS details for unmanaged VM: %s with OS name: %s, OS ID: %s for hypervisor: %s version: %s. templateid parameter can be used to assign template for VM", name, osName, unmanagedInstance.getOperatingSystemId(), host.getHypervisorType().toString(), host.getHypervisorVersion())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.guest.os.not.found", Map.of("name", name, "osName", osName, "osId", unmanagedInstance.getOperatingSystemId(), "hypervisor", host.getHypervisorType().toString(), "version", host.getHypervisorVersion())); } template.setGuestOSId(guestOSHypervisor.getGuestOsId()); @@ -1574,10 +1575,10 @@ protected void checkVmResourceLimitsForUnmanagedInstanceImport(Account owner, Un } if (cpu == null || cpu == 0) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("CPU cores [%s] is not valid for importing VM [%s].", cpu, unmanagedInstance.getName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.invalid.cpu", Map.of("cpu", String.valueOf(cpu), "name", unmanagedInstance.getName())); } if (memory == null || memory == 0) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Memory [%s] is not valid for importing VM [%s].", memory, unmanagedInstance.getName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.invalid.memory", Map.of("memory", String.valueOf(memory), "name", unmanagedInstance.getName())); } List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); @@ -1666,17 +1667,16 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster boolean useVddk = cmd.getUseVddk(); if ((existingVcenterId == null && vcenter == null) || (existingVcenterId != null && vcenter != null)) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, - "Please provide an existing vCenter ID or a vCenter IP/Name, parameters are mutually exclusive"); + throw Exceptions.serverApiException(ApiErrorCode.PARAM_ERROR, + "vm.import.vcenter.param.exclusive", Collections.emptyMap()); } if (existingVcenterId == null && StringUtils.isAnyBlank(vcenter, datacenterName, username, password)) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, - "Please set all the information for a vCenter IP/Name, datacenter, username and password"); + throw Exceptions.serverApiException(ApiErrorCode.PARAM_ERROR, + "vm.import.vcenter.params.incomplete", Collections.emptyMap()); } if (forceMsToImportVmFiles && useVddk) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, - String.format("Parameters %s and %s are mutually exclusive", - ApiConstants.FORCE_MS_TO_IMPORT_VM_FILES, ApiConstants.USE_VDDK)); + throw Exceptions.serverApiException(ApiErrorCode.PARAM_ERROR, + "vm.import.force.ms.vddk.exclusive", Map.of("param1", ApiConstants.FORCE_MS_TO_IMPORT_VM_FILES, "param2", ApiConstants.USE_VDDK)); } checkConversionStoragePool(convertStoragePoolId, forceConvertToPool); @@ -1780,7 +1780,7 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster importVmTasksManager.updateImportVMTaskErrorState(importVMTask, ImportVmTask.TaskState.Failed, e.getMessage()); ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_VM_IMPORT, cmd.getEventDescription(), null, null, 0); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.error", Map.of("error", e.getMessage())); } finally { if (isClonedInstance && sourceVMwareInstance != null) { removeClonedInstance(vcenter, datacenterName, username, password, sourceHostName, sourceVMwareInstance.getName(), sourceVMName); @@ -1830,26 +1830,27 @@ protected void validateSelectedConversionStoragePoolForVddk(boolean useVddk, Lon if (serviceOffering.getDiskOfferingId() != null) { DiskOfferingVO rootDiskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); if (rootDiskOffering == null) { - throw new InvalidParameterValueException(String.format("Cannot find disk offering with ID %s that belongs to the service offering %s", - serviceOffering.getDiskOfferingId(), serviceOffering.getName())); + throw Exceptions.invalidParameterValueException("vm.import.vddk.root.disk.offering.not.found", + Map.of("id", serviceOffering.getDiskOfferingId(), "offering", serviceOffering.getName())); } if (!volumeApiService.doesStoragePoolSupportDiskOffering(selectedStoragePool, rootDiskOffering)) { - throw new InvalidParameterValueException(String.format("The root disk offering '%s' is not supported by the selected conversion storage pool '%s'. " + - "When using VDDK, all selected disk offerings must be compatible with the conversion storage pool, as it will become the primary storage for the imported volumes.", - rootDiskOffering.getName(), selectedStoragePool.getName())); + throw Exceptions.invalidParameterValueException("vm.import.vddk.root.disk.offering.incompatible", + Map.of("offering", rootDiskOffering, "pool", selectedStoragePool)); } } if (MapUtils.isNotEmpty(dataDiskOfferingMap)) { - for (Long diskOfferingId : dataDiskOfferingMap.values()) { + for (Map.Entry entry : dataDiskOfferingMap.entrySet()) { + Long diskOfferingId = entry.getValue(); DiskOfferingVO diskOffering = diskOfferingDao.findById(diskOfferingId); if (diskOffering == null) { - throw new InvalidParameterValueException(String.format("Cannot find disk offering with ID %s", diskOfferingId)); + throw Exceptions.invalidParameterValueException("vm.import.vddk.data.disk.offering.not.found", + Map.of("disk", entry.getKey(), "id", diskOfferingId)); } if (!volumeApiService.doesStoragePoolSupportDiskOffering(selectedStoragePool, diskOffering)) { - throw new InvalidParameterValueException(String.format("The data disk offering '%s' is not supported by the selected conversion storage pool '%s'. " + - "When using VDDK, all selected disk offerings must be compatible with the conversion storage pool, as it will become the primary storage for the imported volumes.", - diskOffering.getName(), selectedStoragePool.getName())); + throw Exceptions.invalidParameterValueException("vm.import.vddk.data.disk.offering.incompatible", + Map.of("offering", diskOffering, "pool", selectedStoragePool)); + } } } @@ -2459,7 +2460,7 @@ private boolean hasVolumeSnapshotsPriorToUnmanageVM(VMInstanceVO vmVO) { private boolean hasISOAttached(VMInstanceVO vmVO) { UserVmVO userVM = userVmDao.findById(vmVO.getId()); if (userVM == null) { - throw new InvalidParameterValueException("Could not find user VM with ID = " + vmVO.getUuid()); + throw Exceptions.invalidParameterValueException("vm.unmanage.user.vm.not.found", Map.of("id", vmVO.getUuid())); } return userVM.getIsoId() != null; } @@ -2492,9 +2493,9 @@ private Long findSuitableHostId(VMInstanceVO vmVO) { public Pair unmanageVMInstance(long vmId, Long paramHostId, boolean isForced) { VMInstanceVO vmVO = vmDao.findById(vmId); if (vmVO == null || vmVO.getRemoved() != null) { - throw new InvalidParameterValueException("Could not find VM to unmanage, it is either removed or not existing VM"); + throw Exceptions.invalidParameterValueException("vm.unmanage.vm.not.found"); } else if (vmVO.getState() != VirtualMachine.State.Running && vmVO.getState() != VirtualMachine.State.Stopped) { - throw new InvalidParameterValueException("VM with id = " + vmVO.getUuid() + " must be running or stopped to be unmanaged"); + throw Exceptions.invalidParameterValueException("vm.unmanage.vm.invalid.state", Map.of("id", vmVO.getUuid())); } else if (!UnmanagedVMsManager.isSupported(vmVO.getHypervisorType())) { throw new UnsupportedServiceException("Unmanage VM is currently not allowed for hypervisor " + vmVO.getHypervisorType().toString()); @@ -2511,7 +2512,7 @@ public Pair unmanageVMInstance(long vmId, Long paramHostId, boo if (vmVO.getType().equals(VirtualMachine.Type.User)) { UserVmVO userVm = userVmDao.findById(vmId); if (UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) { - throw new InvalidParameterValueException("Operation not supported on Shared FileSystem Instance"); + throw Exceptions.invalidParameterValueException("vm.unmanage.sharedfs.not.supported"); } } @@ -2553,19 +2554,19 @@ private UserVmResponse importKvmInstance(ImportVmCmd cmd) { final Long zoneId = cmd.getZoneId(); final DataCenterVO zone = dataCenterDao.findById(zoneId); if (zone == null) { - throw new InvalidParameterValueException("Please specify a valid zone."); + throw Exceptions.invalidParameterValueException("vm.import.zone.invalid"); } final String hypervisorType = cmd.getHypervisor(); if (!Hypervisor.HypervisorType.KVM.toString().equalsIgnoreCase(hypervisorType)) { - throw new InvalidParameterValueException(String.format("VM import is currently not supported for hypervisor: %s", hypervisorType)); + throw Exceptions.invalidParameterValueException("vm.import.kvm.hypervisor.not.supported", Map.of("hypervisor", hypervisorType)); } final String instanceName = cmd.getName(); if (StringUtils.isEmpty(instanceName)) { - throw new InvalidParameterValueException(String.format("Instance name cannot be empty")); + throw Exceptions.invalidParameterValueException("vm.import.instance.name.empty"); } if (cmd.getDomainId() != null && StringUtils.isEmpty(cmd.getAccountName())) { - throw new InvalidParameterValueException("domainid parameter must be specified with account parameter"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.domain.account.required"); } final Account owner = accountService.getActiveAccountById(cmd.getEntityOwnerId()); long userId = CallContext.current().getCallingUserId(); @@ -2576,11 +2577,11 @@ private UserVmResponse importKvmInstance(ImportVmCmd cmd) { VMTemplateVO template = getTemplateForImportInstance(cmd.getTemplateId(), Hypervisor.HypervisorType.KVM); final Long serviceOfferingId = cmd.getServiceOfferingId(); if (serviceOfferingId == null) { - throw new InvalidParameterValueException(String.format("Service offering ID cannot be null")); + throw Exceptions.invalidParameterValueException("vm.import.service.offering.id.null"); } final ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(serviceOfferingId); if (serviceOffering == null) { - throw new InvalidParameterValueException(String.format("Service offering ID: %d cannot be found", serviceOfferingId)); + throw Exceptions.invalidParameterValueException("vm.import.service.offering.id.not.found", Map.of("id", serviceOfferingId)); } accountService.checkAccess(owner, serviceOffering, zone); String displayName = cmd.getDisplayName(); @@ -2590,13 +2591,12 @@ private UserVmResponse importKvmInstance(ImportVmCmd cmd) { String hostName = cmd.getHostName(); if (StringUtils.isEmpty(hostName)) { if (!NetUtils.verifyDomainNameLabel(instanceName, true)) { - throw new InvalidParameterValueException(String.format("Please provide hostname for the VM. VM name contains unsupported characters for it to be used as hostname")); + throw Exceptions.invalidParameterValueException("vm.import.kvm.hostname.invalid.chars"); } hostName = instanceName; } if (!NetUtils.verifyDomainNameLabel(hostName, true)) { - throw new InvalidParameterValueException("Invalid VM hostname. VM hostname can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit"); + throw Exceptions.invalidParameterValueException("vm.import.hostname.invalid"); } final Map nicNetworkMap = cmd.getNicNetworkList(); @@ -2615,56 +2615,56 @@ private UserVmResponse importKvmInstance(ImportVmCmd cmd) { UnmanagedInstanceTO unmanagedInstanceTO = null; if (ImportSource.EXTERNAL == importSource) { if (StringUtils.isBlank(cmd.getUsername())) { - throw new InvalidParameterValueException("Username need to be provided."); + throw Exceptions.invalidParameterValueException("vm.import.kvm.username.required"); } HashMap instancesMap = getRemoteVmsOnKVMHost(zoneId, remoteUrl, cmd.getUsername(), cmd.getPassword()); unmanagedInstanceTO = instancesMap.get(cmd.getName()); if (unmanagedInstanceTO == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM with name: %s not found on remote host %s", instanceName, remoteUrl)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.kvm.remote.vm.not.found", Map.of("name", instanceName, "host", remoteUrl)); } } if (ImportSource.SHARED == importSource || ImportSource.LOCAL == importSource) { if (diskPath == null) { - throw new InvalidParameterValueException("Disk Path is required for Import from shared/local storage"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.disk.path.required"); } if (networkId == null) { - throw new InvalidParameterValueException("Network is required for Import from shared/local storage"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.network.required"); } if (poolId == null) { - throw new InvalidParameterValueException("Storage Pool is required for Import from shared/local storage"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.storage.pool.required"); } StoragePool storagePool = primaryDataStoreDao.findById(poolId); if (storagePool == null) { - throw new InvalidParameterValueException("Storage Pool not found"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.storage.pool.not.found"); } if (volumeDao.findByPoolIdAndPath(poolId, diskPath) != null) { - throw new InvalidParameterValueException("Disk image is already in use"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.disk.in.use"); } DiskOffering diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); if (diskOffering != null && !storagePoolSupportsDiskOffering(storagePool, diskOffering)) { - throw new InvalidParameterValueException(String.format("Service offering: %s storage tags are not compatible with selected storage pool: %s", serviceOffering.getUuid(), storagePool.getUuid())); + throw Exceptions.invalidParameterValueException("vm.import.kvm.offering.pool.incompatible", Map.of("offering", serviceOffering.getUuid(), "pool", storagePool.getUuid())); } } if (ImportSource.LOCAL == importSource) { if (hostId == null) { - throw new InvalidParameterValueException("Host is required for Import from local storage"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.host.required"); } if (hostDao.findById(hostId) == null) { - throw new InvalidParameterValueException("Host not found"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.host.not.found"); } if(storagePoolHostDao.findByPoolHost(poolId, hostId) == null) { - throw new InvalidParameterValueException("Specified Local Storage Pool not found on Host"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.local.pool.not.found"); } } @@ -2695,11 +2695,11 @@ private UserVmResponse importKvmInstance(ImportVmCmd cmd) { } catch (ResourceAllocationException e) { logger.error(String.format("VM resource allocation error for account: %s", owner.getUuid()), e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.vm.allocation.error", Map.of("owner", owner.getUuid(), "error", StringUtils.defaultString(e.getMessage()))); } if (userVm == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import Vm with name: %s ", instanceName)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.vm.create.failed", Map.of("name", instanceName)); } CallContext.current().setEventResourceId(userVm.getId()); @@ -2719,7 +2719,7 @@ private UserVm importExternalKvmVirtualMachine(final UnmanagedInstanceTO unmanag List unmanagedInstanceDisks = unmanagedInstance.getDisks(); if (CollectionUtils.isEmpty(unmanagedInstanceDisks)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("No attached disks found for the unmanaged VM: %s", instanceName)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.no.disks.found", Map.of("vm", instanceName)); } DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); @@ -2727,7 +2727,7 @@ private UserVm importExternalKvmVirtualMachine(final UnmanagedInstanceTO unmanag final UnmanagedInstanceTO.Disk rootDisk = rootAndDataDisksPair.first(); final List dataDisks = rootAndDataDisksPair.second(); if (rootDisk == null || StringUtils.isEmpty(rootDisk.getController())) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed. Unable to retrieve root disk details for VM: %s ", instanceName)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.root.disk.not.found", Map.of("vm", instanceName)); } allDetails.put(VmDetailConstants.ROOT_DISK_CONTROLLER, rootDisk.getController()); @@ -2751,14 +2751,15 @@ private UserVm importExternalKvmVirtualMachine(final UnmanagedInstanceTO unmanag Hypervisor.HypervisorType.KVM, allDetails, powerState, null); } catch (InsufficientCapacityException ice) { logger.error(String.format("Failed to import vm name: %s", instanceName), ice); - throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage()); + throw Exceptions.serverApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, "vm.import.insufficient.capacity.error", Map.of("error", ice.getMessage())); } if (userVm == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.vm.create.failed", Map.of("name", instanceName)); } String rootVolumeName = String.format("ROOT-%s", userVm.getId()); DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, null, false); + DiskProfile[] dataDiskProfiles = new DiskProfile[dataDisks.size()]; int diskSeq = 0; for (UnmanagedInstanceTO.Disk disk : dataDisks) { @@ -2778,10 +2779,10 @@ private UserVm importExternalKvmVirtualMachine(final UnmanagedInstanceTO unmanag } catch (Exception e) { logger.warn("Import failed for Vm: {} while finding deployment destination", userVm, e); cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s while finding deployment destination", userVm.getInstanceName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.kvm.deployment.destination.error", Map.of("vm", userVm.getInstanceName())); } if(dest == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s. Suitable deployment destination not found", userVm.getInstanceName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.kvm.deployment.destination.not.found", Map.of("vm", userVm.getInstanceName())); } List> diskProfileStoragePoolList = new ArrayList<>(); @@ -2802,7 +2803,7 @@ private UserVm importExternalKvmVirtualMachine(final UnmanagedInstanceTO unmanag } catch (Exception e) { logger.error(String.format("Failed to import volumes while importing vm: %s", instanceName), e); cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.volumes.failed", Map.of("vm", instanceName, "error", StringUtils.defaultString(e.getMessage()))); } try { int nicIndex = 0; @@ -2815,7 +2816,7 @@ private UserVm importExternalKvmVirtualMachine(final UnmanagedInstanceTO unmanag } catch (Exception e) { logger.error(String.format("Failed to import NICs while importing vm: %s", instanceName), e); cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import NICs while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.nics.failed", Map.of("vm", instanceName, "error", StringUtils.defaultString(e.getMessage()))); } publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); return userVm; @@ -2829,7 +2830,7 @@ protected void checkVolumeResourceLimitsForExternalKvmVmImport(Account owner, Un List dataDisks, DiskOfferingVO rootDiskOffering, Map dataDiskOfferingMap, List reservations) throws ResourceAllocationException { if (rootDisk.getCapacity() == null || rootDisk.getCapacity() == 0) { - throw new InvalidParameterValueException(String.format("Root disk ID: %s size is invalid", rootDisk.getDiskId())); + throw Exceptions.invalidParameterValueException("vm.import.root.disk.size.invalid", Map.of("diskId", rootDisk.getDiskId())); } resourceLimitService.checkVolumeResourceLimit(owner, true, rootDisk.getCapacity(), rootDiskOffering, reservations); @@ -2838,7 +2839,7 @@ protected void checkVolumeResourceLimitsForExternalKvmVmImport(Account owner, Un } for (UnmanagedInstanceTO.Disk disk : dataDisks) { if (disk.getCapacity() == null || disk.getCapacity() == 0) { - throw new InvalidParameterValueException(String.format("Data disk ID: %s size is invalid", disk.getDiskId())); + throw Exceptions.invalidParameterValueException("vm.import.data.disk.size.invalid", Map.of("diskId", disk.getDiskId())); } DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId())); resourceLimitService.checkVolumeResourceLimit(owner, true, disk.getCapacity(), offering, reservations); @@ -2858,7 +2859,7 @@ private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource, NetworkVO network = networkDao.findById(networkId); if (network == null) { - throw new InvalidParameterValueException("Unable to find network by id " + networkId); + throw Exceptions.invalidParameterValueException("vm.import.kvm.network.by.id.not.found", Map.of("id", networkId)); } networkModel.checkNetworkPermissions(owner, network); @@ -2866,15 +2867,14 @@ private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource, // don't allow to use system networks NetworkOffering networkOffering = entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); if (networkOffering.isSystemOnly()) { - throw new InvalidParameterValueException("Network id=" + networkId + " is system only and can't be used for vm deployment"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.network.system.only", Map.of("id", networkId)); } LinkedHashMap> networkNicMap = new LinkedHashMap<>(); if ((network.getDataCenterId() != zone.getId())) { if (!network.isStrechedL2Network()) { - throw new InvalidParameterValueException("Network id=" + network.getId() + - " doesn't belong to zone " + zone.getId()); + throw Exceptions.invalidParameterValueException("vm.import.kvm.network.zone.mismatch", Map.of("id", network.getId(), "zone", zone.getId())); } } @@ -2907,7 +2907,7 @@ private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource, Hypervisor.HypervisorType.KVM, allDetails, powerState, networkNicMap); if (userVm == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.vm.create.failed", Map.of("name", instanceName)); } DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); @@ -2930,10 +2930,10 @@ private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource, } catch (Exception e) { logger.warn("Import failed for Vm: {} while finding deployment destination", userVm, e); cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s while finding deployment destination", userVm.getInstanceName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.kvm.deployment.destination.error", Map.of("vm", userVm.getInstanceName())); } if(dest == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s. Suitable deployment destination not found", userVm.getInstanceName())); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.kvm.deployment.destination.not.found", Map.of("vm", userVm.getInstanceName())); } Map storage = dest.getStorageForDisks(); @@ -2978,7 +2978,7 @@ private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource, } catch (Exception e) { logger.error(String.format("Failed to import volumes while importing vm: %s", instanceName), e); cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); + throw Exceptions.serverApiException(ApiErrorCode.INTERNAL_ERROR, "vm.import.volumes.failed", Map.of("vm", instanceName, "error", StringUtils.defaultString(e.getMessage()))); } networkOrchestrationService.importNic(macAddress, 0, network, true, userVm, requestedIpPair, zone, true); publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); @@ -2986,7 +2986,7 @@ private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource, } catch (InsufficientCapacityException ice) { // This will be thrown by com.cloud.vm.UserVmService.importVM logger.error(String.format("Failed to import vm name: %s", instanceName), ice); - throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage()); + throw Exceptions.serverApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, "vm.import.insufficient.capacity.error", Map.of("error", ice.getMessage())); } catch (ResourceAllocationException e) { cleanupFailedImportVM(userVm); throw e; @@ -3021,12 +3021,12 @@ protected void checkVmResourceLimitsForExternalKvmVmImport(Account owner, Servic protected Integer getDetailAsInteger(String key, Map details) { String detail = details.get(key); if (detail == null) { - throw new InvalidParameterValueException(String.format("Detail '%s' must be provided.", key)); + throw Exceptions.invalidParameterValueException("vm.import.kvm.detail.missing", Map.of("key", key)); } try { return Integer.valueOf(detail); } catch (NumberFormatException e) { - throw new InvalidParameterValueException(String.format("Please provide a valid integer value for detail '%s'.", key)); + throw Exceptions.invalidParameterValueException("vm.import.kvm.detail.invalid", Map.of("key", key)); } } @@ -3068,25 +3068,24 @@ private NetworkVO getDefaultNetwork(DataCenter zone, Account owner, boolean sele List requiredOfferings = networkOfferingDao.listByAvailability(NetworkOffering.Availability.Required, false); if (requiredOfferings.size() < 1) { - throw new InvalidParameterValueException("Unable to find network offering with availability=" + NetworkOffering.Availability.Required - + " to automatically create the network as a part of vm creation"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.required.network.offering.not.found", Map.of("availability", NetworkOffering.Availability.Required)); } if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { // get Virtual networks List virtualNetworks = networkModel.listNetworksForAccount(owner.getId(), zone.getId(), Network.GuestType.Isolated); if (virtualNetworks == null) { - throw new InvalidParameterValueException("No (virtual) networks are found for account " + owner); + throw Exceptions.invalidParameterValueException("vm.import.kvm.no.virtual.network", Map.of("owner", owner.toString())); } if (virtualNetworks.isEmpty()) { defaultNetwork = createDefaultNetworkForAccount(zone, owner, requiredOfferings); } else if (virtualNetworks.size() > 1 && !selectAny) { - throw new InvalidParameterValueException("More than 1 default Isolated networks are found for account " + owner + "; please specify networkIds"); + throw Exceptions.invalidParameterValueException("vm.import.kvm.multiple.default.networks", Map.of("owner", owner.toString())); } else { defaultNetwork = networkDao.findById(virtualNetworks.get(0).getId()); } } else { - throw new InvalidParameterValueException("Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + NetworkOffering.State.Enabled); + throw Exceptions.invalidParameterValueException("vm.import.kvm.required.network.offering.disabled", Map.of("id", requiredOfferings.get(0).getId(), "state", NetworkOffering.State.Enabled)); } return defaultNetwork; @@ -3099,8 +3098,7 @@ private NetworkVO createDefaultNetworkForAccount(DataCenter zone, Account owner, // Validate physical network PhysicalNetwork physicalNetwork = physicalNetworkDao.findById(physicalNetworkId); if (physicalNetwork == null) { - throw new InvalidParameterValueException("Unable to find physical network with id: " + physicalNetworkId + " and tag: " - + requiredOfferings.get(0).getTags()); + throw Exceptions.invalidParameterValueException("vm.import.kvm.physical.network.not.found", Map.of("id", physicalNetworkId, "tag", requiredOfferings.get(0).getTags())); } logger.debug("Creating network for account {} from the network offering {} as a part of deployVM process", owner, requiredOfferings.get(0)); Network newNetwork = networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", @@ -3120,11 +3118,12 @@ public ListResponse listVmsForImport(ListVmsForImport final Long zoneId = cmd.getZoneId(); final DataCenterVO zone = dataCenterDao.findById(zoneId); if (zone == null) { - throw new InvalidParameterValueException("Please specify a valid zone."); + throw Exceptions.invalidParameterValueException("vm.import.zone.invalid"); } final String hypervisorType = cmd.getHypervisor(); if (!Hypervisor.HypervisorType.KVM.toString().equalsIgnoreCase(hypervisorType)) { - throw new InvalidParameterValueException(String.format("VM Import is currently not supported for hypervisor: %s", hypervisorType)); + throw Exceptions.invalidParameterValueException("vm.import.kvm.list.hypervisor.not.supported", + Map.of("hypervisor", StringUtils.defaultIfEmpty(hypervisorType, "null"))); } String keyword = cmd.getKeyword(); diff --git a/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java b/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java index 8f88800d549f..9ec4932b8ec8 100644 --- a/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java +++ b/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java @@ -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.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.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.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.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 @@ private void prepareTestChangeStoragePoolScope(ScopeType currentScope, StoragePo 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); } diff --git a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java index 4dc5a1f3bdfb..d68ab9a16fdd 100644 --- a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java +++ b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java @@ -16,35 +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.Storage.StoragePoolType; -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; @@ -60,13 +35,11 @@ import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; - -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; @@ -74,9 +47,34 @@ 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.Storage.StoragePoolType; +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 { @@ -248,7 +246,7 @@ public void testValidatePolicyZonesDisabledZone() { 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)); } diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index f55124f0ad69..43226ef879a9 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -1545,7 +1545,6 @@ public void testRestoreVirtualMachineNotInRightState() throws ResourceUnavailabl when(accountMock.getId()).thenReturn(userId); when(userVmDao.findById(vmId)).thenReturn(userVmVoMock); when(userVmVoMock.getAccountId()).thenReturn(accountId); - when(userVmVoMock.getUuid()).thenReturn("a967643d-7633-4ab4-ac26-9c0b63f50cc1"); when(accountDao.findById(accountId)).thenReturn(callerAccount); when(userVmVoMock.getState()).thenReturn(VirtualMachine.State.Starting); @@ -1561,7 +1560,6 @@ public void testRestoreVirtualMachineNoRootVolume() throws ResourceUnavailableEx when(accountMock.getId()).thenReturn(userId); when(userVmDao.findById(vmId)).thenReturn(userVmVoMock); when(userVmVoMock.getAccountId()).thenReturn(accountId); - when(userVmVoMock.getUuid()).thenReturn("a967643d-7633-4ab4-ac26-9c0b63f50cc1"); when(accountDao.findById(accountId)).thenReturn(callerAccount); when(userVmVoMock.getState()).thenReturn(VirtualMachine.State.Running); when(userVmVoMock.getTemplateId()).thenReturn(currentTemplateId); @@ -1582,7 +1580,6 @@ public void testRestoreVirtualMachineMoreThanOneRootVolume() throws ResourceUnav when(accountMock.getId()).thenReturn(userId); when(userVmDao.findById(vmId)).thenReturn(userVmVoMock); when(userVmVoMock.getAccountId()).thenReturn(accountId); - when(userVmVoMock.getUuid()).thenReturn("a967643d-7633-4ab4-ac26-9c0b63f50cc1"); when(accountDao.findById(accountId)).thenReturn(callerAccount); when(userVmVoMock.getState()).thenReturn(VirtualMachine.State.Running); when(userVmVoMock.getTemplateId()).thenReturn(currentTemplateId); @@ -1850,25 +1847,25 @@ public void checkExpungeVmPermissionTestAccountIsAdminHasApiAccessReturnNothing @Test public void validateIfVmSupportsMigrationTestVmIsNullThrowsInvalidParameterValueException() { - String expectedMessage = String.format("There is no VM by ID [%s].", 1L); + String expectedMessageKey = "vm.assign.vm.not.found"; InvalidParameterValueException assertThrows = Assert.assertThrows(expectedInvalidParameterValueException, () -> { userVmManagerImpl.validateIfVmSupportsMigration(null, 1L); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test public void validateIfVmSupportsMigrationTestVmIsRunningThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Unable to move VM [%s] in [%s] state.", userVmVoMock, VirtualMachine.State.Running); + String expectedMessageKey = "vm.assign.vm.not.right.state"; Mockito.doReturn(VirtualMachine.State.Running).when(userVmVoMock).getState(); InvalidParameterValueException assertThrows = Assert.assertThrows(expectedInvalidParameterValueException, () -> { userVmManagerImpl.validateIfVmSupportsMigration(userVmVoMock, 1L); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -1879,7 +1876,7 @@ public void validateIfVmSupportsMigrationTestVmIsSharedFileSystemInstanceThrowsI userVmManagerImpl.validateIfVmSupportsMigration(userVmVoMock, 1L); }); - Assert.assertEquals("Migration is not supported for Shared FileSystem Instances.", assertThrows.getMessage()); + Assert.assertEquals("vm.assign.sharedfs.not.supported", assertThrows.getMessageKey()); } @Test @@ -1897,29 +1894,30 @@ public void validateOldAndNewAccountsTestBothAreValidDoNothing() { @Test public void validateOldAndNewAccountsTestOldAccountIsNullThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Invalid old account [%s] for VM in domain [%s].", userVmVoMock.getAccountId(), assignVmCmdMock.getDomainId()); + String expectedMessageKey = "vm.assign.old.account.invalid"; InvalidParameterValueException assertThrows = Assert.assertThrows(expectedInvalidParameterValueException, () -> { userVmManagerImpl.validateOldAndNewAccounts(null, accountMock, userVmVoMock.getAccountId(), "", assignVmCmdMock.getDomainId()); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test public void validateOldAndNewAccountsTestNewAccountIsNullThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Invalid new account [%s] for VM in domain [%s].", assignVmCmdMock.getAccountName(), assignVmCmdMock.getDomainId()); + String expectedMessageKey = "vm.assign.new.account.invalid"; + Mockito.doReturn("test").when(assignVmCmdMock).getAccountName(); InvalidParameterValueException assertThrows = Assert.assertThrows(expectedInvalidParameterValueException, () -> { userVmManagerImpl.validateOldAndNewAccounts(accountMock, null, 1L, assignVmCmdMock.getAccountName(), assignVmCmdMock.getDomainId()); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test public void validateOldAndNewAccountsTestNewAccountStateIsDisabledThrowsInvalidParameterValueException() { - String expectedMessage = String.format("The new account owner [%s] is disabled.", accountMock); + String expectedMessageKey = "vm.assign.new.account.disabled"; Mockito.doReturn(Account.State.DISABLED).when(accountMock).getState(); @@ -1927,12 +1925,12 @@ public void validateOldAndNewAccountsTestNewAccountStateIsDisabledThrowsInvalidP userVmManagerImpl.validateOldAndNewAccounts(accountMock, accountMock, 1L, "", 1L); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test public void validateOldAndNewAccountsTestOldAccountIsTheSameAsNewAccountThrowsInvalidParameterValueException() { - String expectedMessage = String.format("The new account [%s] is the same as the old account.", accountMock); + String expectedMessageKey = "vm.assign.same.account"; Mockito.doReturn(Account.State.ENABLED).when(accountMock).getState(); @@ -1940,7 +1938,7 @@ public void validateOldAndNewAccountsTestOldAccountIsTheSameAsNewAccountThrowsIn userVmManagerImpl.validateOldAndNewAccounts(accountMock, accountMock, 1L, "", 1L); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -1963,7 +1961,7 @@ public void checkCallerAccessToAccountsTestCallsCheckAccessToOldAccountAndNewAcc @Test public void validateIfVmHasNoRulesTestPortForwardingRulesExistThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Remove any Port Forwarding rules for VM [%s] before assigning it to another user.", userVmVoMock); + String expectedMessageKey = "vm.assign.portforwarding.rules.exist"; Mockito.doReturn(portForwardingRulesListMock).when(portForwardingRulesDaoMock).listByVm(Mockito.anyLong()); @@ -1971,12 +1969,12 @@ public void validateIfVmHasNoRulesTestPortForwardingRulesExistThrowsInvalidParam userVmManagerImpl.validateIfVmHasNoRules(userVmVoMock, 1L); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test public void validateIfVmHasNoRulesTestStaticNatRulesExistThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Remove the StaticNat rules for VM [%s] before assigning it to another user.", userVmVoMock); + String expectedMessageKey = "vm.assign.staticnat.rules.exist"; Mockito.doReturn(firewallRuleVoListMock).when(firewallRulesDaoMock).listStaticNatByVmId(Mockito.anyLong()); @@ -1984,12 +1982,12 @@ public void validateIfVmHasNoRulesTestStaticNatRulesExistThrowsInvalidParameterV userVmManagerImpl.validateIfVmHasNoRules(userVmVoMock, 1L); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test public void validateIfVmHasNoRulesTestLoadBalancingRulesExistThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Remove the Load Balancing rules for VM [%s] before assigning it to another user.", userVmVoMock); + String expectedMessageKey = "vm.assign.loadbalancer.rules.exist"; Mockito.doReturn(loadBalancerVmMapVoListMock).when(loadBalancerVmMapDaoMock).listByInstanceId(Mockito.anyLong()); @@ -1997,12 +1995,12 @@ public void validateIfVmHasNoRulesTestLoadBalancingRulesExistThrowsInvalidParame userVmManagerImpl.validateIfVmHasNoRules(userVmVoMock, 1L); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test public void validateIfVmHasNoRulesTestOneToOneNatRulesExistThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Remove the One to One Nat rule for VM [%s] for IP [%s].", userVmVoMock, ipAddressVoMock.toString()); + String expectedMessageKey = "vm.assign.onetoonat.rule.exists"; LinkedList ipAddressVoList = new LinkedList<>(); @@ -2014,7 +2012,7 @@ public void validateIfVmHasNoRulesTestOneToOneNatRulesExistThrowsInvalidParamete userVmManagerImpl.validateIfVmHasNoRules(userVmVoMock, 1L); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2048,13 +2046,13 @@ public void verifyResourceLimitsForAccountAndStorageTestCountOnlyRunningVmsInRes @Test public void validateIfNewOwnerHasAccessToTemplateTestTemplateIsNullThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Template for VM [%s] cannot be found.", userVmVoMock.getUuid()); + String expectedMessageKey = "vm.assign.template.not.found"; InvalidParameterValueException assertThrows = Assert.assertThrows(expectedInvalidParameterValueException, () -> { userVmManagerImpl.validateIfNewOwnerHasAccessToTemplate(userVmVoMock, accountMock, null); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2109,13 +2107,13 @@ public void cleanupOfOldOwnerNicsForNetworkTestCallsCleanupNicsAndRemoveNics() { @Test public void addDefaultNetworkToNetworkListTestDefaultNetworkIsNullThrowsInvalidParameterValueException() { - String expectedMessage = "Unable to find a default network to start a VM."; + String expectedMessageKey = "vm.assign.default.network.not.found"; InvalidParameterValueException assertThrows = Assert.assertThrows(expectedInvalidParameterValueException, () -> { userVmManagerImpl.addDefaultNetworkToNetworkList(networkVoListMock, null); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2138,7 +2136,7 @@ public void allocateNetworksForVmTestCallsNetworkManagerAllocate() throws Insuff @Test public void addSecurityGroupsToVmTestIsVmWareAndSecurityGroupIdListIsNotNullThrowsInvalidParameterValueException() { - String expectedMessage = "Security group feature is not supported for VMWare hypervisor."; + String expectedMessageKey = "vm.assign.securitygroup.vmware.not.supported"; LinkedList securityGroupIdList = new LinkedList<>(); Mockito.doReturn(Hypervisor.HypervisorType.VMware).when(virtualMachineTemplateMock).getHypervisorType(); @@ -2147,7 +2145,7 @@ public void addSecurityGroupsToVmTestIsVmWareAndSecurityGroupIdListIsNotNullThro userVmManagerImpl.addSecurityGroupsToVm(accountMock, userVmVoMock, virtualMachineTemplateMock, securityGroupIdList, networkMock); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2180,8 +2178,7 @@ public void addNetworksToNetworkIdListTestCallsKeepOldSharedNetworkForVmAndAddAd @Test public void getOfferingWithRequiredAvailabilityForNetworkCreationTestRequiredOfferingsListHasNoOfferingsThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Unable to find network offering with availability [%s] to automatically create the network as a part of VM creation.", - NetworkOffering.Availability.Required); + String expectedMessageKey = "vm.assign.network.offering.required.not.found"; LinkedList requiredOfferings = new LinkedList<>(); Mockito.doReturn(requiredOfferings).when(networkOfferingDaoMock).listByAvailability(NetworkOffering.Availability.Required, false); @@ -2190,25 +2187,23 @@ public void getOfferingWithRequiredAvailabilityForNetworkCreationTestRequiredOff userVmManagerImpl.getOfferingWithRequiredAvailabilityForNetworkCreation(); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test public void getOfferingWithRequiredAvailabilityForNetworkCreationTestFirstOfferingIsNotEnabledThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Required network offering ID [%s] is not in [%s] state.", 1L, NetworkOffering.State.Enabled); + String expectedMessageKey = "vm.assign.network.offering.required.not.enabled"; Mockito.doReturn(networkOfferingVoListMock).when(networkOfferingDaoMock).listByAvailability(NetworkOffering.Availability.Required, false); Mockito.doReturn(networkOfferingVoMock).when(networkOfferingVoListMock).get(0); Mockito.doReturn(NetworkOffering.State.Disabled).when(networkOfferingVoMock).getState(); - Mockito.doReturn(1L).when(networkOfferingVoMock).getId(); - InvalidParameterValueException assertThrows = Assert.assertThrows(expectedInvalidParameterValueException, () -> { userVmManagerImpl.getOfferingWithRequiredAvailabilityForNetworkCreation(); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test(expected = CloudRuntimeException.class) @@ -2225,7 +2220,7 @@ public void selectApplicableNetworkToCreateVmTestVirtualNetworkIsEmptyThrowsExce @Test public void selectApplicableNetworkToCreateVmTestVirtualNetworkHasMultipleNetworksThrowsInvalidParameterValueException() { - String expectedMessage = String.format("More than one default isolated network has been found for account [%s]; please specify networkIDs.", accountMock); + String expectedMessageKey = "vm.assign.multiple.default.networks"; HashSet applicableNetworks = new HashSet<>(); LinkedList virtualNetworks = new LinkedList<>(); @@ -2238,7 +2233,7 @@ public void selectApplicableNetworkToCreateVmTestVirtualNetworkHasMultipleNetwor userVmManagerImpl.selectApplicableNetworkToCreateVm(accountMock, _dcMock, applicableNetworks); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2419,7 +2414,7 @@ public void addAdditionalNetworksToVmTestNetworkIdListIsEmptyDoesNotCallCheckNet @Test public void addAdditionalNetworksToVmTestNetworkIsNullThrowsInvalidParameterValueException() { - String expectedMessage = "Unable to find specified Network ID."; + String expectedMessageKey = "vm.assign.network.not.found"; LinkedList networkIdList = new LinkedList<>(); HashSet applicableNetworks = Mockito.spy(new HashSet<>()); HashMap requestedIPv4ForNics = new HashMap<>(); @@ -2431,12 +2426,12 @@ public void addAdditionalNetworksToVmTestNetworkIsNullThrowsInvalidParameterValu userVmManagerImpl.addAdditionalNetworksToVm(userVmVoMock, accountMock, networkIdList, applicableNetworks, requestedIPv4ForNics, requestedIPv6ForNics); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test public void addAdditionalNetworksToVmTestNetworkOfferingIsSystemOnlyThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Specified network [%s] is system only and cannot be used for VM deployment.", networkMock); + String expectedMessageKey = "vm.assign.network.system.only"; LinkedList networkIdList = new LinkedList<>(); HashSet applicableNetworks = Mockito.spy(new HashSet<>()); HashMap requestedIPv4ForNics = new HashMap<>(); @@ -2452,7 +2447,7 @@ public void addAdditionalNetworksToVmTestNetworkOfferingIsSystemOnlyThrowsInvali userVmManagerImpl.addAdditionalNetworksToVm(userVmVoMock, accountMock, networkIdList, applicableNetworks, requestedIPv4ForNics, requestedIPv6ForNics); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2542,12 +2537,12 @@ public void addAdditionalNetworksToVmTestOldNicIsNotNullPutsIpv4InRequestIpv4For public void createApplicableNetworkToCreateVmTestPhysicalNetworkIsNullThrowsInvalidParameterValueException() { Mockito.doReturn(networkOfferingVoMock).when(userVmManagerImpl).getOfferingWithRequiredAvailabilityForNetworkCreation(); - String expectedMessage = String.format("Unable to find physical network with ID [%s] and tag [%s].", 0L, null); + String expectedMessageKey = "vm.assign.physical.network.not.found"; InvalidParameterValueException assertThrows = Assert.assertThrows(expectedInvalidParameterValueException, () -> { userVmManagerImpl.createApplicableNetworkToCreateVm(accountMock, _dcMock); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2739,7 +2734,7 @@ public void implementNetworkTestImplementedNetworkSecondIsNotNullReturnImplement @Test public void implementNetworkTestImplementedNetworkCatchException() throws ResourceUnavailableException, InsufficientCapacityException { - String expectedMessage = String.format("Failed to implement network [%s] elements and resources as a part of network provision.", networkMock); + String expectedMessageKey = "vm.assign.network.implement.failed"; CallContext callContextMock = Mockito.mock(CallContext.class); @@ -2757,13 +2752,13 @@ public void implementNetworkTestImplementedNetworkCatchException() throws Resour userVmManagerImpl.implementNetwork(accountMock, _dcMock, networkMock); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } } @Test public void updateBasicTypeNetworkForVmTestNetworkIdListIsNotEmptyThrowsInvalidParameterValueException() { - String expectedMessage = "Cannot move VM with Network IDs; this is a basic zone VM."; + String expectedMessageKey = "vm.assign.basic.zone.network.ids.not.allowed"; LinkedList networkIdList = new LinkedList<>(); LinkedList securityGroupIdList = new LinkedList<>(); @@ -2774,7 +2769,7 @@ public void updateBasicTypeNetworkForVmTestNetworkIdListIsNotEmptyThrowsInvalidP securityGroupIdList); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2814,7 +2809,7 @@ public void updateBasicTypeNetworkForVmTestNetworkIdListIsEmptyCallsCleanupOfOld @Test public void updateAdvancedTypeNetworkForVmTestSecurityGroupIsEnabledApplicableNetworksIsEmptyThrowsInvalidParameterValueException() { - String expectedMessage = "No network is specified, please specify one when you move the VM. For now, please add a network to VM on NICs tab."; + String expectedMessageKey = "vm.assign.advanced.sg.no.network"; LinkedList securityGroupIdList = Mockito.mock(LinkedList.class); LinkedList networkIdList = new LinkedList<>(); @@ -2826,7 +2821,7 @@ public void updateAdvancedTypeNetworkForVmTestSecurityGroupIsEnabledApplicableNe }); Mockito.verify(securityGroupManagerMock).removeInstanceFromGroups(Mockito.any()); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2852,7 +2847,7 @@ public void updateAdvancedTypeNetworkForVmTestSecurityGroupIsEnabledApplicableNe @Test public void updateAdvancedTypeNetworkForVmTestSecurityGroupIsNotEnabledSecurityGroupIdListIsNotEmptyThrowsInvalidParameterValueException() { - String expectedMessage = "Cannot move VM with security groups; security group feature is not enabled in this zone."; + String expectedMessageKey = "vm.assign.securitygroup.zone.not.enabled"; LinkedList securityGroupIdList = Mockito.mock(LinkedList.class); LinkedList networkIdList = new LinkedList<>(); @@ -2865,7 +2860,7 @@ public void updateAdvancedTypeNetworkForVmTestSecurityGroupIsNotEnabledSecurityG _dcMock, networkIdList, securityGroupIdList); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2961,8 +2956,7 @@ public void addNicsToApplicableNetworksAndReturnDefaultNetworkTestApplicableNetw @Test public void validateIfVolumesHaveNoSnapshotsTestVolumeHasSnapshotsThrowsInvalidParameterException() { - String expectedMessage = String.format("Snapshots exist for volume [%s]. Detach volume or remove snapshots for the volume before assigning VM to another user.", - volumeVOMock.getName()); + String expectedMessageKey = "vm.assign.volume.snapshots.exist"; LinkedList volumes = new LinkedList<>(); volumes.add(volumeVOMock); @@ -2975,7 +2969,7 @@ public void validateIfVolumesHaveNoSnapshotsTestVolumeHasSnapshotsThrowsInvalidP userVmManagerImpl.validateIfVolumesHaveNoSnapshots(volumes); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -2991,7 +2985,7 @@ public void validateIfVolumesHaveNoSnapshotsTestVolumeHasNoSnapshotsDoesNotThrow @Test public void moveVmToUserTestCallerIsNotRootAdminAndDomainAdminThrowsInvalidParameterValueException() { - String expectedMessage = String.format("Only root or domain admins are allowed to assign VMs. Caller [%s] is of type [%s].", callerAccount, callerAccount.getType()); + String expectedMessageKey = "vm.assign.permission.denied"; Mockito.doReturn(false).when(accountManager).isRootAdmin(Mockito.anyLong()); Mockito.doReturn(false).when(accountManager).isDomainAdmin(Mockito.anyLong()); @@ -3000,7 +2994,7 @@ public void moveVmToUserTestCallerIsNotRootAdminAndDomainAdminThrowsInvalidParam userVmManagerImpl.moveVmToUser(assignVmCmdMock); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -3024,7 +3018,7 @@ public void moveVmToUserTestValidateAccountsAndCallerAccessToThemThrowsInvalidPa public void moveVmToUserTestProjectIdProvidedAndDomainIdIsNullThrowsInvalidParameterValueException() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { - String expectedMessage = "Please provide a valid domain ID; cannot assign VM to a project if domain ID is NULL."; + String expectedMessageKey = "vm.assign.domain.id.null"; Mockito.doReturn(true).when(accountManager).isRootAdmin(Mockito.anyLong()); Mockito.doReturn(userVmVoMock).when(userVmDao).findById(Mockito.anyLong()); @@ -3037,7 +3031,7 @@ public void moveVmToUserTestProjectIdProvidedAndDomainIdIsNullThrowsInvalidParam userVmManagerImpl.moveVmToUser(assignVmCmdMock); }); - Assert.assertEquals(expectedMessage, assertThrows.getMessage()); + Assert.assertEquals(expectedMessageKey, assertThrows.getMessageKey()); } @Test @@ -3800,6 +3794,7 @@ public void testApplyLeaseOnCreateInstanceNullExpiryAction() { public void testApplyLeaseOnUpdateInstanceForNoLease() { UserVmVO userVm = Mockito.mock(UserVmVO.class); when(userVm.getId()).thenReturn(vmId); + when(userVm.getUuid()).thenReturn(UUID.randomUUID().toString()); when(vmInstanceDetailsDao.listDetailsKeyPairs(anyLong(), anyList())).thenReturn(getLeaseDetails(5, VMLeaseManager.LeaseActionExecution.DISABLED.name())); userVmManagerImpl.applyLeaseOnUpdateInstance(userVm, 10, VMLeaseManager.ExpiryAction.STOP); Mockito.verify(userVmManagerImpl, Mockito.times(0)).addLeaseDetailsForInstance(any(), any(), any()); @@ -3809,6 +3804,7 @@ public void testApplyLeaseOnUpdateInstanceForNoLease() { public void testApplyLeaseOnUpdateInstanceForLease() { UserVmVO userVm = Mockito.mock(UserVmVO.class); when(userVm.getId()).thenReturn(vmId); + when(userVm.getUuid()).thenReturn(UUID.randomUUID().toString()); when(vmInstanceDetailsDao.listDetailsKeyPairs(anyLong(), anyList())).thenReturn(getLeaseDetails(5, VMLeaseManager.LeaseActionExecution.PENDING.name())); userVmManagerImpl.applyLeaseOnUpdateInstance(userVm, 10, VMLeaseManager.ExpiryAction.STOP); Mockito.verify(userVmManagerImpl, Mockito.times(1)).addLeaseDetailsForInstance(any(), any(), any()); @@ -3818,6 +3814,7 @@ public void testApplyLeaseOnUpdateInstanceForLease() { public void testApplyLeaseOnUpdateInstanceForDisabledLeaseInstance() { UserVmVO userVm = Mockito.mock(UserVmVO.class); when(userVm.getId()).thenReturn(vmId); + when(userVm.getUuid()).thenReturn(UUID.randomUUID().toString()); when(vmInstanceDetailsDao.listDetailsKeyPairs(anyLong(), anyList())).thenReturn(getLeaseDetails(5, VMLeaseManager.LeaseActionExecution.DISABLED.name())); userVmManagerImpl.applyLeaseOnUpdateInstance(userVm, 10, VMLeaseManager.ExpiryAction.STOP); Mockito.verify(userVmManagerImpl, Mockito.times(1)).addLeaseDetailsForInstance(any(), any(), any()); @@ -4040,7 +4037,7 @@ public void createVirtualMachineWithVolumeFromNonZoneScopedStorageFails() { InvalidParameterValueException ex = assertThrows(InvalidParameterValueException.class, () -> userVmManagerImpl.createVirtualMachine(deployVMCmd)); - assertEquals("Deployment of virtual machine is supported only for Zone-wide storage pools", ex.getMessage()); + assertEquals("vm.deploy.volume.storage.pool.zone.required", ex.getMessageKey()); } @Test @@ -4056,7 +4053,7 @@ public void createVirtualMachineWithVolumeWithNullDataStoreFails() { InvalidParameterValueException ex = assertThrows(InvalidParameterValueException.class, () -> userVmManagerImpl.createVirtualMachine(deployVMCmd)); - assertEquals("Deployment of virtual machine is supported only for Zone-wide storage pools", ex.getMessage()); + assertEquals("vm.deploy.volume.storage.pool.zone.required", ex.getMessageKey()); } @Test @@ -4190,7 +4187,7 @@ public void testUnmanageUserVMVmNotFound() { InvalidParameterValueException exception = assertThrows(InvalidParameterValueException.class, () -> { userVmManagerImpl.unmanageUserVM(vmId, null); }); - assertEquals("Unable to find a VM with ID = " + vmId, exception.getMessage()); + assertEquals("vm.unmanage.vm.not.found", exception.getMessageKey()); verify(userVmDao, never()).acquireInLockTable(anyLong()); verify(userVmDao, never()).releaseFromLockTable(anyLong()); } @@ -4214,7 +4211,7 @@ public void testUnmanageUserVMInvalidState() { CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> { userVmManagerImpl.unmanageUserVM(vmId, null); }); - assertEquals("Instance: test-vm is not running or stopped, cannot be unmanaged", exception.getMessage()); + assertEquals("vm.unmanage.vm.not.right.state", exception.getMessage()); verify(userVmDao, times(1)).releaseFromLockTable(vmId); } @@ -4254,7 +4251,7 @@ public void testUnmanageUserVMItManagerReturnsFalse() { userVmManagerImpl.unmanageUserVM(vmId, null); }); - assertEquals("Error while unmanaging VM: " + vmUuid, exception.getMessage()); + assertEquals("vm.unmanage.failed", exception.getMessageKey()); verify(userVmManagerImpl, never()).cleanupUnmanageVMResources(any(UserVmVO.class)); verify(userVmManagerImpl, never()).unmanageVMFromDB(anyLong()); verify(userVmDao, times(1)).releaseFromLockTable(vmId); @@ -4463,7 +4460,6 @@ public void testTransitionExpungingToErrorHandlesNoTransitionException() throws private ServiceOfferingVO getMockedServiceOffering(boolean custom, boolean customSpeed) { ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class); - when(serviceOffering.getUuid()).thenReturn("offering-uuid"); when(serviceOffering.isDynamic()).thenReturn(custom); when(serviceOffering.isCustomCpuSpeedSupported()).thenReturn(customSpeed); if (custom) { @@ -4484,7 +4480,7 @@ public void customOfferingNeedsCustomizationThrowsException() { ServiceOfferingVO serviceOffering = getMockedServiceOffering(true, true); InvalidParameterValueException ex = Assert.assertThrows(InvalidParameterValueException.class, () -> userVmManagerImpl.validateCustomParameters(serviceOffering, Collections.emptyMap())); - assertEquals("Need to specify custom parameter values cpu, cpu speed and memory when using custom offering", ex.getMessage()); + assertEquals("vm.validate.serviceoffering.custom.params.missing", ex.getMessageKey()); } @Test @@ -4497,7 +4493,7 @@ public void cpuSpeedCustomizationNotAllowedThrowsException() { InvalidParameterValueException ex = Assert.assertThrows(InvalidParameterValueException.class, () -> userVmManagerImpl.validateCustomParameters(serviceOffering, customParameters)); - Assert.assertTrue(ex.getMessage().startsWith("The CPU speed of this offering")); + Assert.assertEquals("vm.validate.serviceoffering.cpu.speed.not.dynamic", ex.getMessageKey()); } @Test @@ -4532,7 +4528,7 @@ public void verifyVmLimits_fixedOffering_throwsException() { InvalidParameterValueException ex = Assert.assertThrows(InvalidParameterValueException.class, () -> userVmManagerImpl.verifyVmLimits(userVmVoMock, customParameters)); - assertEquals("CPU number, Memory and CPU speed cannot be updated for a non-dynamic offering", ex.getMessage()); + assertEquals("vm.update.non.dynamic.cpu.memory.not.updatable", ex.getMessageKey()); } @Test @@ -4550,6 +4546,6 @@ public void verifyVmLimits_constrainedOffering_throwsException() { InvalidParameterValueException ex = Assert.assertThrows(InvalidParameterValueException.class, () -> userVmManagerImpl.verifyVmLimits(userVmVoMock, customParameters)); - Assert.assertTrue(ex.getMessage().startsWith("The CPU speed of this offering")); + Assert.assertEquals("vm.validate.serviceoffering.cpu.speed.not.dynamic", ex.getMessageKey()); } } diff --git a/server/src/test/java/org/apache/cloudstack/storage/sharedfs/SharedFSServiceImplTest.java b/server/src/test/java/org/apache/cloudstack/storage/sharedfs/SharedFSServiceImplTest.java index 88493d10038e..2d54afbf0eaf 100644 --- a/server/src/test/java/org/apache/cloudstack/storage/sharedfs/SharedFSServiceImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/storage/sharedfs/SharedFSServiceImplTest.java @@ -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 void testSearchForSharedFS() { 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(); diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index dbc05ec99f39..3611acd6f420 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -397,6 +397,7 @@ public void setUp() throws Exception { when(responseGenerator.createUserVmResponse(any(ResponseObject.ResponseView.class), Mockito.anyString(), any(UserVm.class))).thenReturn(userVmResponses); when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine); + when(virtualMachine.getUuid()).thenReturn(UUID.randomUUID().toString()); when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Running); when(unmanagedInstanceMock.getCpuCores()).thenReturn(8); @@ -1041,9 +1042,11 @@ public void testImportVmFromVmwareToKvmForceMsMutuallyExclusiveWithUseVddk() thr public void testValidateSelectedConversionStoragePoolForVddkFailsWhenPoolDoesNotSupportDiskOfferings() { long poolId = 11L; StoragePoolVO selectedPool = mock(StoragePoolVO.class); + when(selectedPool.getName()).thenReturn("Pool"); ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class); DiskOfferingVO rootDiskOffering = mock(DiskOfferingVO.class); DiskOfferingVO dataDiskOffering = mock(DiskOfferingVO.class); + when(dataDiskOffering.getName()).thenReturn("DataOffering"); when(serviceOffering.getDiskOfferingId()).thenReturn(21L); when(primaryDataStoreDao.findById(poolId)).thenReturn(selectedPool); diff --git a/ui/src/main.js b/ui/src/main.js index 7441f8010865..69b5dbe4dd44 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -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) diff --git a/ui/src/utils/plugins.js b/ui/src/utils/plugins.js index b8856c7c9b00..d43b888eddfd 100644 --- a/ui/src/utils/plugins.js +++ b/ui/src/utils/plugins.js @@ -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 } @@ -621,3 +622,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 + } + } +} diff --git a/utils/src/main/java/com/cloud/utils/StringUtils.java b/utils/src/main/java/com/cloud/utils/StringUtils.java index 73b8d04bf009..40f9369aa0a2 100644 --- a/utils/src/main/java/com/cloud/utils/StringUtils.java +++ b/utils/src/main/java/com/cloud/utils/StringUtils.java @@ -438,4 +438,11 @@ public static String getFirstValueFromCommaSeparatedString(String inputString) { return null; } + + public static String toNullSafeString(String str) { + if (str == null) { + return "null"; + } + return str; + } } diff --git a/utils/src/main/java/com/cloud/utils/exception/CloudRuntimeException.java b/utils/src/main/java/com/cloud/utils/exception/CloudRuntimeException.java index dd5abc84f5fe..f8a5b7f19825 100644 --- a/utils/src/main/java/com/cloud/utils/exception/CloudRuntimeException.java +++ b/utils/src/main/java/com/cloud/utils/exception/CloudRuntimeException.java @@ -24,6 +24,7 @@ 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 metadata = null; + public CloudRuntimeException(String message) { super(message); setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName())); @@ -91,6 +95,11 @@ public int getCSErrorCode() { public CloudRuntimeException(Throwable t) { super(t.getMessage(), t); + if (t instanceof CloudRuntimeException) { + CloudRuntimeException cre = (CloudRuntimeException)t; + setMessageKey(cre.getMessageKey()); + setMetadata(cre.getMetadata()); + } } @Override @@ -138,4 +147,20 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE uuidList.add(new Pair, String>(Class.forName(clzName), val)); } } + + public String getMessageKey() { + return messageKey; + } + + public void setMessageKey(String messageKey) { + this.messageKey = messageKey; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } }