CLOUDSTACK-8592: calculation of quota

This commit is contained in:
Abhinandan Prateek 2015-07-13 09:09:03 +05:30 committed by Rohit Yadav
parent 1b579afaeb
commit 135af2ceed
29 changed files with 523 additions and 638 deletions

View File

@ -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;

View File

@ -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///////////////////

View File

@ -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;

View File

@ -783,7 +783,6 @@ listOpenDaylightControllers=1
addGloboDnsHost=1
### Quota Service
quotaTypes=15
quotaMapping=15
quotaEditMapping=15
quotaRefresh=15

View File

@ -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() {
}

View File

@ -392,6 +392,8 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
final String sql = str.toString();
s_logger.debug("Sql = " + sql);
PreparedStatement pstmt = null;
final List<T> result = new ArrayList<T>();
try {

View File

@ -27,12 +27,11 @@
<bean id="QuotaManager" class="org.apache.cloudstack.quota.QuotaManagerImpl" />
<bean id="QuotaDBUtils" class="org.apache.cloudstack.quota.QuotaDBUtilsImpl" />
<bean id="QuotaConfigurationDao" class="org.apache.cloudstack.quota.dao.QuotaConfigurationDaoImpl" />
<bean id="QuotaConfigurationDao" class="org.apache.cloudstack.quota.dao.QuotaMappingDaoImpl" />
<bean id="QuotaBalanceDao" class="org.apache.cloudstack.quota.dao.QuotaBalanceDaoImpl"/>
<bean id="QuotaCreditsDao" class="org.apache.cloudstack.quota.dao.QuotaCreditsDaoImpl"/>
<bean id="QuotaEmailTemplatesDao" class="org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDaoImpl"/>
<bean id="QuotaSentEmailDao" class="org.apache.cloudstack.quota.dao.QuotaSentEmailsDaoImpl"/>
<bean id="QuotaUsageDao" class="org.apache.cloudstack.quota.dao.QuotaUsageDaoImpl"/>
<bean id="QuotaJobDao" class="org.apache.cloudstack.quota.dao.QuotaJobDaoImpl"/>
</beans>

View File

@ -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() {

View File

@ -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<List<QuotaConfigurationVO>, Integer> result = _quotaDBUtils.editQuotaMapping(this);
final Pair<List<QuotaMappingVO>, Integer> result = _quotaDBUtils.editQuotaMapping(this);
final List<QuotaConfigurationResponse> responses = new ArrayList<QuotaConfigurationResponse>();
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);
}

View File

@ -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")

View File

@ -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<List<QuotaConfigurationVO>, Integer> result = _quotaDBUtils.listConfigurations(this);
final Pair<List<QuotaMappingVO>, Integer> result = _quotaDBUtils.listConfigurations(this);
final List<QuotaConfigurationResponse> responses = new ArrayList<QuotaConfigurationResponse>();
for (final QuotaConfigurationVO resource : result.first()) {
for (final QuotaMappingVO resource : result.first()) {
final QuotaConfigurationResponse configurationResponse = _quotaDBUtils.createQuotaConfigurationResponse(resource);
configurationResponse.setObjectName("QuotaConfiguration");
responses.add(configurationResponse);

View File

@ -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);
}

View File

@ -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() {

View File

@ -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<QuotaTypeResponse> responses = QuotaUsageTypes.listQuotaUsageTypes();
final ListResponse<QuotaTypeResponse> response = new ListResponse<QuotaTypeResponse>();
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;
}
}

View File

@ -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;
}

View File

@ -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<Boolean> QuotaPluginEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class, "quota.enable.service", "false",
"Indicates whether Quota plugin is enabled or not", true);
public static final ConfigKey<String> QuotaPeriodType = new ConfigKey<String>("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<String> QuotaPeriod = new ConfigKey<String>("Advanced", String.class, "quota.period.config", "15",
"The period config in number of days for the quota period type", true);
public static final ConfigKey<String> QuotaGenerateActivity = new ConfigKey<String>("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<String> QuotaEmailRecordOutgoing = new ConfigKey<String>("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<String> QuotaEnableEnforcement = new ConfigKey<String>("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<String> QuotaCurrencySymbol = new ConfigKey<String>("Advanced", String.class, "quota.currency.symbol", "R",
"The symbol for the currency in use to measure usage.", true);
public static final ConfigKey<String> QuotaLimitCritical = new ConfigKey<String>("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<String> QuotaLimitIncremental = new ConfigKey<String>("Advanced", String.class, "quota.limit.increment", "5",
"Quota limit incremental", true);
public static final ConfigKey<String> QuotaSmtpHost = new ConfigKey<String>("Advanced", String.class, "quota.usage.smtp.host", "",
"Quota SMTP host for quota related emails", true);
public static final ConfigKey<String> QuotaSmtpTimeout = new ConfigKey<String>("Advanced", String.class, "quota.usage.smtp.connection.timeout", "60",
"Quota SMTP server connection timeout duration", true);
public static final ConfigKey<String> QuotaSmtpUser = new ConfigKey<String>("Advanced", String.class, "quota.usage.smtp.user", "",
"Quota SMTP server username", true);
public static final ConfigKey<String> QuotaSmtpPassword = new ConfigKey<String>("Advanced", String.class, "quota.usage.smtp.password", "",
"Quota SMTP server password", true);
public static final ConfigKey<String> QuotaSmtpPort = new ConfigKey<String>("Advanced", String.class, "quota.usage.smtp.port", "",
"Quota SMTP port", true);
public static final ConfigKey<String> QuotaSmtpAuthType = new ConfigKey<String>("Advanced", String.class, "quota.usage.smtp.useAuth", "",
"Quota SMTP authorization type", true);
}

