diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java index 12265665d30..0f790cac264 100644 --- a/api/src/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseCmd.java @@ -97,7 +97,7 @@ public abstract class BaseCmd { GET, POST, PUT, DELETE } public static enum CommandType { - BOOLEAN, DATE, FLOAT, INTEGER, SHORT, LIST, LONG, OBJECT, MAP, STRING, TZDATE, UUID + BOOLEAN, DATE, FLOAT, DOUBLE, INTEGER, SHORT, LIST, LONG, OBJECT, MAP, STRING, TZDATE, UUID } private Object _responseObject; diff --git a/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java b/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java index fd8173d416d..76af050d2ca 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java @@ -38,6 +38,7 @@ import com.cloud.utils.Pair; @APICommand(name = "listUsageRecords", description = "Lists usage records for accounts", responseObject = UsageRecordResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class GetUsageRecordsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(GetUsageRecordsCmd.class.getName()); private static final String s_name = "listusagerecordsresponse"; @@ -111,6 +112,30 @@ public class GetUsageRecordsCmd extends BaseListCmd { public String getUsageId() { return usageId; } + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + public void setUsageId(String usageId) { + this.usageId = usageId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// diff --git a/api/src/org/apache/cloudstack/usage/UsageTypes.java b/api/src/org/apache/cloudstack/usage/UsageTypes.java index 3edfd0b66aa..2163df7efc5 100644 --- a/api/src/org/apache/cloudstack/usage/UsageTypes.java +++ b/api/src/org/apache/cloudstack/usage/UsageTypes.java @@ -22,6 +22,8 @@ import java.util.List; import org.apache.cloudstack.api.response.UsageTypeResponse; public class UsageTypes { + + /* Any changes here should also reflect in cloud_usage.quota_mapping table */ public static final int RUNNING_VM = 1; public static final int ALLOCATED_VM = 2; // used for tracking how long storage has been allocated for a VM public static final int IP_ADDRESS = 3; diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index c817cb9d271..383712ee4d6 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -783,7 +783,6 @@ listOpenDaylightControllers=1 addGloboDnsHost=1 ### Quota Service -quotaTypes=15 quotaMapping=15 quotaEditMapping=15 quotaRefresh=15 diff --git a/engine/schema/src/com/cloud/usage/UsageVO.java b/engine/schema/src/com/cloud/usage/UsageVO.java index c46abb3a9b5..bf5f551d248 100644 --- a/engine/schema/src/com/cloud/usage/UsageVO.java +++ b/engine/schema/src/com/cloud/usage/UsageVO.java @@ -103,6 +103,17 @@ public class UsageVO implements Usage, InternalIdentity { @Temporal(value = TemporalType.TIMESTAMP) private Date endDate = null; + @Column(name = "quota_calculated") + private Integer quotaCalculated = null; + + public Integer getQuotaCalculated() { + return quotaCalculated; + } + + public void setQuotaCalculated(Integer quotaCalculated) { + this.quotaCalculated = quotaCalculated; + } + public UsageVO() { } diff --git a/framework/db/src/com/cloud/utils/db/GenericDaoBase.java b/framework/db/src/com/cloud/utils/db/GenericDaoBase.java index e75646a79fa..b76213ccd13 100755 --- a/framework/db/src/com/cloud/utils/db/GenericDaoBase.java +++ b/framework/db/src/com/cloud/utils/db/GenericDaoBase.java @@ -392,6 +392,8 @@ public abstract class GenericDaoBase extends Compone final String sql = str.toString(); + s_logger.debug("Sql = " + sql); + PreparedStatement pstmt = null; final List result = new ArrayList(); try { diff --git a/plugins/database/quota/resources/META-INF/cloudstack/quota/spring-quota-context.xml b/plugins/database/quota/resources/META-INF/cloudstack/quota/spring-quota-context.xml index 628324c67be..5a22981fbbc 100644 --- a/plugins/database/quota/resources/META-INF/cloudstack/quota/spring-quota-context.xml +++ b/plugins/database/quota/resources/META-INF/cloudstack/quota/spring-quota-context.xml @@ -27,12 +27,11 @@ - + - diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaCreditsCmd.java b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaCreditsCmd.java index 812b5481aa4..29f4efccb57 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaCreditsCmd.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaCreditsCmd.java @@ -43,13 +43,13 @@ public class QuotaCreditsCmd extends BaseCmd { private static final String s_name = "quotacreditsresponse"; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Account Id for which quota credits need to be added") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required=true, description = "Account Id for which quota credits need to be added") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Domain for which quota credits need to be added") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required=true, entityType = DomainResponse.class, description = "Domain for which quota credits need to be added") private Long domainId; - @Parameter(name = ApiConstants.VALUE, type = CommandType.INTEGER, entityType = DomainResponse.class, description = "Value of the credits to be added+, subtracted-") + @Parameter(name = ApiConstants.VALUE, type = CommandType.DOUBLE, required=true, description = "Value of the credits to be added+, subtracted-") private Integer value; public String getAccountName() { diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEditMappingCmd.java b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEditMappingCmd.java index 73a66aa74e7..f2580bf5d5d 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEditMappingCmd.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEditMappingCmd.java @@ -25,11 +25,10 @@ import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.QuotaConfigurationResponse; import org.apache.cloudstack.api.response.QuotaCreditsResponse; -import org.apache.cloudstack.quota.QuotaConfigurationVO; +import org.apache.cloudstack.quota.QuotaMappingVO; import org.apache.cloudstack.quota.QuotaDBUtilsImpl; import com.cloud.user.Account; @@ -45,25 +44,25 @@ public class QuotaEditMappingCmd extends BaseCmd { @Inject QuotaDBUtilsImpl _quotaDBUtils; - @Parameter(name = "type", type = CommandType.STRING, required = false, description = "Usage type of the resource") - private String usageType; + @Parameter(name = "type", type = CommandType.INTEGER, required = true, description = "Integer value for the usage type of the resource") + private Integer usageType; - @Parameter(name = "value", type = CommandType.INTEGER, entityType = DomainResponse.class, description = "The quota vale of the resource as per the default unit") - private Integer value; + @Parameter(name = "value", type = CommandType.DOUBLE, required = true, description = "The quota vale of the resource as per the default unit") + private Double value; - public String getUsageType() { + public int getUsageType() { return usageType; } - public void setUsageType(String usageType) { + public void setUsageType(int usageType) { this.usageType = usageType; } - public Integer getValue() { + public Double getValue() { return value; } - public void setValue(Integer value) { + public void setValue(Double value) { this.value = value; } @@ -83,12 +82,12 @@ public class QuotaEditMappingCmd extends BaseCmd { @Override public void execute() { - final Pair, Integer> result = _quotaDBUtils.editQuotaMapping(this); + final Pair, Integer> result = _quotaDBUtils.editQuotaMapping(this); final List responses = new ArrayList(); - for (final QuotaConfigurationVO resource : result.first()) { + for (final QuotaMappingVO resource : result.first()) { final QuotaConfigurationResponse configurationResponse = _quotaDBUtils.createQuotaConfigurationResponse(resource); - configurationResponse.setObjectName("QuotaConfiguration"); + configurationResponse.setObjectName("QuotaMapping"); responses.add(configurationResponse); } diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateAddCmd.java b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateAddCmd.java index 17458e7a1c7..44aebc3b95e 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateAddCmd.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateAddCmd.java @@ -29,10 +29,10 @@ public class QuotaEmailTemplateAddCmd extends BaseListCmd { private static final String s_name = "quotaemailtemplateresponse"; - @Parameter(name = "templatename", type = CommandType.STRING, description = "The name of email template") + @Parameter(name = "templatename", type = CommandType.STRING, required=true, description = "The name of email template") private String templateName; - @Parameter(name = "templatetext", type = CommandType.STRING, description = "The text of the email") + @Parameter(name = "templatetext", type = CommandType.STRING, required=true, description = "The text of the email") private Long templateText; @Parameter(name = "locale", type = CommandType.STRING, description = "The locale of the email text") diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaMapping.java b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaMapping.java index 7e5f69fe85f..a0f4b510c88 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaMapping.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaMapping.java @@ -27,7 +27,7 @@ import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.QuotaConfigurationResponse; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.quota.QuotaConfigurationVO; +import org.apache.cloudstack.quota.QuotaMappingVO; import org.apache.cloudstack.quota.QuotaDBUtilsImpl; import com.cloud.user.Account; @@ -57,10 +57,10 @@ public class QuotaMapping extends BaseListCmd { @Override public void execute() { - final Pair, Integer> result = _quotaDBUtils.listConfigurations(this); + final Pair, Integer> result = _quotaDBUtils.listConfigurations(this); final List responses = new ArrayList(); - for (final QuotaConfigurationVO resource : result.first()) { + for (final QuotaMappingVO resource : result.first()) { final QuotaConfigurationResponse configurationResponse = _quotaDBUtils.createQuotaConfigurationResponse(resource); configurationResponse.setObjectName("QuotaConfiguration"); responses.add(configurationResponse); diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaRefreshCmd.java b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaRefreshCmd.java index a8ba0967060..6ad84422f01 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaRefreshCmd.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaRefreshCmd.java @@ -16,11 +16,14 @@ //under the License. package org.apache.cloudstack.api.command; +import javax.inject.Inject; + import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.QuotaRefreshResponse; +import org.apache.cloudstack.quota.QuotaManagerImpl; import com.cloud.user.Account; @@ -31,10 +34,18 @@ public class QuotaRefreshCmd extends BaseCmd { private static final String s_name = "quotarefreshresponse"; + @Inject + QuotaManagerImpl _quotaManager; + public QuotaRefreshCmd() { super(); } + public QuotaRefreshCmd(final QuotaManagerImpl quotaManager) { + super(); + _quotaManager = quotaManager; + } + @Override public String getCommandName() { return s_name; @@ -42,6 +53,7 @@ public class QuotaRefreshCmd extends BaseCmd { @Override public void execute() throws ServerApiException { + _quotaManager.calculateQuotaUsage(); final QuotaRefreshResponse response = new QuotaRefreshResponse("Success"); setResponseObject(response); } diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaStatementCmd.java b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaStatementCmd.java index 305a26fa2a4..723080474fa 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaStatementCmd.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaStatementCmd.java @@ -35,10 +35,10 @@ public class QuotaStatementCmd extends BaseListCmd { private static final String s_name = "quotastatementresponse"; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Optional, Account Id for which statement needs to be generated") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required=true, description = "Optional, Account Id for which statement needs to be generated") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Optional, If domain Id is given and the caller is domain admin then the statement is generated for domain.") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required=true, entityType = DomainResponse.class, description = "Optional, If domain Id is given and the caller is domain admin then the statement is generated for domain.") private Long domainId; public QuotaStatementCmd() { diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaTypesCmd.java b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaTypesCmd.java deleted file mode 100644 index ae5e02701d4..00000000000 --- a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaTypesCmd.java +++ /dev/null @@ -1,61 +0,0 @@ -//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.api.command; - -import java.util.List; - -import org.apache.log4j.Logger; -import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.BaseListCmd; -import org.apache.cloudstack.api.response.QuotaConfigurationResponse; -import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.api.response.QuotaTypeResponse; -import org.apache.cloudstack.quota.QuotaUsageTypes; - -import com.cloud.user.Account; - -@APICommand(name = "quotaTypes", responseObject = QuotaConfigurationResponse.class, description = "Lists all Quota type resources", since = "4.2.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class QuotaTypesCmd extends BaseListCmd { - - public static final Logger s_logger = Logger.getLogger(QuotaTypesCmd.class.getName()); - - private static final String s_name = "quotatyperesponse"; - - public QuotaTypesCmd() { - super(); - } - - @Override - public void execute() { - final List responses = QuotaUsageTypes.listQuotaUsageTypes(); - final ListResponse response = new ListResponse(); - response.setResponses(responses, responses.size()); - response.setResponseName(getCommandName()); - setResponseObject(response); - } - - @Override - public String getCommandName() { - return s_name; - } - - @Override - public long getEntityOwnerId() { - return Account.ACCOUNT_ID_SYSTEM; - } - -} diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaConfigurationResponse.java b/plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaConfigurationResponse.java index b76e47ed89a..3e56c3af233 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaConfigurationResponse.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaConfigurationResponse.java @@ -16,6 +16,8 @@ //under the License. package org.apache.cloudstack.api.response; +import java.math.BigDecimal; + import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponse; @@ -26,7 +28,11 @@ public class QuotaConfigurationResponse extends BaseResponse { @SerializedName("usageType") @Param(description = "usageType") - private String usageType; + private int usageType; + + @SerializedName("usageName") + @Param(description = "usageName") + private String usageName; @SerializedName("usageUnit") @Param(description = "usageUnit") @@ -38,7 +44,7 @@ public class QuotaConfigurationResponse extends BaseResponse { @SerializedName("currencyValue") @Param(description = "currencyValue") - private int currencyValue; + private BigDecimal currencyValue; @SerializedName("include") @Param(description = "include") @@ -52,16 +58,24 @@ public class QuotaConfigurationResponse extends BaseResponse { super(); } - public QuotaConfigurationResponse(final String usageType) { + public QuotaConfigurationResponse(final int usageType) { super(); this.usageType = usageType; } - public String getUsageType() { + public String getUsageName() { + return usageName; + } + + public void setUsageName(String usageName) { + this.usageName = usageName; + } + + public int getUsageType() { return usageType; } - public void setUsageType(String usageType) { + public void setUsageType(int usageType) { this.usageType = usageType; } @@ -81,11 +95,11 @@ public class QuotaConfigurationResponse extends BaseResponse { this.usageDiscriminator = usageDiscriminator; } - public int getCurrencyValue() { + public BigDecimal getCurrencyValue() { return currencyValue; } - public void setCurrencyValue(int currencyValue) { + public void setCurrencyValue(BigDecimal currencyValue) { this.currencyValue = currencyValue; } diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaConfig.java b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaConfig.java new file mode 100644 index 00000000000..1ced1ff0567 --- /dev/null +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaConfig.java @@ -0,0 +1,71 @@ +//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.quota; +import org.apache.cloudstack.framework.config.ConfigKey; + +public interface QuotaConfig { + + + public static final ConfigKey QuotaPluginEnabled = new ConfigKey("Advanced", Boolean.class, "quota.enable.service", "false", + "Indicates whether Quota plugin is enabled or not", true); + + public static final ConfigKey QuotaPeriodType = new ConfigKey("Advanced", String.class, "quota.period.type", "2", + "Quota period type: 1 for every x days, 2 for certain day of the month, 3 for yearly on activation day - default quota usage reporting cycl", true); + + public static final ConfigKey QuotaPeriod = new ConfigKey("Advanced", String.class, "quota.period.config", "15", + "The period config in number of days for the quota period type", true); + + public static final ConfigKey QuotaGenerateActivity = new ConfigKey("Advanced", String.class, "quota.activity.generate", "true", + "Set true to enable a detailed log of the quota usage, rating and billing activity, on daily basis. Valid values (true, false)", true); + + public static final ConfigKey QuotaEmailRecordOutgoing = new ConfigKey("Advanced", String.class, "quota.email.outgoing.record", "false", + "true means all the emails sent out will be stored in local DB, by default it is false", true); + + public static final ConfigKey QuotaEnableEnforcement = new ConfigKey("Advanced", String.class, "quota.enable.enforcement", "true", + "Enable the usage quota enforcement, i.e. on true exceeding quota the respective account will be locked.", true); + + public static final ConfigKey QuotaCurrencySymbol = new ConfigKey("Advanced", String.class, "quota.currency.symbol", "R", + "The symbol for the currency in use to measure usage.", true); + + public static final ConfigKey QuotaLimitCritical = new ConfigKey("Advanced", String.class, "quota.limit.critical", "80", + "A percentage limit for quota when it is reached user is sent and alert.", true); + + public static final ConfigKey QuotaLimitIncremental = new ConfigKey("Advanced", String.class, "quota.limit.increment", "5", + "Quota limit incremental", true); + + public static final ConfigKey QuotaSmtpHost = new ConfigKey("Advanced", String.class, "quota.usage.smtp.host", "", + "Quota SMTP host for quota related emails", true); + + public static final ConfigKey QuotaSmtpTimeout = new ConfigKey("Advanced", String.class, "quota.usage.smtp.connection.timeout", "60", + "Quota SMTP server connection timeout duration", true); + + public static final ConfigKey QuotaSmtpUser = new ConfigKey("Advanced", String.class, "quota.usage.smtp.user", "", + "Quota SMTP server username", true); + + public static final ConfigKey QuotaSmtpPassword = new ConfigKey("Advanced", String.class, "quota.usage.smtp.password", "", + "Quota SMTP server password", true); + + public static final ConfigKey QuotaSmtpPort = new ConfigKey("Advanced", String.class, "quota.usage.smtp.port", "", + "Quota SMTP port", true); + + public static final ConfigKey QuotaSmtpAuthType = new ConfigKey("Advanced", String.class, "quota.usage.smtp.useAuth", "", + "Quota SMTP authorization type", true); + + + +} \ No newline at end of file diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaDBUtils.java b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaDBUtils.java index fabb9c382c2..551dc94eef5 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaDBUtils.java +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaDBUtils.java @@ -28,12 +28,13 @@ import com.cloud.utils.component.PluggableService; public interface QuotaDBUtils extends PluggableService { - Pair, Integer> editQuotaMapping(QuotaEditMappingCmd cmd); + Pair, Integer> editQuotaMapping(QuotaEditMappingCmd cmd); - Pair, Integer> listConfigurations(QuotaMapping cmd); + Pair, Integer> listConfigurations(QuotaMapping cmd); - QuotaConfigurationResponse createQuotaConfigurationResponse(QuotaConfigurationVO configuration); + QuotaConfigurationResponse createQuotaConfigurationResponse(QuotaMappingVO configuration); QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Integer amount, Long updatedBy); + } diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaDBUtilsImpl.java b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaDBUtilsImpl.java index 9be68c296c1..8d0f5adef67 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaDBUtilsImpl.java +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaDBUtilsImpl.java @@ -16,6 +16,7 @@ //under the License. package org.apache.cloudstack.quota; +import java.math.BigDecimal; import java.util.Date; import java.util.List; @@ -26,7 +27,7 @@ import org.apache.cloudstack.api.command.QuotaEditMappingCmd; import org.apache.cloudstack.api.command.QuotaMapping; import org.apache.cloudstack.api.response.QuotaConfigurationResponse; import org.apache.cloudstack.api.response.QuotaCreditsResponse; -import org.apache.cloudstack.quota.dao.QuotaConfigurationDao; +import org.apache.cloudstack.quota.dao.QuotaMappingDao; import org.apache.cloudstack.quota.dao.QuotaCreditsDao; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -41,14 +42,16 @@ public class QuotaDBUtilsImpl { private static final Logger s_logger = Logger.getLogger(QuotaDBUtilsImpl.class.getName()); @Inject - private QuotaConfigurationDao _quotaConfigurationDao; + private QuotaMappingDao _quotaMappingDao; @Inject private QuotaCreditsDao _quotaCreditsDao; - public QuotaConfigurationResponse createQuotaConfigurationResponse(final QuotaConfigurationVO configuration) { + + public QuotaConfigurationResponse createQuotaConfigurationResponse(final QuotaMappingVO configuration) { final QuotaConfigurationResponse response = new QuotaConfigurationResponse(); response.setUsageType(configuration.getUsageType()); + response.setUsageName(configuration.getUsageName()); response.setUsageUnit(configuration.getUsageUnit()); response.setUsageDiscriminator(configuration.getUsageDiscriminator()); response.setCurrencyValue(configuration.getCurrencyValue()); @@ -57,28 +60,28 @@ public class QuotaDBUtilsImpl { return response; } - public Pair, Integer> listConfigurations(final QuotaMapping cmd) { - final Pair, Integer> result = _quotaConfigurationDao.searchConfigurations(); + public Pair, Integer> listConfigurations(final QuotaMapping cmd) { + final Pair, Integer> result = _quotaMappingDao.listAllMapping(); TransactionLegacy.open(TransactionLegacy.CLOUD_DB); return result; } - public Pair, Integer> editQuotaMapping(QuotaEditMappingCmd cmd) { - String resourceName = cmd.getUsageType(); - Integer quotaMapping = cmd.getValue(); + public Pair, Integer> editQuotaMapping(QuotaEditMappingCmd cmd) { + int resourceType = cmd.getUsageType(); + BigDecimal quotaCost = new BigDecimal(cmd.getValue()); TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); try { - QuotaConfigurationVO result = _quotaConfigurationDao.findByUsageType(resourceName); + QuotaMappingVO result = _quotaMappingDao.findByUsageType(resourceType); if (result == null) - throw new InvalidParameterValueException(resourceName); - s_logger.info("Old value=" + result.getCurrencyValue() + ", new value = " + quotaMapping + ", for resource =" + resourceName); - result.setCurrencyValue(quotaMapping); - _quotaConfigurationDao.persist(result); + throw new InvalidParameterValueException("Resource type " + resourceType); + s_logger.info("Old value=" + result.getCurrencyValue() + ", new value = " + quotaCost + ", for resource =" + resourceType); + result.setCurrencyValue(quotaCost); + _quotaMappingDao.persist(result); } finally { txn.close(); } - final Pair, Integer> result = _quotaConfigurationDao.searchConfigurations(); + final Pair, Integer> result = _quotaMappingDao.listAllMapping(); TransactionLegacy.open(TransactionLegacy.CLOUD_DB); return result; } @@ -97,4 +100,5 @@ public class QuotaDBUtilsImpl { return new QuotaCreditsResponse(result); } + } diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaJobVO.java b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaJobVO.java deleted file mode 100644 index 2619981334a..00000000000 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaJobVO.java +++ /dev/null @@ -1,184 +0,0 @@ -//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.quota; - -import java.util.Date; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; - -import org.apache.cloudstack.api.InternalIdentity; - -@Entity -@Table(name = "quota_job") -public class QuotaJobVO implements InternalIdentity { - - private static final long serialVersionUID = 78493278947123981L; - - public static final int JOB_TYPE_RECURRING = 0; - public static final int JOB_TYPE_SINGLE = 1; - - public static final int JOB_NOT_SCHEDULED = 0; - public static final int JOB_SCHEDULED = 1; - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id") - private Long id; - - @Column(name = "host") - private String host; - - @Column(name = "pid") - private Integer pid; - - @Column(name = "job_type") - private int jobType; - - @Column(name = "scheduled") - private int scheduled; - - @Column(name = "start_millis") - private long startMillis; - - @Column(name = "end_millis") - private long endMillis; - - @Column(name = "exec_time") - private long execTime; - - @Temporal(TemporalType.TIMESTAMP) - @Column(name = "start_date") - private Date startDate; - - @Temporal(TemporalType.TIMESTAMP) - @Column(name = "end_date") - private Date endDate; - - @Column(name = "success") - private Boolean success; - - @Temporal(TemporalType.TIMESTAMP) - @Column(name = "heartbeat") - private Date heartbeat; - - public QuotaJobVO() { - } - - @Override - public long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public Integer getPid() { - return pid; - } - - public void setPid(Integer pid) { - this.pid = pid; - } - - public int getJobType() { - return jobType; - } - - public void setJobType(int jobType) { - this.jobType = jobType; - } - - public int getScheduled() { - return scheduled; - } - - public void setScheduled(int scheduled) { - this.scheduled = scheduled; - } - - public long getStartMillis() { - return startMillis; - } - - public void setStartMillis(long startMillis) { - this.startMillis = startMillis; - } - - public long getEndMillis() { - return endMillis; - } - - public void setEndMillis(long endMillis) { - this.endMillis = endMillis; - } - - public long getExecTime() { - return execTime; - } - - public void setExecTime(long execTime) { - this.execTime = execTime; - } - - public Date getStartDate() { - return startDate; - } - - public void setStartDate(Date startDate) { - this.startDate = startDate; - } - - public Date getEndDate() { - return endDate; - } - - public void setEndDate(Date endDate) { - this.endDate = endDate; - } - - public Boolean getSuccess() { - return success; - } - - public void setSuccess(Boolean success) { - this.success = success; - } - - public Date getHeartbeat() { - return heartbeat; - } - - public void setHeartbeat(Date heartbeat) { - this.heartbeat = heartbeat; - } -} diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaManager.java b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaManager.java index 5649ce29e73..0a1ee9d41d9 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaManager.java +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaManager.java @@ -20,6 +20,6 @@ import com.cloud.utils.component.PluggableService; public interface QuotaManager extends PluggableService { - public void calculateQuotaUsage(QuotaJobVO job, long startDateMillis, long endDateMillis); + public void calculateQuotaUsage(); } diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaManagerImpl.java b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaManagerImpl.java index 72614335c7f..9ceeb6f0ca0 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaManagerImpl.java +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaManagerImpl.java @@ -16,10 +16,11 @@ //under the License. package org.apache.cloudstack.quota; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; -import java.util.Date; +import java.util.HashMap; import java.util.List; -import java.util.TimeZone; import javax.ejb.Local; import javax.inject.Inject; @@ -30,26 +31,45 @@ import org.apache.cloudstack.api.command.QuotaEditMappingCmd; import org.apache.cloudstack.api.command.QuotaEmailTemplateAddCmd; import org.apache.cloudstack.api.command.QuotaRefreshCmd; import org.apache.cloudstack.api.command.QuotaStatementCmd; -import org.apache.cloudstack.api.command.QuotaTypesCmd; -import org.apache.cloudstack.quota.dao.QuotaJobDao; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.quota.dao.QuotaMappingDao; +import org.apache.cloudstack.quota.dao.QuotaUsageDao; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import com.cloud.usage.UsageJobVO; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.usage.UsageVO; +import com.cloud.usage.dao.UsageDao; +import com.cloud.user.AccountVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; @Component @Local(value = QuotaManager.class) -public class QuotaManagerImpl extends ManagerBase implements QuotaManager { +public class QuotaManagerImpl extends ManagerBase implements QuotaManager, Configurable, QuotaConfig, Runnable { private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName()); @Inject - private QuotaJobDao _quotaJobDao; + private AccountDao _accountDao; + @Inject + private UsageDao _usageDao; + @Inject + private QuotaMappingDao _quotaConfigurationDao; + @Inject + private QuotaUsageDao _quotaUsageDao; + @Inject + private ServiceOfferingDao _serviceOfferingDao; - String _hostname = null; - int _pid = 0; - TimeZone _usageTimezone = TimeZone.getTimeZone("GMT"); + static BigDecimal s_hoursInMonth = new BigDecimal(30.00 * 24.00); + static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024); public QuotaManagerImpl() { super(); @@ -58,7 +78,6 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager { @Override public List> getCommands() { final List> cmdList = new ArrayList>(); - cmdList.add(QuotaTypesCmd.class); cmdList.add(QuotaMapping.class); cmdList.add(QuotaCreditsCmd.class); cmdList.add(QuotaEmailTemplateAddCmd.class); @@ -69,54 +88,258 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager { } @Override - public void calculateQuotaUsage(QuotaJobVO job, long startDateMillis, long endDateMillis) { + public String getConfigComponentName() { + return "QUOTA-PLUGIN"; + } - boolean success = false; - long timeStart = System.currentTimeMillis(); + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] { QuotaPluginEnabled, QuotaPeriodType, QuotaPeriod, QuotaGenerateActivity, QuotaEmailRecordOutgoing, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaLimitCritical, + QuotaLimitIncremental, QuotaSmtpHost, QuotaSmtpTimeout, QuotaSmtpUser, QuotaSmtpPassword, QuotaSmtpPort, QuotaSmtpAuthType }; + } + + @Override + public void calculateQuotaUsage() { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); try { - if ((endDateMillis == 0) || (endDateMillis > timeStart)) { - endDateMillis = timeStart; + // get quota mappings + final Pair, Integer> result = _quotaConfigurationDao.listAllMapping(); + HashMap mapping = new HashMap(); + for (final QuotaMappingVO resource : result.first()) { + s_logger.debug("QuotaConf=" + resource.getDescription()); + mapping.put(resource.getUsageType(), resource); } - long lastSuccess = _quotaJobDao.getLastJobSuccessDateMillis(); - if (lastSuccess != 0) { - startDateMillis = lastSuccess + 1; // 1 millisecond after - } - - if (startDateMillis >= endDateMillis) { - if (s_logger.isInfoEnabled()) { - s_logger.info("not parsing usage records since start time mills (" + startDateMillis + ") is on or after end time millis (" + endDateMillis + ")"); - } - - TransactionLegacy jobUpdateTxn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); - try { - jobUpdateTxn.start(); - // everything seemed to work...set endDate as the last - // success date - _quotaJobDao.updateJobSuccess(job.getId(), startDateMillis, endDateMillis, System.currentTimeMillis() - timeStart, success); - - // create a new job if this is a recurring job - if (job.getJobType() == UsageJobVO.JOB_TYPE_RECURRING) { - _quotaJobDao.createNewJob(_hostname, _pid, UsageJobVO.JOB_TYPE_RECURRING); + // get all the active accounts for which there is usage + List accounts = _accountDao.listAll(); + for (AccountVO account : accounts) { + s_logger.info("Account =" + account.getAccountName()); + Pair, Integer> usageRecords = getUsageRecords(account.getAccountId(), account.getDomainId()); + s_logger.debug("Usage records found " + usageRecords.second()); + for (UsageVO usageRecord : usageRecords.first()) { + s_logger.info("Type=" + usageRecord.getUsageType()); + switch (usageRecord.getUsageType()) { + case QuotaTypes.RUNNING_VM: + updateQuotaRunningVMUsage(usageRecord, mapping); + break; + case QuotaTypes.ALLOCATED_VM: + updateQuotaAllocatedVMUsage(usageRecord, mapping); + break; + case QuotaTypes.SNAPSHOT: + updateQuotaDiskUsage(usageRecord, mapping, QuotaTypes.SNAPSHOT); + break; + case QuotaTypes.TEMPLATE: + updateQuotaDiskUsage(usageRecord, mapping, QuotaTypes.TEMPLATE); + break; + case QuotaTypes.ISO: + updateQuotaDiskUsage(usageRecord, mapping, QuotaTypes.ISO); + break; + case QuotaTypes.VOLUME: + updateQuotaDiskUsage(usageRecord, mapping, QuotaTypes.VOLUME); + break; + case QuotaTypes.VM_SNAPSHOT: + updateQuotaDiskUsage(usageRecord, mapping, QuotaTypes.VM_SNAPSHOT); + break; + case QuotaTypes.LOAD_BALANCER_POLICY: + updateQuotaRaw(usageRecord, mapping, QuotaTypes.LOAD_BALANCER_POLICY); + break; + case QuotaTypes.PORT_FORWARDING_RULE: + updateQuotaRaw(usageRecord, mapping, QuotaTypes.PORT_FORWARDING_RULE); + break; + case QuotaTypes.IP_ADDRESS: + updateQuotaRaw(usageRecord, mapping, QuotaTypes.IP_ADDRESS); + break; + case QuotaTypes.NETWORK_OFFERING: + updateQuotaRaw(usageRecord, mapping, QuotaTypes.NETWORK_OFFERING); + break; + case QuotaTypes.SECURITY_GROUP: + updateQuotaRaw(usageRecord, mapping, QuotaTypes.SECURITY_GROUP); + break; + case QuotaTypes.VPN_USERS: + updateQuotaRaw(usageRecord, mapping, QuotaTypes.VPN_USERS); + break; + case QuotaTypes.NETWORK_BYTES_RECEIVED: + updateQuotaRaw(usageRecord, mapping, QuotaTypes.NETWORK_BYTES_RECEIVED); + break; + case QuotaTypes.NETWORK_BYTES_SENT: + updateQuotaRaw(usageRecord, mapping, QuotaTypes.NETWORK_BYTES_SENT); + break; + case QuotaTypes.VM_DISK_IO_READ: + case QuotaTypes.VM_DISK_IO_WRITE: + case QuotaTypes.VM_DISK_BYTES_READ: + case QuotaTypes.VM_DISK_BYTES_WRITE: + default: + break; } - jobUpdateTxn.commit(); - } finally { - jobUpdateTxn.close(); } - - return; } - Date startDate = new Date(startDateMillis); - Date endDate = new Date(endDateMillis); - if (s_logger.isInfoEnabled()) { - s_logger.info("Calculating quota usage for records between " + startDate + " and " + endDate); - } - - // get all the accounts from usage db } catch (Exception e) { s_logger.error("Quota Manager error", e); + e.printStackTrace(); + } finally { + txn.close(); } } + @DB + private void updateQuotaDiskUsage(UsageVO usageRecord, HashMap mapping, int quotaType) { + if (mapping.get(quotaType) != null) { + QuotaUsageVO quota_usage; + BigDecimal quotaUsgage; + BigDecimal onehourcostpergb; + BigDecimal noofgbinuse; + s_logger.info(usageRecord.getDescription() + ", " + usageRecord.getType() + ", " + usageRecord.getOfferingId() + ", " + usageRecord.getTemplateId() + ", " + usageRecord.getUsageDisplay()); + onehourcostpergb = mapping.get(quotaType).getCurrencyValue().divide(s_hoursInMonth, 4, RoundingMode.HALF_EVEN); + noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 4, RoundingMode.HALF_EVEN); + quotaUsgage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostpergb).multiply(noofgbinuse); + s_logger.info(" No of GB In use = " + noofgbinuse + " onehour cost=" + onehourcostpergb); + quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getUsageType(), quotaUsgage, usageRecord.getStartDate(), usageRecord.getEndDate()); + _quotaUsageDao.persist(quota_usage); + } + usageRecord.setQuotaCalculated(1); + _usageDao.persist(usageRecord); + } + + @DB + private void updateQuotaRunningVMUsage(UsageVO usageRecord, HashMap mapping) { + QuotaUsageVO quota_usage; + BigDecimal cpuquotausgage, speedquotausage, memoryquotausage, vmusage; + BigDecimal onehourcostpercpu, onehourcostper100mhz, onehourcostper1mb, onehourcostforvmusage; + BigDecimal rawusage; + s_logger.info(usageRecord.getDescription() + ", " + usageRecord.getType() + ", " + usageRecord.getOfferingId() + ", " + usageRecord.getVmInstanceId() + ", " + usageRecord.getUsageDisplay()); + // get service offering details + ServiceOfferingVO serviceoffering = findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId()); + rawusage = new BigDecimal(usageRecord.getRawUsage()); + + if (mapping.get(QuotaTypes.CPU_NUMBER) == null) { + BigDecimal cpu = new BigDecimal(serviceoffering.getCpu()); + onehourcostpercpu = mapping.get(QuotaTypes.CPU_NUMBER).getCurrencyValue().divide(s_hoursInMonth, 4, RoundingMode.HALF_EVEN); + cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu); + quota_usage = new QuotaUsageVO(usageRecord.getId(), QuotaTypes.CPU_NUMBER, cpuquotausgage, usageRecord.getStartDate(), usageRecord.getEndDate()); + _quotaUsageDao.persist(quota_usage); + } + if (mapping.get(QuotaTypes.CPU_CLOCK_RATE) == null) { + BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00); + onehourcostper100mhz = mapping.get(QuotaTypes.CPU_CLOCK_RATE).getCurrencyValue().divide(s_hoursInMonth, 4, RoundingMode.HALF_EVEN); + speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed); + quota_usage = new QuotaUsageVO(usageRecord.getId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage, usageRecord.getStartDate(), usageRecord.getEndDate()); + _quotaUsageDao.persist(quota_usage); + } + if (mapping.get(QuotaTypes.MEMORY) == null) { + BigDecimal memory = new BigDecimal(serviceoffering.getRamSize()); + onehourcostper1mb = mapping.get(QuotaTypes.MEMORY).getCurrencyValue().divide(s_hoursInMonth, 4, RoundingMode.HALF_EVEN); + memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory); + quota_usage = new QuotaUsageVO(usageRecord.getId(), QuotaTypes.MEMORY, memoryquotausage, usageRecord.getStartDate(), usageRecord.getEndDate()); + _quotaUsageDao.persist(quota_usage); + } + if (mapping.get(QuotaTypes.RUNNING_VM) == null) { + onehourcostforvmusage = mapping.get(QuotaTypes.RUNNING_VM).getCurrencyValue().divide(s_hoursInMonth, 4, RoundingMode.HALF_EVEN); + vmusage = rawusage.multiply(onehourcostforvmusage); + quota_usage = new QuotaUsageVO(usageRecord.getId(), QuotaTypes.RUNNING_VM, vmusage, usageRecord.getStartDate(), usageRecord.getEndDate()); + _quotaUsageDao.persist(quota_usage); + } + + usageRecord.setQuotaCalculated(1); + _usageDao.persist(usageRecord); + } + + @DB + private void updateQuotaAllocatedVMUsage(UsageVO usageRecord, HashMap mapping) { + if (mapping.get(QuotaTypes.ALLOCATED_VM) != null) { + QuotaUsageVO quota_usage; + BigDecimal vmusage; + BigDecimal onehourcostforvmusage; + s_logger.info(usageRecord.getDescription() + ", " + usageRecord.getType() + ", " + usageRecord.getOfferingId() + ", " + usageRecord.getVmInstanceId() + ", " + + usageRecord.getUsageDisplay()); + + onehourcostforvmusage = mapping.get(QuotaTypes.ALLOCATED_VM).getCurrencyValue().divide(s_hoursInMonth, 4, RoundingMode.HALF_EVEN); + vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage); + quota_usage = new QuotaUsageVO(usageRecord.getId(), QuotaTypes.ALLOCATED_VM, vmusage, usageRecord.getStartDate(), usageRecord.getEndDate()); + _quotaUsageDao.persist(quota_usage); + } + + usageRecord.setQuotaCalculated(1); + _usageDao.persist(usageRecord); + } + + @DB + private void updateQuotaRaw(UsageVO usageRecord, HashMap mapping, int ruleType) { + if (mapping.get(ruleType) != null) { + QuotaUsageVO quota_usage; + BigDecimal ruleusage; + BigDecimal onehourcost; + s_logger.info(usageRecord.getDescription() + ", " + usageRecord.getType() + ", " + usageRecord.getOfferingId() + ", " + usageRecord.getVmInstanceId() + ", " + + usageRecord.getUsageDisplay()); + + onehourcost = mapping.get(ruleType).getCurrencyValue().divide(s_hoursInMonth, 4, RoundingMode.HALF_EVEN); + ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost); + quota_usage = new QuotaUsageVO(usageRecord.getId(), ruleType, ruleusage, usageRecord.getStartDate(), usageRecord.getEndDate()); + _quotaUsageDao.persist(quota_usage); + } + + usageRecord.setQuotaCalculated(1); + _usageDao.persist(usageRecord); + } + + @DB + private void updateQuotaNetwork(UsageVO usageRecord, HashMap mapping, int transferType) { + if (mapping.get(transferType) != null) { + QuotaUsageVO quota_usage; + BigDecimal onehourcost; + BigDecimal rawusageingb; + BigDecimal networkusage; + s_logger.info(usageRecord.getDescription() + ", " + usageRecord.getType() + ", " + usageRecord.getOfferingId() + ", " + usageRecord.getVmInstanceId() + ", " + + usageRecord.getUsageDisplay()); + + onehourcost = mapping.get(transferType).getCurrencyValue().divide(s_hoursInMonth, 4, RoundingMode.HALF_EVEN); + rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 4, RoundingMode.HALF_EVEN); + networkusage = rawusageingb.multiply(onehourcost); + quota_usage = new QuotaUsageVO(usageRecord.getId(), transferType, networkusage, usageRecord.getStartDate(), usageRecord.getEndDate()); + _quotaUsageDao.persist(quota_usage); + } + + usageRecord.setQuotaCalculated(1); + _usageDao.persist(usageRecord); + } + + @SuppressWarnings("deprecation") + public Pair, Integer> getUsageRecords(long accountId, long domainId) { + s_logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId); + Filter usageFilter = new Filter(UsageVO.class, "id", true, 10000L, null); + SearchCriteria sc = _usageDao.createSearchCriteria(); + if (accountId != -1) { + sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + } + if (domainId != -1) { + sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + } + sc.addAnd("quotaCalculated", SearchCriteria.Op.EQ, 0); + s_logger.debug("Getting usage records" + usageFilter.getOrderBy()); + Pair, Integer> usageRecords = _usageDao.searchAndCountAllRecords(sc, usageFilter); + return new Pair, Integer>(usageRecords.first(), usageRecords.second()); + } + + public ServiceOfferingVO findServiceOffering(Long vmId, long serviceOfferingId) { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); + ServiceOfferingVO result; + try { + result = _serviceOfferingDao.findById(vmId, serviceOfferingId); + } finally { + txn.close(); + } + TransactionLegacy.open(TransactionLegacy.USAGE_DB); + return result; + } + + @Override + public void run() { + (new ManagedContextRunnable() { + @Override + protected void runInContext() { + calculateQuotaUsage(); + } + }).run(); + } + } diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaConfigurationVO.java b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaMappingVO.java similarity index 62% rename from plugins/database/quota/src/org/apache/cloudstack/quota/QuotaConfigurationVO.java rename to plugins/database/quota/src/org/apache/cloudstack/quota/QuotaMappingVO.java index eba9e40f228..f9b2c7ae494 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaConfigurationVO.java +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaMappingVO.java @@ -16,6 +16,8 @@ //under the License. package org.apache.cloudstack.quota; +import java.math.BigDecimal; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; @@ -24,25 +26,8 @@ import javax.persistence.Table; import org.apache.cloudstack.api.InternalIdentity; @Entity -@Table(name = "quota_configuration") -public class QuotaConfigurationVO implements InternalIdentity { - /** - * enable.quota.service: Enable quota service by default all of this - * functionality is disabled. quota.period.type : Quota period type: 1 for - * every x days, 2 for certain day of the month, 3 for yearly on activation - * day - default usage reporting cycle quota.period.config : The value for - * the above quota period type quota.activity.generate : Set “Y” to enable a - * detailed log of the quota usage, rating and billing activity, on daily - * basis. Valid values (“Y”, “N”); record.outgoingEmail: Boolean, yes means - * all the emails sent out will be stored in local DB, by default it is no. - * quota.enable : enable the usage quota enforcement quota.currencySymbol : - * The symbol for the currency in use to measure usage. quota.criticalLimit: - * A limit when it is reached user is sent and alert. - * quota.incrementalLimit: A incremental limit that is added to - * criticalLimit in this increments, when breached a email is send to the - * user with details. - * */ - +@Table(name = "quota_mapping") +public class QuotaMappingVO implements InternalIdentity { private static final long serialVersionUID = -7117933766387653203L; @Id @@ -50,7 +35,10 @@ public class QuotaConfigurationVO implements InternalIdentity { private Long id; @Column(name = "usage_type") - private String usageType; + private int usageType; + + @Column(name = "usage_name") + private String usageName; @Column(name = "usage_unit") private String usageUnit; @@ -59,7 +47,7 @@ public class QuotaConfigurationVO implements InternalIdentity { private String usageDiscriminator; @Column(name = "currency_value") - private int currencyValue; + private BigDecimal currencyValue; @Column(name = "include") private int include; @@ -67,11 +55,12 @@ public class QuotaConfigurationVO implements InternalIdentity { @Column(name = "description") private String description; - public QuotaConfigurationVO() { + public QuotaMappingVO() { } - public QuotaConfigurationVO(final String usagetype, final String usageunit, final String usagediscriminator, final int currencyvalue, final int include, final String description) { + public QuotaMappingVO(final int usagetype, final String usagename, final String usageunit, final String usagediscriminator, final BigDecimal currencyvalue, final int include, final String description) { this.usageType = usagetype; + this.usageName = usagename; this.usageUnit = usageunit; this.usageDiscriminator = usagediscriminator; this.currencyValue = currencyvalue; @@ -79,14 +68,22 @@ public class QuotaConfigurationVO implements InternalIdentity { this.description = description; } - public String getUsageType() { + public int getUsageType() { return usageType; } - public void setUsageType(String usageType) { + public void setUsageType(int usageType) { this.usageType = usageType; } + public String getUsageName() { + return usageName; + } + + public void setUsageName(String usageName) { + this.usageName = usageName; + } + public String getUsageUnit() { return usageUnit; } @@ -103,11 +100,11 @@ public class QuotaConfigurationVO implements InternalIdentity { this.usageDiscriminator = usageDiscriminator; } - public int getCurrencyValue() { + public BigDecimal getCurrencyValue() { return currencyValue; } - public void setCurrencyValue(int currencyValue) { + public void setCurrencyValue(BigDecimal currencyValue) { this.currencyValue = currencyValue; } diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaUsageTypes.java b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaTypes.java similarity index 94% rename from plugins/database/quota/src/org/apache/cloudstack/quota/QuotaUsageTypes.java rename to plugins/database/quota/src/org/apache/cloudstack/quota/QuotaTypes.java index c788a40ca66..e9572a836d7 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaUsageTypes.java +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaTypes.java @@ -22,10 +22,10 @@ import java.util.List; import org.apache.cloudstack.api.response.QuotaTypeResponse; import org.apache.cloudstack.usage.UsageTypes; -public class QuotaUsageTypes extends UsageTypes { - public static final int CPU_CLOCK_RATE = 24; - public static final int CPU_NUMBER = 25; - public static final int MEMORY = 26; +public class QuotaTypes extends UsageTypes { + public static final int CPU_CLOCK_RATE = 15; + public static final int CPU_NUMBER = 16; + public static final int MEMORY = 17; public static List responseList = new ArrayList(); diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaUsageVO.java b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaUsageVO.java index 0aa2b8bc705..3f7a2eb41fd 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaUsageVO.java +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaUsageVO.java @@ -42,7 +42,7 @@ public class QuotaUsageVO implements InternalIdentity { private Long usageItemId; @Column(name = "usage_type") - private String usageType; + private int usageType; @Column(name = "quota_used") private BigDecimal quotaUsed; @@ -58,7 +58,7 @@ public class QuotaUsageVO implements InternalIdentity { public QuotaUsageVO() { } - public QuotaUsageVO(Long usageItemId, String usageType, BigDecimal quotaUsed, Date startDate, Date endDate) { + public QuotaUsageVO(Long usageItemId, int usageType, BigDecimal quotaUsed, Date startDate, Date endDate) { super(); this.usageItemId = usageItemId; this.usageType = usageType; @@ -81,11 +81,11 @@ public class QuotaUsageVO implements InternalIdentity { this.usageItemId = usageItemId; } - public String getUsageType() { + public int getUsageType() { return usageType; } - public void setUsageType(String usageType) { + public void setUsageType(int usageType) { this.usageType = usageType; } diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaJobDao.java b/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaJobDao.java deleted file mode 100644 index 20b328cff9b..00000000000 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaJobDao.java +++ /dev/null @@ -1,41 +0,0 @@ -//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.quota.dao; - -import java.util.Date; - -import org.apache.cloudstack.quota.QuotaJobVO; - -import com.cloud.utils.db.GenericDao; - -public interface QuotaJobDao extends GenericDao { - Long checkHeartbeat(String hostname, int pid, int aggregationDuration); - - void createNewJob(String hostname, int pid, int jobType); - - QuotaJobVO getLastJob(); - - QuotaJobVO getNextImmediateJob(); - - long getLastJobSuccessDateMillis(); - - Date getLastHeartbeat(); - - QuotaJobVO isOwner(String hostname, int pid); - - void updateJobSuccess(Long jobId, long startMillis, long endMillis, long execTime, boolean success); -} diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaJobDaoImpl.java b/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaJobDaoImpl.java deleted file mode 100644 index f0c7d09419c..00000000000 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaJobDaoImpl.java +++ /dev/null @@ -1,201 +0,0 @@ -//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.quota.dao; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.util.Date; -import java.util.List; - -import javax.ejb.Local; - -import org.apache.cloudstack.quota.QuotaJobVO; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - -import com.cloud.utils.db.Filter; -import com.cloud.utils.db.GenericDaoBase; -import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.TransactionLegacy; -import com.cloud.utils.exception.CloudRuntimeException; - -@Component -@Local(value = { QuotaJobDao.class }) -public class QuotaJobDaoImpl extends GenericDaoBase implements QuotaJobDao { - private static final Logger s_logger = Logger.getLogger(QuotaJobDaoImpl.class.getName()); - - private static final String GET_LAST_JOB_SUCCESS_DATE_MILLIS = "SELECT end_millis FROM cloud_usage.usage_job WHERE end_millis > 0 and success = 1 ORDER BY end_millis DESC LIMIT 1"; - - @Override - public long getLastJobSuccessDateMillis() { - TransactionLegacy txn = TransactionLegacy.currentTxn(); - PreparedStatement pstmt = null; - String sql = GET_LAST_JOB_SUCCESS_DATE_MILLIS; - try { - pstmt = txn.prepareAutoCloseStatement(sql); - ResultSet rs = pstmt.executeQuery(); - if (rs.next()) { - return rs.getLong(1); - } - } catch (Exception ex) { - s_logger.error("error getting last usage job success date", ex); - } finally { - txn.close(); - } - return 0L; - } - - @Override - public void updateJobSuccess(Long jobId, long startMillis, long endMillis, long execTime, boolean success) { - TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); - try { - txn.start(); - - QuotaJobVO job = lockRow(jobId, Boolean.TRUE); - QuotaJobVO jobForUpdate = createForUpdate(); - jobForUpdate.setStartMillis(startMillis); - jobForUpdate.setEndMillis(endMillis); - jobForUpdate.setExecTime(execTime); - jobForUpdate.setStartDate(new Date(startMillis)); - jobForUpdate.setEndDate(new Date(endMillis)); - jobForUpdate.setSuccess(success); - update(job.getId(), jobForUpdate); - - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - s_logger.error("error updating job success date", ex); - throw new CloudRuntimeException(ex.getMessage()); - } finally { - txn.close(); - } - } - - @Override - public Long checkHeartbeat(String hostname, int pid, int aggregationDuration) { - QuotaJobVO job = getNextRecurringJob(); - if (job == null) { - return null; - } - - if (job.getHost().equals(hostname) && (job.getPid() != null) && (job.getPid().intValue() == pid)) { - return job.getId(); - } - - Date lastHeartbeat = job.getHeartbeat(); - if (lastHeartbeat == null) { - return null; - } - - long sinceLastHeartbeat = System.currentTimeMillis() - lastHeartbeat.getTime(); - - // TODO: Make this check a little smarter..but in the mean time we want - // the mgmt - // server to monitor the usage server, we need to make sure other usage - // servers take over as the usage job owner more aggressively. For now - // this is hardcoded to 5 minutes. - if (sinceLastHeartbeat > (5 * 60 * 1000)) { - return job.getId(); - } - return null; - } - - @Override - public QuotaJobVO isOwner(String hostname, int pid) { - TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); - try { - if ((hostname == null) || (pid <= 0)) { - return null; - } - - QuotaJobVO job = getLastJob(); - if (job == null) { - return null; - } - - if (hostname.equals(job.getHost()) && (job.getPid() != null) && (pid == job.getPid().intValue())) { - return job; - } - } finally { - txn.close(); - } - return null; - } - - @Override - public void createNewJob(String hostname, int pid, int jobType) { - QuotaJobVO newJob = new QuotaJobVO(); - newJob.setHost(hostname); - newJob.setPid(pid); - newJob.setHeartbeat(new Date()); - newJob.setJobType(jobType); - persist(newJob); - } - - @Override - public QuotaJobVO getLastJob() { - Filter filter = new Filter(QuotaJobVO.class, "id", false, Long.valueOf(0), Long.valueOf(1)); - SearchCriteria sc = createSearchCriteria(); - sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); - List jobs = search(sc, filter); - - if ((jobs == null) || jobs.isEmpty()) { - return null; - } - return jobs.get(0); - } - - private QuotaJobVO getNextRecurringJob() { - Filter filter = new Filter(QuotaJobVO.class, "id", false, Long.valueOf(0), Long.valueOf(1)); - SearchCriteria sc = createSearchCriteria(); - sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); - sc.addAnd("jobType", SearchCriteria.Op.EQ, Integer.valueOf(QuotaJobVO.JOB_TYPE_RECURRING)); - List jobs = search(sc, filter); - - if ((jobs == null) || jobs.isEmpty()) { - return null; - } - return jobs.get(0); - } - - @Override - public QuotaJobVO getNextImmediateJob() { - Filter filter = new Filter(QuotaJobVO.class, "id", false, Long.valueOf(0), Long.valueOf(1)); - SearchCriteria sc = createSearchCriteria(); - sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); - sc.addAnd("jobType", SearchCriteria.Op.EQ, Integer.valueOf(QuotaJobVO.JOB_TYPE_SINGLE)); - sc.addAnd("scheduled", SearchCriteria.Op.EQ, Integer.valueOf(0)); - List jobs = search(sc, filter); - - if ((jobs == null) || jobs.isEmpty()) { - return null; - } - return jobs.get(0); - } - - @Override - public Date getLastHeartbeat() { - Filter filter = new Filter(QuotaJobVO.class, "heartbeat", false, Long.valueOf(0), Long.valueOf(1)); - SearchCriteria sc = createSearchCriteria(); - List jobs = search(sc, filter); - - if ((jobs == null) || jobs.isEmpty()) { - return null; - } - return jobs.get(0).getHeartbeat(); - } -} diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaConfigurationDao.java b/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaMappingDao.java similarity index 77% rename from plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaConfigurationDao.java rename to plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaMappingDao.java index b8fdd1a8a52..7bd9105d5d0 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaConfigurationDao.java +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaMappingDao.java @@ -18,15 +18,15 @@ package org.apache.cloudstack.quota.dao; import java.util.List; -import org.apache.cloudstack.quota.QuotaConfigurationVO; +import org.apache.cloudstack.quota.QuotaMappingVO; import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; -public interface QuotaConfigurationDao extends GenericDao { +public interface QuotaMappingDao extends GenericDao { - QuotaConfigurationVO findByUsageType(String usageType); + QuotaMappingVO findByUsageType(int usageType); - Pair, Integer> searchConfigurations(); + Pair, Integer> listAllMapping(); } diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaConfigurationDaoImpl.java b/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaMappingDaoImpl.java similarity index 66% rename from plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaConfigurationDaoImpl.java rename to plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaMappingDaoImpl.java index d01d7dfd7b6..08b89eba212 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaConfigurationDaoImpl.java +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/dao/QuotaMappingDaoImpl.java @@ -21,7 +21,7 @@ import java.util.List; import javax.ejb.Local; import org.springframework.stereotype.Component; -import org.apache.cloudstack.quota.QuotaConfigurationVO; +import org.apache.cloudstack.quota.QuotaMappingVO; import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDaoBase; @@ -30,12 +30,12 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; @Component -@Local(value = { QuotaConfigurationDao.class }) -public class QuotaConfigurationDaoImpl extends GenericDaoBase implements QuotaConfigurationDao { - private final SearchBuilder searchUsageType; - private final SearchBuilder listAllIncludedUsageType; +@Local(value = { QuotaMappingDao.class }) +public class QuotaMappingDaoImpl extends GenericDaoBase implements QuotaMappingDao { + private final SearchBuilder searchUsageType; + private final SearchBuilder listAllIncludedUsageType; - public QuotaConfigurationDaoImpl() { + public QuotaMappingDaoImpl() { super(); searchUsageType = createSearchBuilder(); searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ); @@ -47,17 +47,22 @@ public class QuotaConfigurationDaoImpl extends GenericDaoBase sc = searchUsageType.create(); - sc.setParameters("usage_type", usageType); - return findOneBy(sc); + public QuotaMappingVO findByUsageType(final int usageType) { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + try { + final SearchCriteria sc = searchUsageType.create(); + sc.setParameters("usage_type", usageType); + return findOneBy(sc); + } finally { + txn.close(); + } } @Override - public Pair, Integer> searchConfigurations() { + public Pair, Integer> listAllMapping() { TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); try { - final SearchCriteria sc = listAllIncludedUsageType.create(); + final SearchCriteria sc = listAllIncludedUsageType.create(); sc.setParameters("include", 1); return searchAndCount(sc, null); } finally { diff --git a/server/src/com/cloud/api/dispatch/ParamProcessWorker.java b/server/src/com/cloud/api/dispatch/ParamProcessWorker.java index b990d4aedf4..f9c9e0f079b 100644 --- a/server/src/com/cloud/api/dispatch/ParamProcessWorker.java +++ b/server/src/com/cloud/api/dispatch/ParamProcessWorker.java @@ -291,6 +291,14 @@ public class ParamProcessWorker implements DispatchWorker { field.set(cmdObj, Float.valueOf(paramObj.toString())); } break; + case DOUBLE: + // Assuming that the parameters have been checked for required before now, + // we ignore blank or null values and defer to the command to set a default + // value for optional parameters ... + if (paramObj != null && isNotBlank(paramObj.toString())) { + field.set(cmdObj, Double.valueOf(paramObj.toString())); + } + break; case INTEGER: // Assuming that the parameters have been checked for required before now, // we ignore blank or null values and defer to the command to set a default