diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 7a167a7aeb6..2f0e4f16797 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -696,6 +696,7 @@ public class ApiConstants { public static final String TRAFFIC_TYPE_IMPLEMENTOR = "traffictypeimplementor"; public static final String KEYWORD = "keyword"; public static final String LIST_ALL = "listall"; + public static final String LIST_ONLY_REMOVED = "listonlyremoved"; public static final String LIST_SYSTEM_VMS = "listsystemvms"; public static final String IP_RANGES = "ipranges"; public static final String IPV6_ROUTING = "ip6routing"; @@ -1141,6 +1142,11 @@ public class ApiConstants { public static final String NFS_MOUNT_OPTIONS = "nfsmountopts"; + public static final String PARAMETER_DESCRIPTION_ACTIVATION_RULE = "Quota tariff's activation rule. It can receive a JS script that results in either " + + "a boolean or a numeric value: if it results in a boolean value, the tariff value will be applied according to the result; if it results in a numeric value, the " + + "numeric value will be applied; if the result is neither a boolean nor a numeric value, the tariff will not be applied. If the rule is not informed, the tariff " + + "value will be applied."; + /** * This enum specifies IO Drivers, each option controls specific policies on I/O. * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0). diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageTypesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageTypesCmd.java index 2772743c75a..b993735dba7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageTypesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageTypesCmd.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UsageTypeResponse; +import org.apache.cloudstack.usage.UsageTypes; import com.cloud.user.Account; @@ -37,8 +38,8 @@ public class ListUsageTypesCmd extends BaseCmd { @Override public void execute() { - List result = _usageService.listUsageTypes(); - ListResponse response = new ListResponse(); + List result = UsageTypes.listUsageTypes(); + ListResponse response = new ListResponse<>(); response.setResponses(result); response.setResponseName(getCommandName()); this.setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UsageTypeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UsageTypeResponse.java index 83b97f00c15..5beef5ac556 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UsageTypeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UsageTypeResponse.java @@ -25,12 +25,16 @@ import com.cloud.serializer.Param; public class UsageTypeResponse extends BaseResponse { - @SerializedName("usagetypeid") - @Param(description = "usage type") + @SerializedName("id") + @Param(description = "Usage type ID") private Integer usageType; + @SerializedName(ApiConstants.NAME) + @Param(description = "Usage type name") + private String name; + @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "description of usage type") + @Param(description = "Usage type description") private String description; public String getDescription() { @@ -49,10 +53,10 @@ public class UsageTypeResponse extends BaseResponse { this.usageType = usageType; } - public UsageTypeResponse(Integer usageType, String description) { + public UsageTypeResponse(Integer usageType, String name, String description) { this.usageType = usageType; + this.name = name; this.description = description; setObjectName("usagetype"); } - } diff --git a/api/src/main/java/org/apache/cloudstack/usage/UsageService.java b/api/src/main/java/org/apache/cloudstack/usage/UsageService.java index 73962ba5875..00e8b431f8f 100644 --- a/api/src/main/java/org/apache/cloudstack/usage/UsageService.java +++ b/api/src/main/java/org/apache/cloudstack/usage/UsageService.java @@ -20,7 +20,6 @@ import com.cloud.utils.Pair; import org.apache.cloudstack.api.command.admin.usage.GenerateUsageRecordsCmd; import org.apache.cloudstack.api.command.admin.usage.ListUsageRecordsCmd; import org.apache.cloudstack.api.command.admin.usage.RemoveRawUsageRecordsCmd; -import org.apache.cloudstack.api.response.UsageTypeResponse; import java.util.List; import java.util.TimeZone; @@ -62,6 +61,4 @@ public interface UsageService { TimeZone getUsageTimezone(); boolean removeRawUsageRecords(RemoveRawUsageRecordsCmd cmd); - - List listUsageTypes(); } diff --git a/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java b/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java index 32ae34056ec..5ad360a8026 100644 --- a/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java +++ b/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java @@ -51,31 +51,31 @@ public class UsageTypes { public static List listUsageTypes() { List responseList = new ArrayList(); - responseList.add(new UsageTypeResponse(RUNNING_VM, "Running Vm Usage")); - responseList.add(new UsageTypeResponse(ALLOCATED_VM, "Allocated Vm Usage")); - responseList.add(new UsageTypeResponse(IP_ADDRESS, "IP Address Usage")); - responseList.add(new UsageTypeResponse(NETWORK_BYTES_SENT, "Network Usage (Bytes Sent)")); - responseList.add(new UsageTypeResponse(NETWORK_BYTES_RECEIVED, "Network Usage (Bytes Received)")); - responseList.add(new UsageTypeResponse(VOLUME, "Volume Usage")); - responseList.add(new UsageTypeResponse(TEMPLATE, "Template Usage")); - responseList.add(new UsageTypeResponse(ISO, "ISO Usage")); - responseList.add(new UsageTypeResponse(SNAPSHOT, "Snapshot Usage")); - responseList.add(new UsageTypeResponse(SECURITY_GROUP, "Security Group Usage")); - responseList.add(new UsageTypeResponse(LOAD_BALANCER_POLICY, "Load Balancer Usage")); - responseList.add(new UsageTypeResponse(PORT_FORWARDING_RULE, "Port Forwarding Usage")); - responseList.add(new UsageTypeResponse(NETWORK_OFFERING, "Network Offering Usage")); - responseList.add(new UsageTypeResponse(VPN_USERS, "VPN users usage")); - responseList.add(new UsageTypeResponse(VM_DISK_IO_READ, "VM Disk usage(I/O Read)")); - responseList.add(new UsageTypeResponse(VM_DISK_IO_WRITE, "VM Disk usage(I/O Write)")); - responseList.add(new UsageTypeResponse(VM_DISK_BYTES_READ, "VM Disk usage(Bytes Read)")); - responseList.add(new UsageTypeResponse(VM_DISK_BYTES_WRITE, "VM Disk usage(Bytes Write)")); - responseList.add(new UsageTypeResponse(VM_SNAPSHOT, "VM Snapshot storage usage")); - responseList.add(new UsageTypeResponse(VOLUME_SECONDARY, "Volume on secondary storage usage")); - responseList.add(new UsageTypeResponse(VM_SNAPSHOT_ON_PRIMARY, "VM Snapshot on primary storage usage")); - responseList.add(new UsageTypeResponse(BACKUP, "Backup storage usage")); - responseList.add(new UsageTypeResponse(BUCKET, "Bucket storage usage")); - responseList.add(new UsageTypeResponse(NETWORK, "Network usage")); - responseList.add(new UsageTypeResponse(VPC, "VPC usage")); + responseList.add(new UsageTypeResponse(RUNNING_VM, "RUNNING_VM", "Running Vm Usage")); + responseList.add(new UsageTypeResponse(ALLOCATED_VM, "ALLOCATED_VM", "Allocated Vm Usage")); + responseList.add(new UsageTypeResponse(IP_ADDRESS, "IP_ADDRESS", "IP Address Usage")); + responseList.add(new UsageTypeResponse(NETWORK_BYTES_SENT, "NETWORK_BYTES_SENT", "Network Usage (Bytes Sent)")); + responseList.add(new UsageTypeResponse(NETWORK_BYTES_RECEIVED, "NETWORK_BYTES_RECEIVED", "Network Usage (Bytes Received)")); + responseList.add(new UsageTypeResponse(VOLUME, "VOLUME", "Volume Usage")); + responseList.add(new UsageTypeResponse(TEMPLATE, "TEMPLATE", "Template Usage")); + responseList.add(new UsageTypeResponse(ISO, "ISO", "ISO Usage")); + responseList.add(new UsageTypeResponse(SNAPSHOT, "SNAPSHOT", "Snapshot Usage")); + responseList.add(new UsageTypeResponse(SECURITY_GROUP, "SECURITY_GROUP", "Security Group Usage")); + responseList.add(new UsageTypeResponse(LOAD_BALANCER_POLICY, "LOAD_BALANCER_POLICY", "Load Balancer Usage")); + responseList.add(new UsageTypeResponse(PORT_FORWARDING_RULE, "PORT_FORWARDING_RULE", "Port Forwarding Usage")); + responseList.add(new UsageTypeResponse(NETWORK_OFFERING, "NETWORK_OFFERING", "Network Offering Usage")); + responseList.add(new UsageTypeResponse(VPN_USERS, "VPN_USERS", "VPN users usage")); + responseList.add(new UsageTypeResponse(VM_DISK_IO_READ, "VM_DISK_IO_READ", "VM Disk usage(I/O Read)")); + responseList.add(new UsageTypeResponse(VM_DISK_IO_WRITE, "VM_DISK_IO_WRITE", "VM Disk usage(I/O Write)")); + responseList.add(new UsageTypeResponse(VM_DISK_BYTES_READ, "VM_DISK_BYTES_READ", "VM Disk usage(Bytes Read)")); + responseList.add(new UsageTypeResponse(VM_DISK_BYTES_WRITE, "VM_DISK_BYTES_WRITE", "VM Disk usage(Bytes Write)")); + responseList.add(new UsageTypeResponse(VM_SNAPSHOT, "VM_SNAPSHOT", "VM Snapshot storage usage")); + responseList.add(new UsageTypeResponse(VOLUME_SECONDARY, "VOLUME_SECONDARY", "Volume on secondary storage usage")); + responseList.add(new UsageTypeResponse(VM_SNAPSHOT_ON_PRIMARY, "VM_SNAPSHOT_ON_PRIMARY", "VM Snapshot on primary storage usage")); + responseList.add(new UsageTypeResponse(BACKUP, "BACKUP", "Backup storage usage")); + responseList.add(new UsageTypeResponse(BUCKET, "BUCKET", "Bucket storage usage")); + responseList.add(new UsageTypeResponse(NETWORK, "NETWORK", "Network usage")); + responseList.add(new UsageTypeResponse(VPC, "VPC", "VPC usage")); return responseList; } } diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDao.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDao.java index 4f13fb33180..419bb0ad7d2 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDao.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDao.java @@ -28,18 +28,10 @@ public interface QuotaTariffDao extends GenericDao { Pair, Integer> listQuotaTariffs(Date startDate, Date endDate, Integer usageType, String name, String uuid, boolean listAll, Long startIndex, Long pageSize); + Pair, Integer> listQuotaTariffs(Date startDate, Date endDate, Integer usageType, String name, String uuid, boolean listAll, boolean listOnlyRemoved, Long startIndex, Long pageSize, String keyword); + QuotaTariffVO findByName(String name); - QuotaTariffVO findTariffPlanByUsageType(int quotaType, Date onOrBefore); - - Pair, Integer> listAllTariffPlans(); - - Pair, Integer> listAllTariffPlans(final Long startIndex, final Long pageSize); - - Pair, Integer> listAllTariffPlans(Date onOrBefore); - - Pair, Integer> listAllTariffPlans(Date onOrBefore, Long startIndex, Long pageSize); - Boolean updateQuotaTariff(QuotaTariffVO plan); QuotaTariffVO addQuotaTariff(QuotaTariffVO plan); diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java index 8cbec8c8598..d36c698f44d 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java @@ -16,12 +16,9 @@ //under the License. package org.apache.cloudstack.quota.dao; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.List; -import org.apache.cloudstack.quota.constant.QuotaTypes; import org.apache.cloudstack.quota.vo.QuotaTariffVO; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; @@ -34,7 +31,6 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.db.TransactionLegacy; -import com.cloud.utils.db.TransactionStatus; @Component public class QuotaTariffDaoImpl extends GenericDaoBase implements QuotaTariffDao { @@ -45,7 +41,7 @@ public class QuotaTariffDaoImpl extends GenericDaoBase impl public QuotaTariffDaoImpl() { super(); searchUsageType = createSearchBuilder(); - searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ); + searchUsageType.and("usageType", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ); searchUsageType.done(); listAllIncludedUsageType = createSearchBuilder(); @@ -54,111 +50,28 @@ public class QuotaTariffDaoImpl extends GenericDaoBase impl listAllIncludedUsageType.done(); } - @Override - public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) { - return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback() { - @Override - public QuotaTariffVO doInTransaction(final TransactionStatus status) { - List result = new ArrayList<>(); - final Filter filter = new Filter(QuotaTariffVO.class, "updatedOn", false, 0L, 1L); - final SearchCriteria sc = listAllIncludedUsageType.create(); - sc.setParameters("onorbefore", effectiveDate); - sc.setParameters("quotatype", quotaType); - result = search(sc, filter); - if (result != null && !result.isEmpty()) { - return result.get(0); - } else { - if (logger.isDebugEnabled()) { - logger.debug("QuotaTariffDaoImpl::findTariffPlanByUsageType: Missing quota type " + quotaType); - } - return null; - } - } - }); - } - - @Override - public Pair, Integer> listAllTariffPlans() { - return listAllTariffPlans(null, null); - } - - @Override - public Pair, Integer> listAllTariffPlans(final Long startIndex, final Long pageSize) { - return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback, Integer>>() { - @Override - public Pair, Integer> doInTransaction(final TransactionStatus status) { - return searchAndCount(null, new Filter(QuotaTariffVO.class, "updatedOn", false, startIndex, pageSize)); - } - }); - } - - - private List paginateList(final List list, final Long startIndex, final Long pageSize) { - if (startIndex == null || pageSize == null) { - return list; - } - if (list.size() < startIndex){ - return Collections.emptyList(); - } - return list.subList(startIndex.intValue(), (int) Math.min(startIndex + pageSize, list.size())); - } - - @Override - public Pair, Integer> listAllTariffPlans(final Date effectiveDate) { - return listAllTariffPlans(effectiveDate, null, null); - } - - @Override - public Pair, Integer> listAllTariffPlans(final Date effectiveDate, final Long startIndex, final Long pageSize) { - return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback, Integer>>() { - @Override - public Pair, Integer> doInTransaction(final TransactionStatus status) { - List tariffs = new ArrayList(); - final Filter filter = new Filter(QuotaTariffVO.class, "updatedOn", false, 0L, 1L); - final SearchCriteria sc = listAllIncludedUsageType.create(); - sc.setParameters("onorbefore", effectiveDate); - for (Integer quotaType : QuotaTypes.listQuotaTypes().keySet()) { - sc.setParameters("quotatype", quotaType); - List result = search(sc, filter); - if (result != null && !result.isEmpty()) { - tariffs.add(result.get(0)); - if (logger.isDebugEnabled()) { - logger.debug("ListAllTariffPlans on or before " + effectiveDate + " quota type " + result.get(0).getUsageTypeDescription() + " , effective Date=" - + result.get(0).getEffectiveOn() + " val=" + result.get(0).getCurrencyValue()); - } - } - } - return new Pair<>(paginateList(tariffs, startIndex, pageSize), tariffs.size()); - } - }); - } - @Override public Boolean updateQuotaTariff(final QuotaTariffVO plan) { - return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback() { - @Override - public Boolean doInTransaction(final TransactionStatus status) { - return update(plan.getId(), plan); - } - }); + return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback) status -> update(plan.getId(), plan)); } @Override public QuotaTariffVO addQuotaTariff(final QuotaTariffVO plan) { if (plan.getIdObj() != null) { - throw new IllegalStateException("The QuotaTariffVO being added should not have an Id set "); + throw new IllegalStateException("The QuotaTariffVO being added should not have an Id set."); } - return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback() { - @Override - public QuotaTariffVO doInTransaction(final TransactionStatus status) { - return persist(plan); - } - }); + return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback) status -> persist(plan)); } @Override public Pair, Integer> listQuotaTariffs(Date startDate, Date endDate, Integer usageType, String name, String uuid, boolean listAll, Long startIndex, Long pageSize) { - SearchCriteria searchCriteria = createListQuotaTariffsSearchCriteria(startDate, endDate, usageType, name, uuid); + return listQuotaTariffs(startDate, endDate, usageType, name, uuid, listAll, false, startIndex, pageSize, null); + } + + @Override + public Pair, Integer> listQuotaTariffs(Date startDate, Date endDate, Integer usageType, String name, String uuid, boolean listAll, boolean listOnlyRemoved, Long startIndex, Long pageSize, String keyword) { + SearchCriteria searchCriteria = createListQuotaTariffsSearchCriteria(startDate, endDate, usageType, name, uuid, listOnlyRemoved, keyword); + Filter sorter = new Filter(QuotaTariffVO.class, "usageType", false, startIndex, pageSize); sorter.addOrderBy(QuotaTariffVO.class, "effectiveOn", false); sorter.addOrderBy(QuotaTariffVO.class, "updatedOn", false); @@ -166,39 +79,34 @@ public class QuotaTariffDaoImpl extends GenericDaoBase impl return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback, Integer>>) status -> searchAndCount(searchCriteria, sorter, listAll)); } - protected SearchCriteria createListQuotaTariffsSearchCriteria(Date startDate, Date endDate, Integer usageType, String name, String uuid) { - SearchCriteria searchCriteria = createListQuotaTariffsSearchBuilder(startDate, endDate, usageType, name, uuid).create(); + protected SearchCriteria createListQuotaTariffsSearchCriteria(Date startDate, Date endDate, Integer usageType, String name, String uuid, boolean listOnlyRemoved, String keyword) { + SearchCriteria searchCriteria = createListQuotaTariffsSearchBuilder(listOnlyRemoved).create(); - searchCriteria.setParametersIfNotNull("start_date", startDate); - searchCriteria.setParametersIfNotNull("end_date", endDate); - searchCriteria.setParametersIfNotNull("usage_type", usageType); + searchCriteria.setParametersIfNotNull("startDate", startDate); + searchCriteria.setParametersIfNotNull("endDate", endDate); + searchCriteria.setParametersIfNotNull("usageType", usageType); searchCriteria.setParametersIfNotNull("name", name); searchCriteria.setParametersIfNotNull("uuid", uuid); + if (keyword != null) { + searchCriteria.setParameters("nameLike", "%" + keyword + "%"); + } + return searchCriteria; } - protected SearchBuilder createListQuotaTariffsSearchBuilder(Date startDate, Date endDate, Integer usageType, String name, String uuid) { + protected SearchBuilder createListQuotaTariffsSearchBuilder(boolean listOnlyRemoved) { SearchBuilder searchBuilder = createSearchBuilder(); - if (startDate != null) { - searchBuilder.and("start_date", searchBuilder.entity().getEffectiveOn(), SearchCriteria.Op.GTEQ); - } + searchBuilder.and("startDate", searchBuilder.entity().getEffectiveOn(), SearchCriteria.Op.GTEQ); + searchBuilder.and("endDate", searchBuilder.entity().getEndDate(), SearchCriteria.Op.LTEQ); + searchBuilder.and("usageType", searchBuilder.entity().getUsageType(), SearchCriteria.Op.EQ); + searchBuilder.and("name", searchBuilder.entity().getName(), SearchCriteria.Op.EQ); + searchBuilder.and("uuid", searchBuilder.entity().getUuid(), SearchCriteria.Op.EQ); + searchBuilder.and("nameLike", searchBuilder.entity().getName(), SearchCriteria.Op.LIKE); - if (endDate != null) { - searchBuilder.and("end_date", searchBuilder.entity().getEndDate(), SearchCriteria.Op.LTEQ); - } - - if (usageType != null) { - searchBuilder.and("usage_type", searchBuilder.entity().getUsageType(), SearchCriteria.Op.EQ); - } - - if (name != null) { - searchBuilder.and("name", searchBuilder.entity().getName(), SearchCriteria.Op.EQ); - } - - if (uuid != null) { - searchBuilder.and("uuid", searchBuilder.entity().getUuid(), SearchCriteria.Op.EQ); + if (listOnlyRemoved) { + searchBuilder.and("removed", searchBuilder.entity().getRemoved(), SearchCriteria.Op.NNULL); } return searchBuilder; diff --git a/plugins/database/quota/pom.xml b/plugins/database/quota/pom.xml index 9dada4128a5..b574b263020 100644 --- a/plugins/database/quota/pom.xml +++ b/plugins/database/quota/pom.xml @@ -62,5 +62,10 @@ joda-time ${cs.joda-time.version} + + org.apache.cloudstack + cloud-plugin-api-discovery + ${project.version} + diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffCreateCmd.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffCreateCmd.java index 137f42536df..f1fd4b4afe1 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffCreateCmd.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffCreateCmd.java @@ -54,10 +54,7 @@ public class QuotaTariffCreateCmd extends BaseCmd { @Parameter(name = "value", type = CommandType.DOUBLE, required = true, description = "The quota tariff value of the resource as per the default unit.") private Double value; - @Parameter(name = ApiConstants.ACTIVATION_RULE, type = CommandType.STRING, description = "Quota tariff's activation rule. It can receive a JS script that results in either " + - "a boolean or a numeric value: if it results in a boolean value, the tariff value will be applied according to the result; if it results in a numeric value, the " + - "numeric value will be applied; if the result is neither a boolean nor a numeric value, the tariff will not be applied. If the rule is not informed, the tariff " + - "value will be applied.", length = 65535) + @Parameter(name = ApiConstants.ACTIVATION_RULE, type = CommandType.STRING, description = ApiConstants.PARAMETER_DESCRIPTION_ACTIVATION_RULE, length = 65535) private String activationRule; @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "The effective start date on/after which the quota tariff is effective. Inform null to " + @@ -80,7 +77,7 @@ public class QuotaTariffCreateCmd extends BaseCmd { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create new quota tariff."); } - QuotaTariffResponse response = responseBuilder.createQuotaTariffResponse(result); + QuotaTariffResponse response = responseBuilder.createQuotaTariffResponse(result, true); response.setResponseName(getCommandName()); setResponseObject(response); } diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffListCmd.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffListCmd.java index b4e8c868e40..d054d545931 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffListCmd.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffListCmd.java @@ -17,15 +17,18 @@ package org.apache.cloudstack.api.command; import com.cloud.user.Account; +import com.cloud.user.User; import com.cloud.utils.Pair; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.QuotaResponseBuilder; import org.apache.cloudstack.api.response.QuotaTariffResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.quota.vo.QuotaTariffVO; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @@ -59,20 +62,29 @@ public class QuotaTariffListCmd extends BaseListCmd { + "list all, including the removed ones. The default is false.", since = "4.18.0.0") private boolean listAll = false; - public QuotaTariffListCmd() { - super(); - } + @Parameter(name = ApiConstants.LIST_ONLY_REMOVED, type = CommandType.BOOLEAN, description = "If set to true, we will list only the removed tariffs." + + " The default is false.") + private boolean listOnlyRemoved = false; + + @Parameter(name = ApiConstants.ID, type = CommandType.STRING, description = "The quota tariff's id.", validations = {ApiArgValidator.UuidString}) + private String id; @Override public void execute() { final Pair, Integer> result = _responseBuilder.listQuotaTariffPlans(this); + User user = CallContext.current().getCallingUser(); + boolean returnActivationRules = _responseBuilder.isUserAllowedToSeeActivationRules(user); + if (!returnActivationRules) { + logger.debug("User [{}] does not have permission to create or update quota tariffs, therefore we will not return the activation rules.", user.getUuid()); + } + final List responses = new ArrayList<>(); - logger.trace(String.format("Adding quota tariffs [%s] to response of API quotaTariffList.", ReflectionToStringBuilderUtils.reflectCollection(responses))); + logger.trace("Adding quota tariffs [{}] to response of API quotaTariffList.", ReflectionToStringBuilderUtils.reflectCollection(responses)); for (final QuotaTariffVO resource : result.first()) { - responses.add(_responseBuilder.createQuotaTariffResponse(resource)); + responses.add(_responseBuilder.createQuotaTariffResponse(resource, returnActivationRules)); } final ListResponse response = new ListResponse<>(); @@ -106,4 +118,15 @@ public class QuotaTariffListCmd extends BaseListCmd { return listAll; } + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public boolean isListOnlyRemoved() { + return listOnlyRemoved; + } } diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmd.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmd.java index 6370cc57e4e..b5766875507 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmd.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmd.java @@ -63,10 +63,8 @@ public class QuotaTariffUpdateCmd extends BaseCmd { since = "4.18.0.0") private String description; - @Parameter(name = ApiConstants.ACTIVATION_RULE, type = CommandType.STRING, description = "Quota tariff's activation rule. It can receive a JS script that results in either " + - "a boolean or a numeric value: if it results in a boolean value, the tariff value will be applied according to the result; if it results in a numeric value, the " + - "numeric value will be applied; if the result is neither a boolean nor a numeric value, the tariff will not be applied. If the rule is not informed, the tariff " + - "value will be applied. Inform empty to remove the activation rule.", length = 65535, since = "4.18.0.0") + @Parameter(name = ApiConstants.ACTIVATION_RULE, type = CommandType.STRING, description = ApiConstants.PARAMETER_DESCRIPTION_ACTIVATION_RULE + + " Inform empty to remove the activation rule.", length = 65535, since = "4.18.0.0") private String activationRule; @Parameter(name = ApiConstants.POSITION, type = CommandType.INTEGER, description = "Position in the execution sequence for tariffs of the same type", since = "4.20.0.0") @@ -119,7 +117,7 @@ public class QuotaTariffUpdateCmd extends BaseCmd { if (result == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update quota tariff plan"); } - final QuotaTariffResponse response = _responseBuilder.createQuotaTariffResponse(result); + final QuotaTariffResponse response = _responseBuilder.createQuotaTariffResponse(result, true); response.setResponseName(getCommandName()); setResponseObject(response); } diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java index ecbb809b60b..c635551aeb5 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java @@ -16,6 +16,7 @@ //under the License. package org.apache.cloudstack.api.response; +import com.cloud.user.User; import org.apache.cloudstack.api.command.QuotaBalanceCmd; import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd; import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd; @@ -41,7 +42,9 @@ public interface QuotaResponseBuilder { Pair, Integer> listQuotaTariffPlans(QuotaTariffListCmd cmd); - QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO configuration); + QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO quotaTariff, boolean returnActivationRule); + + boolean isUserAllowedToSeeActivationRules(User user); QuotaStatementResponse createQuotaStatementResponse(List quotaUsage); diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java index 88e90cc9ba9..1c486759e43 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java @@ -52,6 +52,7 @@ import org.apache.cloudstack.api.command.QuotaTariffCreateCmd; import org.apache.cloudstack.api.command.QuotaTariffListCmd; import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.discovery.ApiDiscoveryService; import org.apache.cloudstack.quota.QuotaManager; import org.apache.cloudstack.quota.QuotaManagerImpl; import org.apache.cloudstack.quota.QuotaService; @@ -135,8 +136,11 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder { private final Class[] assignableClasses = {GenericPresetVariable.class, ComputingResources.class}; + @Inject + private ApiDiscoveryService apiDiscoveryService; + @Override - public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) { + public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff, boolean returnActivationRule) { final QuotaTariffResponse response = new QuotaTariffResponse(); response.setUsageType(tariff.getUsageType()); response.setUsageName(tariff.getUsageName()); @@ -146,13 +150,15 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder { response.setEffectiveOn(tariff.getEffectiveOn()); response.setUsageTypeDescription(tariff.getUsageTypeDescription()); response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value()); - response.setActivationRule(tariff.getActivationRule()); response.setName(tariff.getName()); response.setEndDate(tariff.getEndDate()); response.setDescription(tariff.getDescription()); response.setId(tariff.getUuid()); response.setRemoved(tariff.getRemoved()); response.setPosition(tariff.getPosition()); + if (returnActivationRule) { + response.setActivationRule(tariff.getActivationRule()); + } return response; } @@ -228,6 +234,11 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder { } } + public boolean isUserAllowedToSeeActivationRules(User user) { + List apiList = (List) apiDiscoveryService.listApis(user, null).getResponses(); + return apiList.stream().anyMatch(response -> StringUtils.equalsAny(response.getName(), "quotaTariffCreate", "quotaTariffUpdate")); + } + @Override public QuotaBalanceResponse createQuotaBalanceResponse(List quotaBalance, Date startDate, Date endDate) { if (quotaBalance == null || quotaBalance.isEmpty()) { @@ -400,11 +411,14 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder { boolean listAll = cmd.isListAll(); Long startIndex = cmd.getStartIndex(); Long pageSize = cmd.getPageSizeVal(); + String uuid = cmd.getId(); + boolean listOnlyRemoved = cmd.isListOnlyRemoved(); + String keyword = cmd.getKeyword(); - logger.debug(String.format("Listing quota tariffs for parameters [%s].", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(cmd, "effectiveDate", - "endDate", "listAll", "name", "page", "pageSize", "usageType"))); + logger.debug("Listing quota tariffs for parameters [{}].", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(cmd, "effectiveDate", + "endDate", "listAll", "name", "page", "pageSize", "usageType", "uuid", "listOnlyRemoved", "keyword")); - return _quotaTariffDao.listQuotaTariffs(startDate, endDate, usageType, name, null, listAll, startIndex, pageSize); + return _quotaTariffDao.listQuotaTariffs(startDate, endDate, usageType, name, uuid, listAll, listOnlyRemoved, startIndex, pageSize, keyword); } @Override diff --git a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java index f5ce92ae014..a98d3d611de 100644 --- a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java +++ b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java @@ -16,15 +16,18 @@ // under the License. package org.apache.cloudstack.api.command; +import com.cloud.user.User; import junit.framework.TestCase; import org.apache.cloudstack.api.response.QuotaResponseBuilder; import org.apache.cloudstack.api.response.QuotaTariffResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.quota.constant.QuotaTypes; import org.apache.cloudstack.quota.vo.QuotaTariffVO; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.MockedStatic; import org.mockito.junit.MockitoJUnitRunner; import java.lang.reflect.Field; @@ -40,6 +43,12 @@ public class QuotaTariffListCmdTest extends TestCase { @Mock QuotaResponseBuilder responseBuilder; + @Mock + User userMock; + + @Mock + CallContext callContextMock; + @Test public void testQuotaTariffListCmd() throws NoSuchFieldException, IllegalAccessException { QuotaTariffListCmd cmd = new QuotaTariffListCmd(); @@ -48,17 +57,24 @@ public class QuotaTariffListCmdTest extends TestCase { rbField.setAccessible(true); rbField.set(cmd, responseBuilder); - List quotaTariffVOList = new ArrayList(); + List quotaTariffVOList = new ArrayList<>(); QuotaTariffVO tariff = new QuotaTariffVO(); tariff.setEffectiveOn(new Date()); tariff.setCurrencyValue(new BigDecimal(100)); tariff.setUsageType(QuotaTypes.VOLUME); quotaTariffVOList.add(new QuotaTariffVO()); - Mockito.when(responseBuilder.listQuotaTariffPlans(Mockito.eq(cmd))).thenReturn(new Pair<>(quotaTariffVOList, quotaTariffVOList.size())); - Mockito.when(responseBuilder.createQuotaTariffResponse(Mockito.any(QuotaTariffVO.class))).thenReturn(new QuotaTariffResponse()); - cmd.execute(); - Mockito.verify(responseBuilder, Mockito.times(1)).createQuotaTariffResponse(Mockito.any(QuotaTariffVO.class)); + try (MockedStatic callContextStaticMock = Mockito.mockStatic(CallContext.class)) { + Mockito.when(responseBuilder.listQuotaTariffPlans(Mockito.eq(cmd))).thenReturn(new Pair<>(quotaTariffVOList, quotaTariffVOList.size())); + callContextStaticMock.when(CallContext::current).thenReturn(callContextMock); + Mockito.when(callContextMock.getCallingUser()).thenReturn(userMock); + Mockito.when(responseBuilder.isUserAllowedToSeeActivationRules(userMock)).thenReturn(true); + Mockito.when(responseBuilder.createQuotaTariffResponse(Mockito.any(QuotaTariffVO.class), Mockito.eq(true))).thenReturn(new QuotaTariffResponse()); + + cmd.execute(); + } + + Mockito.verify(responseBuilder, Mockito.times(1)).createQuotaTariffResponse(Mockito.any(QuotaTariffVO.class), Mockito.eq(true)); } } diff --git a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java index 22d78d6794e..7a4d1a75356 100644 --- a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java +++ b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java @@ -60,8 +60,8 @@ public class QuotaTariffUpdateCmdTest extends TestCase { } Mockito.when(responseBuilder.updateQuotaTariffPlan(Mockito.eq(cmd))).thenReturn(tariff); - Mockito.when(responseBuilder.createQuotaTariffResponse(Mockito.eq(tariff))).thenReturn(new QuotaTariffResponse()); + Mockito.when(responseBuilder.createQuotaTariffResponse(Mockito.eq(tariff), Mockito.eq(true))).thenReturn(new QuotaTariffResponse()); cmd.execute(); - Mockito.verify(responseBuilder, Mockito.times(1)).createQuotaTariffResponse(Mockito.eq(tariff)); + Mockito.verify(responseBuilder, Mockito.times(1)).createQuotaTariffResponse(Mockito.eq(tariff), Mockito.eq(true)); } } diff --git a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java index 71e38a5ab8c..fd359525893 100644 --- a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java +++ b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java @@ -55,7 +55,10 @@ import org.apache.cloudstack.quota.vo.QuotaCreditsVO; import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO; import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO; import org.apache.cloudstack.quota.vo.QuotaTariffVO; +import org.apache.cloudstack.discovery.ApiDiscoveryService; + import org.apache.commons.lang3.time.DateUtils; + import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -69,6 +72,7 @@ import com.cloud.user.Account; import com.cloud.user.AccountVO; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; +import com.cloud.user.User; import junit.framework.TestCase; import org.mockito.junit.MockitoJUnitRunner; @@ -91,6 +95,12 @@ public class QuotaResponseBuilderImplTest extends TestCase { @Mock UserDao userDaoMock; + @Mock + User userMock; + + @Mock + ApiDiscoveryService discoveryServiceMock; + @Mock QuotaService quotaServiceMock; @@ -164,11 +174,29 @@ public class QuotaResponseBuilderImplTest extends TestCase { @Test public void testQuotaResponse() { QuotaTariffVO tariffVO = makeTariffTestData(); - QuotaTariffResponse response = quotaResponseBuilderSpy.createQuotaTariffResponse(tariffVO); + QuotaTariffResponse response = quotaResponseBuilderSpy.createQuotaTariffResponse(tariffVO, true); assertTrue(tariffVO.getUsageType() == response.getUsageType()); assertTrue(tariffVO.getCurrencyValue().equals(response.getTariffValue())); } + @Test + public void createQuotaTariffResponseTestIfReturnsActivationRuleWithPermission() { + QuotaTariffVO tariff = makeTariffTestData(); + tariff.setActivationRule("x === 10"); + + QuotaTariffResponse tariffResponse = quotaResponseBuilderSpy.createQuotaTariffResponse(tariff, true); + assertEquals("x === 10", tariffResponse.getActivationRule()); + } + + @Test + public void createQuotaTariffResponseTestIfReturnsActivationRuleWithoutPermission() { + QuotaTariffVO tariff = makeTariffTestData(); + tariff.setActivationRule("x === 10"); + + QuotaTariffResponse tariffResponse = quotaResponseBuilderSpy.createQuotaTariffResponse(tariff, false); + assertNull(tariffResponse.getActivationRule()); + } + @Test public void testAddQuotaCredits() { final long accountId = 2L; @@ -569,4 +597,52 @@ public class QuotaResponseBuilderImplTest extends TestCase { Mockito.verify(quotaTariffVoMock).setPosition(position); } + + @Test + public void isUserAllowedToSeeActivationRulesTestWithPermissionToCreateTariff() { + ApiDiscoveryResponse response = new ApiDiscoveryResponse(); + response.setName("quotaTariffCreate"); + + List cmdList = new ArrayList<>(); + cmdList.add(response); + + ListResponse responseList = new ListResponse<>(); + responseList.setResponses(cmdList); + + Mockito.doReturn(responseList).when(discoveryServiceMock).listApis(userMock, null); + + assertTrue(quotaResponseBuilderSpy.isUserAllowedToSeeActivationRules(userMock)); + } + + @Test + public void isUserAllowedToSeeActivationRulesTestWithPermissionToUpdateTariff() { + ApiDiscoveryResponse response = new ApiDiscoveryResponse(); + response.setName("quotaTariffUpdate"); + + List cmdList = new ArrayList<>(); + cmdList.add(response); + + ListResponse responseList = new ListResponse<>(); + responseList.setResponses(cmdList); + + Mockito.doReturn(responseList).when(discoveryServiceMock).listApis(userMock, null); + + assertTrue(quotaResponseBuilderSpy.isUserAllowedToSeeActivationRules(userMock)); + } + + @Test + public void isUserAllowedToSeeActivationRulesTestWithNoPermission() { + ApiDiscoveryResponse response = new ApiDiscoveryResponse(); + response.setName("testCmd"); + + List cmdList = new ArrayList<>(); + cmdList.add(response); + + ListResponse responseList = new ListResponse<>(); + responseList.setResponses(cmdList); + + Mockito.doReturn(responseList).when(discoveryServiceMock).listApis(userMock, null); + + assertFalse(quotaResponseBuilderSpy.isUserAllowedToSeeActivationRules(userMock)); + } } diff --git a/server/src/main/java/com/cloud/usage/UsageServiceImpl.java b/server/src/main/java/com/cloud/usage/UsageServiceImpl.java index 170ef1fdbbc..421d2587441 100644 --- a/server/src/main/java/com/cloud/usage/UsageServiceImpl.java +++ b/server/src/main/java/com/cloud/usage/UsageServiceImpl.java @@ -31,7 +31,6 @@ import com.cloud.utils.DateUtil; import org.apache.cloudstack.api.command.admin.usage.GenerateUsageRecordsCmd; import org.apache.cloudstack.api.command.admin.usage.ListUsageRecordsCmd; import org.apache.cloudstack.api.command.admin.usage.RemoveRawUsageRecordsCmd; -import org.apache.cloudstack.api.response.UsageTypeResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.usage.Usage; @@ -485,10 +484,4 @@ public class UsageServiceImpl extends ManagerBase implements UsageService, Manag } return true; } - - @Override - public List listUsageTypes() { - return UsageTypes.listUsageTypes(); - } - } diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 166097778c0..b42899fc631 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -161,6 +161,9 @@ "label.action.patch.systemvm.processing": "Patching System VM....", "label.action.project.add.account": "Add Account to project", "label.action.project.add.user": "Add User to project", +"label.action.quota.tariff.create": "Create Quota Tariff", +"label.action.quota.tariff.edit": "Edit Quota Tariff", +"label.action.quota.tariff.remove": "Remove Quota Tariff", "label.action.reboot.instance": "Reboot Instance", "label.action.reboot.router": "Reboot router", "label.action.reboot.systemvm": "Reboot System VM", @@ -865,6 +868,7 @@ "label.encrypt": "Encrypt", "label.encryptroot": "Encrypt Root Disk", "label.end": "End", +"label.end.date": "End date", "label.end.date.and.time": "End date and time", "label.end.ip": "End IP", "label.end.reserved.system.ip": "End reserved system IP", @@ -1727,6 +1731,8 @@ "label.quota.summary": "Summary", "label.quota.tariff": "Tariff", "label.quota.tariff.effectivedate": "Effective date", +"label.quota.tariff.position": "Position", +"label.quota.tariff.value": "Tariff value", "label.quota.total": "Total", "label.quota.type.name": "Usage Type", "label.quota.type.unit": "Usage unit", @@ -2060,6 +2066,7 @@ "label.sslverification": "SSL verification", "label.standard.us.keyboard": "Standard (US) keyboard", "label.start": "Start", +"label.start.date": "Start date", "label.start.date.and.time": "Start date and time", "label.start.ip": "Start IP", "label.start.lb.vm": "Start LB Instance", @@ -2586,6 +2593,10 @@ "message.action.primary.storage.scope.cluster": "Please confirm that you want to change the scope from zone to the specified cluster.
This operation will update the database and disconnect the storage pool from all hosts that were previously connected to the primary storage and are not part of the specified cluster.", "message.action.primary.storage.scope.zone": "Please confirm that you want to change the scope from cluster to zone.
This operation will update the database and connect the storage pool to all hosts of the zone running the same hypervisor as set on the storage pool.", "message.action.primarystorage.enable.maintenance.mode": "Warning: placing the primary storage into maintenance mode will cause all Instances using volumes from it to be stopped. Do you want to continue?", +"message.action.quota.tariff.create.error.namerequired": "Please, inform a name for the quota tariff.", +"message.action.quota.tariff.create.error.usagetyperequired": "Please, select the usage type of the quota tariff.", +"message.action.quota.tariff.create.error.valuerequired": "Please, inform a value for the quota tariff.", +"message.action.quota.tariff.remove": "Please confirm that you want to remove this Quota Tariff.", "message.action.reboot.instance": "Please confirm that you want to reboot this Instance.", "message.action.reboot.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to reboot this router.", "message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.", @@ -3194,6 +3205,8 @@ "message.protocol.description": "For XenServer, choose NFS, iSCSI, or PreSetup. For KVM, choose NFS, SharedMountPoint, RDB, CLVM or Gluster. For vSphere, choose NFS, PreSetup (VMFS or iSCSI or FiberChannel or vSAN or vVols) or DatastoreCluster. For Hyper-V, choose SMB/CIFS. For LXC, choose NFS or SharedMountPoint. For OVM, choose NFS or OCFS2.", "message.public.traffic.in.advanced.zone": "Public traffic is generated when Instances in the cloud access the internet. Publicly-accessible IPs must be allocated for this purpose. End Users can use the CloudStack UI to acquire these IPs to implement NAT between their guest Network and their public Network.

Provide at least one range of IP addresses for internet traffic.", "message.public.traffic.in.basic.zone": "Public traffic is generated when Instances in the cloud access the Internet or provide services to clients over the Internet. Publicly accessible IPs must be allocated for this purpose. When a Instance is created, an IP from this set of Public IPs will be allocated to the Instance in addition to the guest IP address. Static 1-1 NAT will be set up automatically between the public IP and the guest IP. End Users can also use the CloudStack UI to acquire additional IPs to implement static NAT between their Instances and the public IP.", +"message.quota.tariff.create.success": "Successfully created quota tariff \"{quotaTariff}\"", +"message.quota.tariff.update.success": "Successfully updated quota tariff \"{quotaTariff}\"", "message.read.accept.license.agreements": "Please read and accept the terms for the license agreements.", "message.read.admin.guide.scaling.up": "Please read the dynamic scaling section in the admin guide before scaling up.", "message.recover.vm": "Please confirm that you would like to recover this Instance.", @@ -3522,6 +3535,13 @@ "migrate.from": "Migrate from", "migrate.to": "Migrate to", "migrationPolicy": "Migration policy", +"placeholder.quota.tariff.description": "Quota tariff's description", +"placeholder.quota.tariff.enddate": "Quota tariff's end date", +"placeholder.quota.tariff.name": "Quota tariff's name", +"placeholder.quota.tariff.position": "Quota tariff's position in the execution sequence", +"placeholder.quota.tariff.startdate": "Quota tariff's start date", +"placeholder.quota.tariff.usagetype": "Quota tariff's usage type", +"placeholder.quota.tariff.value": "Quota tariff's value", "router.health.checks": "Health check", "side.by.side": "Side by Side", "state.completed": "Completed", @@ -3543,5 +3563,32 @@ "state.stopping": "Stopping", "state.suspended": "Suspended", "user.login": "Login", -"user.logout": "Logout" +"user.logout": "Logout", +"ALLOCATED_VM": "Allocated VM", +"BACKUP": "Backup", +"BACKUP_OBJECT": "Backup Object", +"IP_ADDRESS": "IP Address", +"LOAD_BALANCER_POLICY": "Load Balancer Policy", +"NETWORK": "Network", +"NETWORK_BYTES_RECEIVED": "Network Bytes Received", +"NETWORK_BYTES_SENT": "Network Bytes Sent", +"NETWORK_OFFERING": "Network Offering", +"RUNNING_VM": "Running VM", +"PORT_FORWARDING_RULE": "Port Forwarding Rule", +"SECURITY_GROUP": "Security Group", +"SNAPSHOT": "Snapshot", +"TEMPLATE": "Template", +"VM_DISK_BYTES_READ": "VM Disk (Bytes Read)", +"VM_DISK_BYTES_WRITE": "VM Disk (Bytes Write)", +"VM_DISK_IO_READ": "VM Disk (IO Read)", +"VM_DISK_IO_WRITE": "VM Disk (IO Write)", +"VM_SNAPSHOT": "VM Snapshot", +"VM_SNAPSHOT_ON_PRIMARY": "VM Snapshot on Primary", +"VOLUME": "Volume", +"VOLUME_SECONDARY": "Volume on Secondary", +"VPN_USERS": "VPN Users", +"Compute*Month": "Compute * Month", +"GB*Month": "GB * Month", +"IP*Month": "IP * Month", +"Policy*Month": "Policy * Month" } diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json index fec66ba4cef..82d527ae4c1 100644 --- a/ui/public/locales/pt_BR.json +++ b/ui/public/locales/pt_BR.json @@ -162,6 +162,7 @@ "label.action.vmsnapshot.revert": "Reverter snapshot de VM", "label.action.vmstoragesnapshot.create": "Criar snapshot de volume da VM", "label.actions": "A\u00e7\u00f5es", +"label.active": "Ativo", "label.activate.project": "Ativar projeto", "label.activeviewersessions": "Sess\u00f5es ativas", "label.add": "Adicionar", @@ -625,6 +626,7 @@ "label.enable.vpc.offering": "Habilitar oferta VPC", "label.enable.vpn": "Habilitar VPN", "label.end": "Fim", +"label.end.date": "Data de término", "label.end.date.and.time": "Data e hor\u00e1rio final", "label.end.ip": "IP final", "label.end.reserved.system.ip": "Fim dos IPs reservados para o sistema", @@ -1279,7 +1281,12 @@ "label.quotastate": "Estado da cota", "label.summary": "Sum\u00e1rio", "label.quota.tariff": "Tarifa", +"label.action.quota.tariff.create": "Criar tarifa", +"label.action.quota.tariff.edit": "Editar tarifa", +"label.action.quota.tariff.remove": "Remover tarifa", "label.quota.tariff.effectivedate": "Data efetiva", +"label.quota.tariff.position": "Posi\u00e7\u00e3o", +"label.quota.tariff.value": "Valor", "label.quota.total": "Total", "label.quota.type.name": "Tipo de uso", "label.quota.type.unit": "Unidade do uso", @@ -1338,6 +1345,7 @@ "label.remove.vmware.datacenter": "Remover datacenter VMware", "label.remove.vpc": "Remover VPC", "label.remove.vpc.offering": "Remover oferta VPC", +"label.removed": "Removido", "label.removing": "Removendo", "label.replace.acl": "Substituir ACL", "label.replace.acl.list": "Substituir lista ACL", @@ -1518,6 +1526,7 @@ "label.standard.us.keyboard": "Teclado padr\u00e3o (EUA)", "label.sslcertificates": "Certificados SSL", "label.start": "Iniciar", +"label.start.date": "Data de in\u00edcio", "label.start.date.and.time": "Data e hor\u00e1rio inicial", "label.start.ip": "IP do in\u00edcio", "label.start.lb.vm": "Iniciar VM LB", @@ -1674,7 +1683,7 @@ "label.upload.volume.from.url": "Carregar volume por URL", "label.url": "URL", "label.usageinterface": "Interface de uso", -"label.usagename": "Tipo", +"label.usagetype": "Tipo", "label.usageunit": "Unidade", "label.use.kubectl.access.cluster": "os arquivos kubectl e kubeconfig para acessar o cluster", "label.use.local.timezone": "Use o fuso hor\u00e1rio local", @@ -1858,6 +1867,10 @@ "message.action.instance.reset.password": "Por favor confirme que voc\u00ea deseja alterar a senha de root para est\u00e1 m\u00e1quina virtual.", "message.action.manage.cluster": "Confirma a vincula\u00e7\u00e3o do cluster.", "message.action.primarystorage.enable.maintenance.mode": "Aviso: colocar o armazenamento prim\u00e1rio em modo de manuten\u00e7\u00e3o ir\u00e1 causar a parada de todas as VMs hospedadas nesta unidade. Deseja continuar?", +"message.action.quota.tariff.create.error.namerequired": "Por favor, informe o nome da tarifa.", +"message.action.quota.tariff.create.error.usagetyperequired": "Por favor, selecione o tipo da tarifa.", +"message.action.quota.tariff.create.error.valuerequired": "Por favor, informe o valor da tarifa.", +"message.action.quota.tariff.remove": "Por favor, confirme que voc\u00ea deseja remover a tarifa.", "message.action.reboot.instance": "Por favor, confirme que voc\u00ea deseja reiniciar esta inst\u00e2ncia.", "message.action.reboot.router": "Confirme que voc\ufffd deseja reiniciar este roteador.", "message.action.reboot.systemvm": "Confirme que voc\u00ea deseja reiniciar esta VM de sistema.", @@ -2280,6 +2293,8 @@ "message.protocol.description": "Para XenServer, escolha NFS, iSCSI, ou PreSetup. para KVM, escolha NFS, SharedMountPoint, RDB, CLVM ou Gluster. para vSphere, escolha NFS, PreSetup (VMFS, iSCSI, fiberChannel, vSAN ou vVols) ou datastoreCluster. para Hyper-V, escolha SMB/CIFS. para LXC, escolha NFS ou SharedMountPoint. para OVM, escolha NFS ou ocfs2.", "message.public.traffic.in.advanced.zone": "O tr\u00e1fego p\u00fablico \u00e9 gerado quando as VMs na nuvem acessam a internet. Os IPs acess\u00edveis ao p\u00fablico devem ser alocados para essa finalidade. Os usu\u00e1rios finais podem usar a interface do usu\u00e1rio CloudStack para adquirir esses IPs afim de implementar NAT entre a sua rede de guests e sua rede p\u00fablica.

Forne\u00e7a pelo menos um intervalo de endere\u00e7os IP para o tr\u00e1fego de internet.", "message.public.traffic.in.basic.zone": "O tr\u00e1fego p\u00fablico \u00e9 gerado quando as VMs na nuvem acessam a internet ou prestam servi\u00e7os aos clientes atrav\u00e9s da internet. Os IPs acess\u00edveis ao p\u00fablico devem ser alocados para essa finalidade. Quando uma inst\u00e2ncia \u00e9 criada, um IP a partir deste conjunto de IPs P\u00fablicos ser\u00e3o destinados \u00e0 inst\u00e2ncia, al\u00e9m do endere\u00e7o IP guest. Um NAT est\u00e1tico 1-1 ser\u00e1 criada automaticamente entre o IP p\u00fablico e IP guest. Os usu\u00e1rios finais tamb\u00e9m podem usar a interface de usu\u00e1rio CloudStack para adquirir IPs adicionais afim de se implementar NAT est\u00e1tico entre suas inst\u00e2ncias e o IP p\u00fablico.", +"message.quota.tariff.create.success": "Tarifa \"{quotaTariff}\" criada com sucesso", +"message.quota.tariff.update.success": "Tarifa \"{quotaTariff}\" atualizada com sucesso", "message.read.accept.license.agreements": "Leia e aceite os termos dos contratos de licen\u00e7a.", "message.read.admin.guide.scaling.up": "Por favor leia a sess\u00e3o sobre escalonamento din\u00e2mico no guia do administrador antes de escalonar.", "message.recover.vm": "Por favor, confirme a recupera\u00e7\u00e3o desta VM.", @@ -2495,6 +2510,13 @@ "migrate.from": "Migrar de", "migrate.to": "Migrar para", "migrationPolicy": "Pol\u00edtica de migra\u00e7\u00e3o", +"placeholder.quota.tariff.description": "Descri\u00e7\u00e3o", +"placeholder.quota.tariff.enddate": "Data de t\u00e9rmino", +"placeholder.quota.tariff.name": "Nome", +"placeholder.quota.tariff.position": "Posi\u00e7\u00e3o da tarifa do Quota na sequ\u00eancia de execu\u00e7\u00e3o", +"placeholder.quota.tariff.startdate": "Data de in\u00edcio", +"placeholder.quota.tariff.usagetype": "Tipo", +"placeholder.quota.tariff.value": "Valor", "router.health.checks": "Checagem de sa\u00fade", "side.by.side": "Lado a lado", "state.completed": "Completo", @@ -2516,5 +2538,32 @@ "state.stopping": "Parando", "state.suspended": "Suspendido", "user.login": "Entrar", -"user.logout": "Sair" +"user.logout": "Sair", +"ALLOCATED_VM": "VM alocada", +"BACKUP": "Backup", +"BACKUP_OBJECT": "Objeto backup", +"IP_ADDRESS": "Endere\u00e7o IP", +"LOAD_BALANCER_POLICY": "Pol\u00edtica de balanceamento de carga", +"NETWORK": "Rede", +"NETWORK_BYTES_RECEIVED": "Bytes recebidos na rede", +"NETWORK_BYTES_SENT": "Bytes enviados na rede", +"NETWORK_OFFERING": "Oferta de rede", +"RUNNING_VM": "VM rodando", +"PORT_FORWARDING_RULE": "Regra de redirecionamento de porta", +"SECURITY_GROUP": "Grupo de seguran\u00e7a", +"SNAPSHOT": "Snapshot", +"TEMPLATE": "Template", +"VM_DISK_BYTES_READ": "Leitura do disco da VM (bytes)", +"VM_DISK_BYTES_WRITE": "Escrita no disco da VM (bytes)", +"VM_DISK_IO_READ": "Leitura do disco da VM (IO)", +"VM_DISK_IO_WRITE": "Escrita no disco da VM (IO)", +"VM_SNAPSHOT": "Snapshot de VM", +"VM_SNAPSHOT_ON_PRIMARY": "Snapshot de VM no armazenamento prim\u00e1rio", +"VOLUME": "Volume", +"VOLUME_SECONDARY": "Volume no armazenamento secund\u00e1rio", +"VPN_USERS": "Usu\u00e1rios de VPN", +"Compute*Month": "Recurso * M\u00eas", +"GB*Month": "GB * M\u00eas", +"IP*Month": "IP * M\u00eas", +"Policy*Month": "Pol\u00edticas de Rede * M\u00eas" } diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue index c9ab6b89ec8..017d304e39b 100644 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@ -38,8 +38,8 @@ :dataSource="fetchDetails()"> -