View File

@ -28,12 +28,13 @@ import com.cloud.utils.component.PluggableService;
public interface QuotaDBUtils extends PluggableService {
Pair<List<QuotaConfigurationVO>, Integer> editQuotaMapping(QuotaEditMappingCmd cmd);
Pair<List<QuotaMappingVO>, Integer> editQuotaMapping(QuotaEditMappingCmd cmd);
Pair<List<QuotaConfigurationVO>, Integer> listConfigurations(QuotaMapping cmd);
Pair<List<QuotaMappingVO>, Integer> listConfigurations(QuotaMapping cmd);
QuotaConfigurationResponse createQuotaConfigurationResponse(QuotaConfigurationVO configuration);
QuotaConfigurationResponse createQuotaConfigurationResponse(QuotaMappingVO configuration);
QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Integer amount, Long updatedBy);
}

View File

@ -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<List<QuotaConfigurationVO>, Integer> listConfigurations(final QuotaMapping cmd) {
final Pair<List<QuotaConfigurationVO>, Integer> result = _quotaConfigurationDao.searchConfigurations();
public Pair<List<QuotaMappingVO>, Integer> listConfigurations(final QuotaMapping cmd) {
final Pair<List<QuotaMappingVO>, Integer> result = _quotaMappingDao.listAllMapping();
TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
return result;
}
public Pair<List<QuotaConfigurationVO>, Integer> editQuotaMapping(QuotaEditMappingCmd cmd) {
String resourceName = cmd.getUsageType();
Integer quotaMapping = cmd.getValue();
public Pair<List<QuotaMappingVO>, 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<List<QuotaConfigurationVO>, Integer> result = _quotaConfigurationDao.searchConfigurations();
final Pair<List<QuotaMappingVO>, Integer> result = _quotaMappingDao.listAllMapping();
TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
return result;
}
@ -97,4 +100,5 @@ public class QuotaDBUtilsImpl {
return new QuotaCreditsResponse(result);
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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<Class<?>> getCommands() {
final List<Class<?>> cmdList = new ArrayList<Class<?>>();
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<List<QuotaMappingVO>, Integer> result = _quotaConfigurationDao.listAllMapping();
HashMap<Integer, QuotaMappingVO> mapping = new HashMap<Integer, QuotaMappingVO>();
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<AccountVO> accounts = _accountDao.listAll();
for (AccountVO account : accounts) {
s_logger.info("Account =" + account.getAccountName());
Pair<List<? extends UsageVO>, 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<Integer, QuotaMappingVO> 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<Integer, QuotaMappingVO> 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<Integer, QuotaMappingVO> 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<Integer, QuotaMappingVO> 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<Integer, QuotaMappingVO> 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<List<? extends UsageVO>, 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<UsageVO> 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<List<UsageVO>, Integer> usageRecords = _usageDao.searchAndCountAllRecords(sc, usageFilter);
return new Pair<List<? extends UsageVO>, 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();
}
}

View File

@ -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;
}

View File

@ -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<QuotaTypeResponse> responseList = new ArrayList<QuotaTypeResponse>();

View File

@ -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;
}

View File

@ -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<QuotaJobVO, Long> {
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);
}

View File

@ -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<QuotaJobVO, Long> 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<QuotaJobVO> sc = createSearchCriteria();
sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0));
List<QuotaJobVO> 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<QuotaJobVO> sc = createSearchCriteria();
sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0));
sc.addAnd("jobType", SearchCriteria.Op.EQ, Integer.valueOf(QuotaJobVO.JOB_TYPE_RECURRING));
List<QuotaJobVO> 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<QuotaJobVO> 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<QuotaJobVO> 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<QuotaJobVO> sc = createSearchCriteria();
List<QuotaJobVO> jobs = search(sc, filter);
if ((jobs == null) || jobs.isEmpty()) {
return null;
}
return jobs.get(0).getHeartbeat();
}
}

View File

@ -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<QuotaConfigurationVO, Long> {
public interface QuotaMappingDao extends GenericDao<QuotaMappingVO, Long> {
QuotaConfigurationVO findByUsageType(String usageType);
QuotaMappingVO findByUsageType(int usageType);
Pair<List<QuotaConfigurationVO>, Integer> searchConfigurations();
Pair<List<QuotaMappingVO>, Integer> listAllMapping();
}

View File

@ -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<QuotaConfigurationVO, Long> implements QuotaConfigurationDao {
private final SearchBuilder<QuotaConfigurationVO> searchUsageType;
private final SearchBuilder<QuotaConfigurationVO> listAllIncludedUsageType;
@Local(value = { QuotaMappingDao.class })
public class QuotaMappingDaoImpl extends GenericDaoBase<QuotaMappingVO, Long> implements QuotaMappingDao {
private final SearchBuilder<QuotaMappingVO> searchUsageType;
private final SearchBuilder<QuotaMappingVO> 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<QuotaConfiguration
}
@Override
public QuotaConfigurationVO findByUsageType(final String usageType) {
final SearchCriteria<QuotaConfigurationVO> 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<QuotaMappingVO> sc = searchUsageType.create();
sc.setParameters("usage_type", usageType);
return findOneBy(sc);
} finally {
txn.close();
}
}
@Override
public Pair<List<QuotaConfigurationVO>, Integer> searchConfigurations() {
public Pair<List<QuotaMappingVO>, Integer> listAllMapping() {
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
try {
final SearchCriteria<QuotaConfigurationVO> sc = listAllIncludedUsageType.create();
final SearchCriteria<QuotaMappingVO> sc = listAllIncludedUsageType.create();
sc.setParameters("include", 1);
return searchAndCount(sc, null);
} finally {

View File

@ -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