You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cloudstack.apache.org by abhinandanprateek <gi...@git.apache.org> on 2015/08/13 09:41:49 UTC

[GitHub] cloudstack pull request: Quota master

GitHub user abhinandanprateek opened a pull request:

    https://github.com/apache/cloudstack/pull/689

    Quota master

    This pull request is for quota service implementation, divided into following two commits:
    1. Quota server side implementation
       a. Quota Framework plugin containing Daos
       b. Quota database plugin for quota APIs
       3. Quota Usage server enhancements 
    2. Quota UI plugin

You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/shapeblue/cloudstack quota-master

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/cloudstack/pull/689.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #689
    
----
commit 230b6f4bb81f4eeb314ac02b3fd338b24b7fb359
Author: Abhinandan Prateek <ab...@shapeblue.com>
Date:   2015-08-13T06:12:30Z

    CLOUDSTACK-8592: Implement Quota service
    
    Quota service while allowing for scalability will make sure that the cloud is
    not exploited by attacks, careless use and program errors. To address this
    problem, we propose to employ a quota-enforcement service that allows resource
    usage within certain bounds as defined by policies and available quotas for
    various entities.  Quota service extends the functionality of usage server to
    provide a measurement for the resources used by the accounts and domains using a
    common unit referred to as cloud currency in this document. It can be configured
    to ensure that your usage won’t exceed the budget allocated to accounts/domain
    in cloud currency.  It will let user know how much of the cloud resources he is
    using. It will help the cloud admins, if they want, to ensure that a user does
    not go beyond his allocated quota. Per usage cycle if a account is found to be
    exceeding its quota then it is locked. Locking an account means that it will not
    be able to initiat e a new resource allocation request, whether it is more
    storage or an additional ip. Needless to say quota service as well as any action
    on the account is configurable.
    
    FS: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Quota+Service+-+FS
    
    Signed-off-by: Rohit Yadav <ro...@shapeblue.com>

commit c78fb351bdd199a77aba8f4f0b1578711c696bfb
Author: Rohit Yadav <ro...@shapeblue.com>
Date:   2015-08-13T06:15:21Z

    CLOUDSTACK-8592: Implement UI Plugin for Quota Service
    
    Signed-off-by: Rohit Yadav <ro...@shapeblue.com>

----


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37581010
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java ---
    @@ -0,0 +1,73 @@
    +//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.List;
    +
    +import javax.ejb.Local;
    +
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.springframework.stereotype.Component;
    +
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaAccountDao.class })
    +public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> implements QuotaAccountDao {
    +
    +    @Override
    +    public List<QuotaAccountVO> listAll() {
    +        List<QuotaAccountVO> result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    As mentioned in a previous comment, why is a transaction created and immediately closed?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582467
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java ---
    @@ -0,0 +1,73 @@
    +//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.List;
    +
    +import javax.ejb.Local;
    +
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.springframework.stereotype.Component;
    +
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaAccountDao.class })
    +public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> implements QuotaAccountDao {
    +
    +    @Override
    +    public List<QuotaAccountVO> listAll() {
    +        List<QuotaAccountVO> result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.listAll();
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaAccountVO findById(Long id) {
    +        QuotaAccountVO result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.findById(id);
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaAccountVO persist(QuotaAccountVO entity) {
    +        QuotaAccountVO result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.persist(entity);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947791
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    +                    s_logger.error(String.format("Unable to unlock account %s after getting enough quota credits", account.getAccountName()));
    --- End diff --
    
    Please include the exception instance in the logging call to capture the stack trace and assist debugging.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37583449
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java ---
    @@ -0,0 +1,138 @@
    +//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 com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaTariffDao.class })
    +public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> implements QuotaTariffDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaTariffDaoImpl.class.getName());
    +
    +    private final SearchBuilder<QuotaTariffVO> searchUsageType;
    +    private final SearchBuilder<QuotaTariffVO> listAllIncludedUsageType;
    +
    +    public QuotaTariffDaoImpl() {
    +        super();
    +        searchUsageType = createSearchBuilder();
    +        searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        searchUsageType.done();
    +
    +        listAllIncludedUsageType = createSearchBuilder();
    +        listAllIncludedUsageType.and("onorbefore", listAllIncludedUsageType.entity().getEffectiveOn(), SearchCriteria.Op.LTEQ);
    +        listAllIncludedUsageType.and("quotatype", listAllIncludedUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        listAllIncludedUsageType.done();
    +    }
    +
    +    @Override
    +    public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            sc.setParameters("quotatype", quotaType);
    +            result = search(sc, filter);
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        if (result.size() > 0) {
    +            //s_logger.info("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType  + " val=" + result.get(0).getCurrencyValue());
    +            return result.get(0);
    +        } else {
    +            //s_logger.info("Missing quota type " + quotaType);
    +            return null;
    +        }
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listAllTariffPlans(final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> tariffs = new ArrayList<QuotaTariffVO>();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            for (Integer quotaType : QuotaTypes.listQuotaTypes().keySet()) {
    +                sc.setParameters("quotatype", quotaType);
    +                List<QuotaTariffVO> result = search(sc, filter);
    +                if (result.size() > 0) {
    +                    tariffs.add(result.get(0));
    +                    s_logger.info("listAllTariffPlans onorbefore" + effectiveDate +  "quota type " + result.get(0).getDescription() + " , effective Date=" + result.get(0).getEffectiveOn() + " val=" + result.get(0).getCurrencyValue());
    +                }
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        return tariffs;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaTariff(QuotaTariffVO plan) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); // Switch
    +                                                                                    // to
    +        boolean result = false;
    +        try {
    +            // Usage DB
    +            result = this.update(plan.getId(), plan);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close(); // Switch back
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO addQuotaTariff(QuotaTariffVO plan) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); // Switch
    +                                                                                    // to
    +        QuotaTariffVO result = null;
    +        try {
    +            // Usage DB
    +            plan.setId(null);
    +            result = this.persist(plan);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close(); // Switch back
    --- End diff --
    
    See previous comments regarding opening/closing transactions on the same line.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950233
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    --- End diff --
    
    Why aren't all attributes declared ``final``?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950136
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    --- End diff --
    
    These two ``if`` blocks only differ in that the ``else`` sets the alert type.  Consider simplifying the method in a manner such as:
    ```
    public void sentSuccessfully() {
    
        quotaAccount.setLastStatementDate(new Date());
        _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    
        if (emailTemplateType != QuotaEmailTemplateTypes.QUOTA_STATEMENT) {                 
            quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
        }
    
    }
    ```


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951181
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37586898
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    --- End diff --
    
    This message seems like a DEBUG rather than INFO level. To my way of thinking, an INFO level message should provide information that a system administrator can use to understand the operation of the system. This message does not appear to provide such information.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951243
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    +            ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal onegbcost;
    +            BigDecimal rawusageingb;
    +            BigDecimal networkusage;
    +            onegbcost = tariff.getCurrencyValue();
    +            // s_logger.info("Quotatariff onegbcost=" + onegbcost);
    +            rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
    +            networkusage = rawusageingb.multiply(onegbcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), transferType, networkusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    Per previous comments, the immediate open and close of a transaction needs needs to be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r38059425
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDaoImpl.java ---
    @@ -0,0 +1,85 @@
    +// 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.Map;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +
    +import com.cloud.event.UsageEventVO;
    +import com.cloud.utils.db.DB;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +
    +@Component
    +@Local(value = { ServiceOfferingDao.class })
    +@DB()
    +public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Long> implements ServiceOfferingDao {
    +    protected static final Logger s_logger = Logger.getLogger(ServiceOfferingDaoImpl.class);
    +
    +    @Inject
    +    UserVmDetailsDao userVmDetailsDao;
    +
    +    @Override
    +    public ServiceOfferingVO findServiceOffering(final Long vmId, final long serviceOfferingId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
    +        ServiceOfferingVO result;
    +        try {
    +            result = findById(vmId, serviceOfferingId);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    public ServiceOfferingVO findById(Long vmId, long serviceOfferingId) {
    +        ServiceOfferingVO offering = super.findById(serviceOfferingId);
    +        if (offering.isDynamic()) {
    +            offering.setDynamicFlag(true);
    +            if (vmId == null) {
    +                throw new CloudRuntimeException("missing argument vmId");
    +            }
    --- End diff --
    
    vmId is only required if the service offering is dynamic.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949069
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    --- End diff --
    
    An INFO level log message should contextual information for a system administrator. This messages lacks that context. It should either be logged at DEBUG or context information should be added to it.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37604922
  
    --- Diff: engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java ---
    @@ -469,4 +478,25 @@ public void removeOldUsageRecords(int days) {
                 txn.close();
             }
         }
    +
    +    @SuppressWarnings("deprecation")
    +    public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        s_logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId);
    +        Filter usageFilter = new Filter(UsageVO.class, "startDate", true, 0L, 10000L);
    +        SearchCriteria<UsageVO> sc = 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.NULL);
    +        sc.addOr("quotaCalculated", SearchCriteria.Op.EQ, 0);
    +        s_logger.debug("Getting usage records" + usageFilter.getOrderBy());
    --- End diff --
    
    Accepted.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950999
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    --- End diff --
    
    I thought printing traces in the logs is not a good idea.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605311
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java ---
    @@ -0,0 +1,72 @@
    +//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.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaCreditsDao.class })
    +public class QuotaCreditsDaoImpl extends GenericDaoBase<QuotaCreditsVO, Long> implements QuotaCreditsDao {
    +    @Inject
    +    QuotaBalanceDao _quotaBalanceDao;
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaCreditsVO> findCredits(final long accountId, final long domainId, final Date startDate, final Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaCreditsVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
    +        } else {
    +            return new ArrayList<QuotaCreditsVO>();
    +        }
    +        List<QuotaCreditsVO> qc = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return qc;
    +    }
    +
    +    @Override
    +    public QuotaCreditsVO saveCredits(final QuotaCreditsVO credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    --- End diff --
    
    Accepted.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37585801
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateUpdateCmd.java ---
    @@ -0,0 +1,111 @@
    +//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 com.cloud.user.Account;
    +import org.apache.cloudstack.api.APICommand;
    +import org.apache.cloudstack.api.ApiErrorCode;
    +import org.apache.cloudstack.api.BaseCmd;
    +import org.apache.cloudstack.api.Parameter;
    +import org.apache.cloudstack.api.ServerApiException;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.api.response.SuccessResponse;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.log4j.Logger;
    +
    +import javax.inject.Inject;
    +import java.util.Arrays;
    +
    +@APICommand(name = "quotaEmailTemplateUpdate", responseObject = SuccessResponse.class, description = "Updates existing email templates for quota alerts", since = "4.2.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
    --- End diff --
    
    The ``since`` attribute says ``4.2.0``, but this API is new for ``4.6.0``.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37604903
  
    --- Diff: engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java ---
    @@ -469,4 +478,25 @@ public void removeOldUsageRecords(int days) {
                 txn.close();
             }
         }
    +
    +    @SuppressWarnings("deprecation")
    +    public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    This is to switch database from CLOUD_DB to USAGE_DB.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37586787
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    --- End diff --
    
    In order to avoid a ``NullPointerException``, this ``if`` condition should first check that ``quotaBalance`` is not null.  Also, consider using ``com.google.common.base.Preconditions#checkArgument`` for checking the validity of arguments.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950509
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    +            _smtpHost = smtpHost;
    +            _smtpPort = smtpPort;
    +            _smtpUseAuth = smtpUseAuth;
    +            _smtpUsername = smtpUsername;
    +            _smtpPassword = smtpPassword;
    +            _emailSender = emailSender;
    +
    +            if (_smtpHost != null) {
    +                Properties smtpProps = new Properties();
    +                smtpProps.put("mail.smtp.host", smtpHost);
    +                smtpProps.put("mail.smtp.port", smtpPort);
    +                smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtp.user", smtpUsername);
    +                }
    +
    +                smtpProps.put("mail.smtps.host", smtpHost);
    +                smtpProps.put("mail.smtps.port", smtpPort);
    +                smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtps.user", smtpUsername);
    +                }
    +
    +                if ((smtpUsername != null) && (smtpPassword != null)) {
    +                    _smtpSession = Session.getInstance(smtpProps, new Authenticator() {
    +                        @Override
    +                        protected PasswordAuthentication getPasswordAuthentication() {
    +                            return new PasswordAuthentication(smtpUsername, smtpPassword);
    +                        }
    +                    });
    +                } else {
    +                    _smtpSession = Session.getInstance(smtpProps);
    +                }
    +                _smtpSession.setDebug(smtpDebug);
    +            } else {
    +                _smtpSession = null;
    +            }
    +        }
    +
    +        public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException {
    +            if (_smtpSession != null) {
    +                SMTPMessage msg = new SMTPMessage(_smtpSession);
    +                msg.setSender(new InternetAddress(_emailSender, _emailSender));
    +                msg.setFrom(new InternetAddress(_emailSender, _emailSender));
    +
    +                for (String email : emails) {
    +                    if (email != null && !email.isEmpty()) {
    +                        try {
    +                            InternetAddress address = new InternetAddress(email, email);
    +                            msg.addRecipient(Message.RecipientType.TO, address);
    +                        } catch (Exception pokemon) {
    +                            s_logger.error("Exception in creating address for:" + email, pokemon);
    +                        }
    +                    }
    +                }
    +
    +                msg.setSubject(subject);
    +                msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
    +                msg.setContent(body, "text/html; charset=utf-8");
    +                msg.saveChanges();
    +
    +                SMTPTransport smtpTrans = null;
    +                if (_smtpUseAuth) {
    +                    smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                } else {
    +                    smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                }
    +                smtpTrans.connect();
    +                smtpTrans.sendMessage(msg, msg.getAllRecipients());
    +                smtpTrans.close();
    +            } else {
    +                throw new CloudRuntimeException("Unable to create smtp session.");
    +            }
    +        }
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    public static long getDifferenceDays(Date d1, Date d2) {
    +        long diff = d2.getTime() - d1.getTime();
    +        return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    Per previous comments, the immediate open and close of a transaction needs needs to be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582475
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java ---
    @@ -0,0 +1,73 @@
    +//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.List;
    +
    +import javax.ejb.Local;
    +
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.springframework.stereotype.Component;
    +
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaAccountDao.class })
    +public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> implements QuotaAccountDao {
    +
    +    @Override
    +    public List<QuotaAccountVO> listAll() {
    +        List<QuotaAccountVO> result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.listAll();
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaAccountVO findById(Long id) {
    +        QuotaAccountVO result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.findById(id);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37586853
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    --- End diff --
    
    Should there be a secondary sort condition if/when the updatedOn values are equal?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by DaanHoogland <gi...@git.apache.org>.
Github user DaanHoogland commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-136335429
  
    7.5kloc would justify 15klotc (kilo-lines of test code) I am not very worried about the quality of this code or the zeal of some bluely shaped programmers to maintain it but it will have to be maintainable by others! over 10 lines of code is an hazard to review if there is not twice as much test-code there, give or take.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37628373
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    --- End diff --
    
    @abhinandanprateek The issue is that equal values will be placed in a non-deterministic order.  Is there a secondary sort condition that can be evaluated to provide greater determinism?  For example, would it make sense to sort on account or amount?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582598
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java ---
    @@ -0,0 +1,72 @@
    +//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.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaCreditsDao.class })
    +public class QuotaCreditsDaoImpl extends GenericDaoBase<QuotaCreditsVO, Long> implements QuotaCreditsDao {
    +    @Inject
    +    QuotaBalanceDao _quotaBalanceDao;
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaCreditsVO> findCredits(final long accountId, final long domainId, final Date startDate, final Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaCreditsVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
    +        } else {
    +            return new ArrayList<QuotaCreditsVO>();
    +        }
    +        List<QuotaCreditsVO> qc = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return qc;
    +    }
    +
    +    @Override
    +    public QuotaCreditsVO saveCredits(final QuotaCreditsVO credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    --- End diff --
    
    This transaction never appears to be closed.  Seems that a ``try { ... } finally { txn.close() }`` block is needed.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by DaanHoogland <gi...@git.apache.org>.
Github user DaanHoogland commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-133335452
  
    I saw @jburwell his remarks coming by. I trust him to be thorough. I will review when I see unit and integration tests. 
    try using more try with resource when possible. No beef just 
    try (Transaction trx =TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {} is an often used pattern. maybe create a method/utility called switchDb(...)


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582389
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLaterBalanceEntry(final long accountId, final long domainId, final Date afterThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.GT, afterThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @Override
    +    public void saveQuotaBalance(final List<QuotaBalanceVO> credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            for (QuotaBalanceVO credit : credits) {
    +                persist(credit);
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605369
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java ---
    @@ -0,0 +1,138 @@
    +//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 com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaTariffDao.class })
    +public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> implements QuotaTariffDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaTariffDaoImpl.class.getName());
    +
    +    private final SearchBuilder<QuotaTariffVO> searchUsageType;
    +    private final SearchBuilder<QuotaTariffVO> listAllIncludedUsageType;
    +
    +    public QuotaTariffDaoImpl() {
    +        super();
    +        searchUsageType = createSearchBuilder();
    +        searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        searchUsageType.done();
    +
    +        listAllIncludedUsageType = createSearchBuilder();
    +        listAllIncludedUsageType.and("onorbefore", listAllIncludedUsageType.entity().getEffectiveOn(), SearchCriteria.Op.LTEQ);
    +        listAllIncludedUsageType.and("quotatype", listAllIncludedUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        listAllIncludedUsageType.done();
    +    }
    +
    +    @Override
    +    public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            sc.setParameters("quotatype", quotaType);
    +            result = search(sc, filter);
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        if (result.size() > 0) {
    +            //s_logger.info("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType  + " val=" + result.get(0).getCurrencyValue());
    +            return result.get(0);
    +        } else {
    +            //s_logger.info("Missing quota type " + quotaType);
    +            return null;
    +        }
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listAllTariffPlans(final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> tariffs = new ArrayList<QuotaTariffVO>();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            for (Integer quotaType : QuotaTypes.listQuotaTypes().keySet()) {
    +                sc.setParameters("quotatype", quotaType);
    +                List<QuotaTariffVO> result = search(sc, filter);
    +                if (result.size() > 0) {
    +                    tariffs.add(result.get(0));
    +                    s_logger.info("listAllTariffPlans onorbefore" + effectiveDate +  "quota type " + result.get(0).getDescription() + " , effective Date=" + result.get(0).getEffectiveOn() + " val=" + result.get(0).getCurrencyValue());
    +                }
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        return tariffs;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaTariff(QuotaTariffVO plan) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); // Switch
    +                                                                                    // to
    +        boolean result = false;
    +        try {
    +            // Usage DB
    +            result = this.update(plan.getId(), plan);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close(); // Switch back
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO addQuotaTariff(QuotaTariffVO plan) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); // Switch
    +                                                                                    // to
    +        QuotaTariffVO result = null;
    +        try {
    +            // Usage DB
    +            plan.setId(null);
    +            result = this.persist(plan);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close(); // Switch back
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950718
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    +                    s_logger.error(String.format("Unable to unlock account %s after getting enough quota credits", account.getAccountName()));
    +                }
    +            }
    +        }
    +
    +        String creditor = String.valueOf(Account.ACCOUNT_ID_SYSTEM);
    +        User creditorUser = _userDao.getUser(updatedBy);
    +        if (creditorUser != null) {
    +            creditor = creditorUser.getUsername();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        QuotaCreditsResponse response = new QuotaCreditsResponse(result, creditor);
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    private QuotaEmailTemplateResponse createQuotaEmailResponse(QuotaEmailTemplatesVO template) {
    +        QuotaEmailTemplateResponse response = new QuotaEmailTemplateResponse();
    +        response.setTemplateType(template.getTemplateName());
    +        response.setTemplateSubject(template.getTemplateSubject());
    +        response.setTemplateText(template.getTemplateBody());
    +        response.setLocale(template.getLocale());
    +        response.setLastUpdatedOn(template.getLastUpdated());
    +        return response;
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplateResponse> listQuotaEmailTemplates(QuotaEmailTemplateListCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        final List<QuotaEmailTemplateResponse> responses = new ArrayList<QuotaEmailTemplateResponse>();
    +        for (final QuotaEmailTemplatesVO template : templates) {
    +            responses.add(createQuotaEmailResponse(template));
    +        }
    +        return responses;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplateUpdateCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        final String templateSubject = StringEscapeUtils.escapeJavaScript(cmd.getTemplateSubject());
    +        final String templateBody = StringEscapeUtils.escapeJavaScript(cmd.getTemplateBody());
    +        final String locale = cmd.getLocale();
    +
    +        final List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        if (templates.size() == 1) {
    +            final QuotaEmailTemplatesVO template = templates.get(0);
    +            template.setTemplateSubject(templateSubject);
    +            template.setTemplateBody(templateBody);
    +            if (locale != null) {
    +                template.setLocale(locale);
    +            }
    +            return _quotaEmailTemplateDao.updateQuotaEmailTemplate(template);
    +        }
    +        return false;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("There are no balance entries on or before the requested date.");
    +        }
    +        if (startDate == null) {
    +            startDate = new Date();
    +        }
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    --- End diff --
    
    yes can be used.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950548
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    --- End diff --
    
    InvalidParameterValueException exception indicates to the user that there is something wrong with the parameters here it indicates issues with account param.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37952296
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    +            _smtpHost = smtpHost;
    +            _smtpPort = smtpPort;
    +            _smtpUseAuth = smtpUseAuth;
    +            _smtpUsername = smtpUsername;
    +            _smtpPassword = smtpPassword;
    +            _emailSender = emailSender;
    +
    +            if (_smtpHost != null) {
    +                Properties smtpProps = new Properties();
    +                smtpProps.put("mail.smtp.host", smtpHost);
    +                smtpProps.put("mail.smtp.port", smtpPort);
    +                smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtp.user", smtpUsername);
    +                }
    +
    +                smtpProps.put("mail.smtps.host", smtpHost);
    +                smtpProps.put("mail.smtps.port", smtpPort);
    +                smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtps.user", smtpUsername);
    +                }
    +
    +                if ((smtpUsername != null) && (smtpPassword != null)) {
    +                    _smtpSession = Session.getInstance(smtpProps, new Authenticator() {
    +                        @Override
    +                        protected PasswordAuthentication getPasswordAuthentication() {
    +                            return new PasswordAuthentication(smtpUsername, smtpPassword);
    +                        }
    +                    });
    +                } else {
    +                    _smtpSession = Session.getInstance(smtpProps);
    +                }
    +                _smtpSession.setDebug(smtpDebug);
    +            } else {
    +                _smtpSession = null;
    +            }
    +        }
    +
    +        public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException {
    +            if (_smtpSession != null) {
    +                SMTPMessage msg = new SMTPMessage(_smtpSession);
    +                msg.setSender(new InternetAddress(_emailSender, _emailSender));
    +                msg.setFrom(new InternetAddress(_emailSender, _emailSender));
    +
    +                for (String email : emails) {
    +                    if (email != null && !email.isEmpty()) {
    +                        try {
    +                            InternetAddress address = new InternetAddress(email, email);
    +                            msg.addRecipient(Message.RecipientType.TO, address);
    +                        } catch (Exception pokemon) {
    +                            s_logger.error("Exception in creating address for:" + email, pokemon);
    +                        }
    +                    }
    +                }
    +
    +                msg.setSubject(subject);
    +                msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
    +                msg.setContent(body, "text/html; charset=utf-8");
    +                msg.saveChanges();
    +
    +                SMTPTransport smtpTrans = null;
    +                if (_smtpUseAuth) {
    +                    smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                } else {
    +                    smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                }
    +                smtpTrans.connect();
    +                smtpTrans.sendMessage(msg, msg.getAllRecipients());
    +                smtpTrans.close();
    +            } else {
    +                throw new CloudRuntimeException("Unable to create smtp session.");
    +            }
    +        }
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    public static long getDifferenceDays(Date d1, Date d2) {
    +        long diff = d2.getTime() - d1.getTime();
    +        return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605477
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    --- End diff --
    
    Will check.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950446
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    +            _smtpHost = smtpHost;
    +            _smtpPort = smtpPort;
    +            _smtpUseAuth = smtpUseAuth;
    +            _smtpUsername = smtpUsername;
    +            _smtpPassword = smtpPassword;
    +            _emailSender = emailSender;
    +
    +            if (_smtpHost != null) {
    +                Properties smtpProps = new Properties();
    +                smtpProps.put("mail.smtp.host", smtpHost);
    +                smtpProps.put("mail.smtp.port", smtpPort);
    +                smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtp.user", smtpUsername);
    +                }
    +
    +                smtpProps.put("mail.smtps.host", smtpHost);
    +                smtpProps.put("mail.smtps.port", smtpPort);
    +                smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtps.user", smtpUsername);
    +                }
    +
    +                if ((smtpUsername != null) && (smtpPassword != null)) {
    +                    _smtpSession = Session.getInstance(smtpProps, new Authenticator() {
    +                        @Override
    +                        protected PasswordAuthentication getPasswordAuthentication() {
    +                            return new PasswordAuthentication(smtpUsername, smtpPassword);
    +                        }
    +                    });
    +                } else {
    +                    _smtpSession = Session.getInstance(smtpProps);
    +                }
    +                _smtpSession.setDebug(smtpDebug);
    +            } else {
    +                _smtpSession = null;
    +            }
    +        }
    +
    +        public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException {
    +            if (_smtpSession != null) {
    +                SMTPMessage msg = new SMTPMessage(_smtpSession);
    +                msg.setSender(new InternetAddress(_emailSender, _emailSender));
    +                msg.setFrom(new InternetAddress(_emailSender, _emailSender));
    +
    +                for (String email : emails) {
    +                    if (email != null && !email.isEmpty()) {
    +                        try {
    +                            InternetAddress address = new InternetAddress(email, email);
    +                            msg.addRecipient(Message.RecipientType.TO, address);
    +                        } catch (Exception pokemon) {
    +                            s_logger.error("Exception in creating address for:" + email, pokemon);
    +                        }
    +                    }
    +                }
    +
    +                msg.setSubject(subject);
    +                msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
    +                msg.setContent(body, "text/html; charset=utf-8");
    +                msg.saveChanges();
    +
    +                SMTPTransport smtpTrans = null;
    +                if (_smtpUseAuth) {
    +                    smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                } else {
    +                    smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                }
    +                smtpTrans.connect();
    +                smtpTrans.sendMessage(msg, msg.getAllRecipients());
    +                smtpTrans.close();
    +            } else {
    +                throw new CloudRuntimeException("Unable to create smtp session.");
    +            }
    +        }
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    --- End diff --
    
    Consider using the ``DateUtil.addDaysTo`` method suggested previously.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950415
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    --- End diff --
    
    This is the first version, as a developer, I think this message is important for me. Yes, I have no issues to remove it in later releases.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37952257
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    +            ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal onegbcost;
    +            BigDecimal rawusageingb;
    +            BigDecimal networkusage;
    +            onegbcost = tariff.getCurrencyValue();
    +            // s_logger.info("Quotatariff onegbcost=" + onegbcost);
    +            rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
    +            networkusage = rawusageingb.multiply(onegbcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), transferType, networkusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947136
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    --- End diff --
    
    Please remove commented code -- it is cruft.  git provides the means to track changes in code including removed code.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582530
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java ---
    @@ -0,0 +1,72 @@
    +//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.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaCreditsDao.class })
    +public class QuotaCreditsDaoImpl extends GenericDaoBase<QuotaCreditsVO, Long> implements QuotaCreditsDao {
    +    @Inject
    +    QuotaBalanceDao _quotaBalanceDao;
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaCreditsVO> findCredits(final long accountId, final long domainId, final Date startDate, final Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaCreditsVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
    +        } else {
    +            return new ArrayList<QuotaCreditsVO>();
    +        }
    +        List<QuotaCreditsVO> qc = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951007
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    --- End diff --
    
    Consider using ``List#isEmpty()`` instead of checking ``size`` as it is more idiomatic and expressive of intent.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605401
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaStatementCmd.java ---
    @@ -0,0 +1,143 @@
    +//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.Date;
    +import java.util.List;
    +
    +import javax.inject.Inject;
    +
    +import org.apache.log4j.Logger;
    +import org.apache.cloudstack.api.APICommand;
    +import org.apache.cloudstack.api.ApiConstants;
    +import org.apache.cloudstack.api.BaseCmd;
    +import org.apache.cloudstack.api.Parameter;
    +import org.apache.cloudstack.api.response.AccountResponse;
    +import org.apache.cloudstack.api.response.DomainResponse;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.api.response.QuotaStatementResponse;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.api.response.QuotaStatementItemResponse;
    +
    +import com.cloud.user.Account;
    +
    +@APICommand(name = "quotaStatement", responseObject = QuotaStatementItemResponse.class, description = "Create a quota statement", since = "4.2.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
    --- End diff --
    
    ok


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37948797
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    +                    s_logger.error(String.format("Unable to unlock account %s after getting enough quota credits", account.getAccountName()));
    +                }
    +            }
    +        }
    +
    +        String creditor = String.valueOf(Account.ACCOUNT_ID_SYSTEM);
    +        User creditorUser = _userDao.getUser(updatedBy);
    +        if (creditorUser != null) {
    +            creditor = creditorUser.getUsername();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        QuotaCreditsResponse response = new QuotaCreditsResponse(result, creditor);
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    private QuotaEmailTemplateResponse createQuotaEmailResponse(QuotaEmailTemplatesVO template) {
    +        QuotaEmailTemplateResponse response = new QuotaEmailTemplateResponse();
    +        response.setTemplateType(template.getTemplateName());
    +        response.setTemplateSubject(template.getTemplateSubject());
    +        response.setTemplateText(template.getTemplateBody());
    +        response.setLocale(template.getLocale());
    +        response.setLastUpdatedOn(template.getLastUpdated());
    +        return response;
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplateResponse> listQuotaEmailTemplates(QuotaEmailTemplateListCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        final List<QuotaEmailTemplateResponse> responses = new ArrayList<QuotaEmailTemplateResponse>();
    +        for (final QuotaEmailTemplatesVO template : templates) {
    +            responses.add(createQuotaEmailResponse(template));
    +        }
    +        return responses;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplateUpdateCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        final String templateSubject = StringEscapeUtils.escapeJavaScript(cmd.getTemplateSubject());
    +        final String templateBody = StringEscapeUtils.escapeJavaScript(cmd.getTemplateBody());
    +        final String locale = cmd.getLocale();
    +
    +        final List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        if (templates.size() == 1) {
    +            final QuotaEmailTemplatesVO template = templates.get(0);
    +            template.setTemplateSubject(templateSubject);
    +            template.setTemplateBody(templateBody);
    +            if (locale != null) {
    +                template.setLocale(locale);
    +            }
    +            return _quotaEmailTemplateDao.updateQuotaEmailTemplate(template);
    +        }
    +        return false;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("There are no balance entries on or before the requested date.");
    +        }
    +        if (startDate == null) {
    +            startDate = new Date();
    +        }
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    --- End diff --
    
    Why not use a for-each for more concise iteration code?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947297
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Per comments above, the immediate open and close of a database connection needs to be replaced with the newer transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37604908
  
    --- Diff: engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java ---
    @@ -469,4 +478,25 @@ public void removeOldUsageRecords(int days) {
                 txn.close();
             }
         }
    +
    +    @SuppressWarnings("deprecation")
    +    public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        s_logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId);
    --- End diff --
    
    Accepted.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582819
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java ---
    @@ -0,0 +1,64 @@
    +//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 com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaEmailTemplatesDao.class })
    +public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase<QuotaEmailTemplatesVO, Long> implements QuotaEmailTemplatesDao {
    +
    +    protected SearchBuilder<QuotaEmailTemplatesVO> QuotaEmailTemplateSearch;
    +
    +    public QuotaEmailTemplatesDaoImpl() {
    +        super();
    +
    +        QuotaEmailTemplateSearch = createSearchBuilder();
    +        QuotaEmailTemplateSearch.and("template_name", QuotaEmailTemplateSearch.entity().getTemplateName(), SearchCriteria.Op.EQ);
    +        QuotaEmailTemplateSearch.done();
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        SearchCriteria<QuotaEmailTemplatesVO> sc = QuotaEmailTemplateSearch.create();
    +        if (templateName != null) {
    +            sc.setParameters("template_name", templateName);
    +        }
    +        List<QuotaEmailTemplatesVO> result = this.listBy(sc);
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplatesVO template) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        final boolean result = this.update(template.getId(), template);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    This ``close`` call should be wrapped in a ``try { } finally { }`` block to ensure that transaction is closed in the event of an unchecked exception.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-133352253
  
    Where ever there is a DB change the pattern is use. The plain switch is used where it is db readonly.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37583016
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java ---
    @@ -0,0 +1,138 @@
    +//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 com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaTariffDao.class })
    +public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> implements QuotaTariffDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaTariffDaoImpl.class.getName());
    +
    +    private final SearchBuilder<QuotaTariffVO> searchUsageType;
    +    private final SearchBuilder<QuotaTariffVO> listAllIncludedUsageType;
    +
    +    public QuotaTariffDaoImpl() {
    +        super();
    +        searchUsageType = createSearchBuilder();
    +        searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        searchUsageType.done();
    +
    +        listAllIncludedUsageType = createSearchBuilder();
    +        listAllIncludedUsageType.and("onorbefore", listAllIncludedUsageType.entity().getEffectiveOn(), SearchCriteria.Op.LTEQ);
    +        listAllIncludedUsageType.and("quotatype", listAllIncludedUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        listAllIncludedUsageType.done();
    +    }
    +
    +    @Override
    +    public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            sc.setParameters("quotatype", quotaType);
    +            result = search(sc, filter);
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        if (result.size() > 0) {
    +            //s_logger.info("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType  + " val=" + result.get(0).getCurrencyValue());
    --- End diff --
    
    Please remove commented code.  Git provides a better history of code changes without obscurity.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605319
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java ---
    @@ -0,0 +1,64 @@
    +//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 com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaEmailTemplatesDao.class })
    +public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase<QuotaEmailTemplatesVO, Long> implements QuotaEmailTemplatesDao {
    +
    +    protected SearchBuilder<QuotaEmailTemplatesVO> QuotaEmailTemplateSearch;
    +
    +    public QuotaEmailTemplatesDaoImpl() {
    +        super();
    +
    +        QuotaEmailTemplateSearch = createSearchBuilder();
    +        QuotaEmailTemplateSearch.and("template_name", QuotaEmailTemplateSearch.entity().getTemplateName(), SearchCriteria.Op.EQ);
    +        QuotaEmailTemplateSearch.done();
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        SearchCriteria<QuotaEmailTemplatesVO> sc = QuotaEmailTemplateSearch.create();
    +        if (templateName != null) {
    +            sc.setParameters("template_name", templateName);
    +        }
    +        List<QuotaEmailTemplatesVO> result = this.listBy(sc);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582622
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java ---
    @@ -0,0 +1,64 @@
    +//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 com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaEmailTemplatesDao.class })
    +public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase<QuotaEmailTemplatesVO, Long> implements QuotaEmailTemplatesDao {
    +
    +    protected SearchBuilder<QuotaEmailTemplatesVO> QuotaEmailTemplateSearch;
    +
    +    public QuotaEmailTemplatesDaoImpl() {
    +        super();
    +
    +        QuotaEmailTemplateSearch = createSearchBuilder();
    +        QuotaEmailTemplateSearch.and("template_name", QuotaEmailTemplateSearch.entity().getTemplateName(), SearchCriteria.Op.EQ);
    +        QuotaEmailTemplateSearch.done();
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        SearchCriteria<QuotaEmailTemplatesVO> sc = QuotaEmailTemplateSearch.create();
    +        if (templateName != null) {
    +            sc.setParameters("template_name", templateName);
    +        }
    +        List<QuotaEmailTemplatesVO> result = this.listBy(sc);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by bhaisaab <gi...@git.apache.org>.
Github user bhaisaab commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-136322597
  
    Changes included from the PR, branch rebased from latest master.
    
    Please help review - @karuturi @jburwell @DaanHoogland @remibergsma @kishankavala and others


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949073
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    +                } else {
    +                    return qbrecords;
    +                }
    +            } else {
    +                throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    --- End diff --
    
    This error does not seem to pertain to a parameter value. As such, the type should be changed to something more appropriate such as IllegalStateException.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37948979
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    --- End diff --
    
    This error does not seem to pertain to a parameter value.  As such, the type should be changed to something more appropriate such as ``IllegalStateException``.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605555
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    --- End diff --
    
    Since items need to be pruned, have to use an iterator.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37628668
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    --- End diff --
    
    @abhinandanprateek this issue is not whether or not it is the correct way to modify a list, but whether or not that list should be modified.  ``quotaBalance`` is passed into the method.  Therefore, ``it.remove()`` is modifying a list referenced by the caller -- causing unexpected side effects.  To avoid these effects, consider copying the contents of ``quotaBalance`` to a new list (e.g. ``final List<QuotaBalanceVO> updatedBalances = new ArrayList<>(quotaBalance)``) and modifying the copied list.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37952261
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    +            ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal onegbcost;
    +            BigDecimal rawusageingb;
    +            BigDecimal networkusage;
    +            onegbcost = tariff.getCurrencyValue();
    +            // s_logger.info("Quotatariff onegbcost=" + onegbcost);
    +            rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
    +            networkusage = rawusageingb.multiply(onegbcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), transferType, networkusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950867
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    +                    s_logger.error(String.format("Unable to unlock account %s after getting enough quota credits", account.getAccountName()));
    --- End diff --
    
    i think that was done due to code that is commented now. Should be removed.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950535
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    +            _smtpHost = smtpHost;
    +            _smtpPort = smtpPort;
    +            _smtpUseAuth = smtpUseAuth;
    +            _smtpUsername = smtpUsername;
    +            _smtpPassword = smtpPassword;
    +            _emailSender = emailSender;
    +
    +            if (_smtpHost != null) {
    +                Properties smtpProps = new Properties();
    +                smtpProps.put("mail.smtp.host", smtpHost);
    +                smtpProps.put("mail.smtp.port", smtpPort);
    +                smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtp.user", smtpUsername);
    +                }
    +
    +                smtpProps.put("mail.smtps.host", smtpHost);
    +                smtpProps.put("mail.smtps.port", smtpPort);
    +                smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtps.user", smtpUsername);
    +                }
    +
    +                if ((smtpUsername != null) && (smtpPassword != null)) {
    +                    _smtpSession = Session.getInstance(smtpProps, new Authenticator() {
    +                        @Override
    +                        protected PasswordAuthentication getPasswordAuthentication() {
    +                            return new PasswordAuthentication(smtpUsername, smtpPassword);
    +                        }
    +                    });
    +                } else {
    +                    _smtpSession = Session.getInstance(smtpProps);
    +                }
    +                _smtpSession.setDebug(smtpDebug);
    +            } else {
    +                _smtpSession = null;
    +            }
    +        }
    +
    +        public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException {
    +            if (_smtpSession != null) {
    +                SMTPMessage msg = new SMTPMessage(_smtpSession);
    +                msg.setSender(new InternetAddress(_emailSender, _emailSender));
    +                msg.setFrom(new InternetAddress(_emailSender, _emailSender));
    +
    +                for (String email : emails) {
    +                    if (email != null && !email.isEmpty()) {
    +                        try {
    +                            InternetAddress address = new InternetAddress(email, email);
    +                            msg.addRecipient(Message.RecipientType.TO, address);
    +                        } catch (Exception pokemon) {
    +                            s_logger.error("Exception in creating address for:" + email, pokemon);
    +                        }
    +                    }
    +                }
    +
    +                msg.setSubject(subject);
    +                msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
    +                msg.setContent(body, "text/html; charset=utf-8");
    +                msg.saveChanges();
    +
    +                SMTPTransport smtpTrans = null;
    +                if (_smtpUseAuth) {
    +                    smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                } else {
    +                    smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                }
    +                smtpTrans.connect();
    +                smtpTrans.sendMessage(msg, msg.getAllRecipients());
    +                smtpTrans.close();
    +            } else {
    +                throw new CloudRuntimeException("Unable to create smtp session.");
    +            }
    +        }
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    public static long getDifferenceDays(Date d1, Date d2) {
    +        long diff = d2.getTime() - d1.getTime();
    +        return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return success;
    +    }
    +
    +    public boolean enableAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    Per previous comments, the immediate open and close of a transaction needs needs to be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605375
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaUsageDaoImpl.java ---
    @@ -0,0 +1,106 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +
    +import com.cloud.utils.Pair;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaUsageDao.class })
    +public class QuotaUsageDaoImpl extends GenericDaoBase<QuotaUsageVO, Long> implements QuotaUsageDao {
    +
    +    @Override
    +    public Pair<List<QuotaUsageVO>, Integer> searchAndCountAllRecords(SearchCriteria<QuotaUsageVO> sc, Filter filter) {
    +        return listAndCountIncludingRemovedBy(sc, filter);
    +    }
    +
    +    @Override
    +    public void saveQuotaUsage(List<QuotaUsageVO> records) {
    +        for (QuotaUsageVO usageRecord : records) {
    +            persist(usageRecord);
    +        }
    +    }
    +
    +    @Override
    +    public BigDecimal findTotalQuotaUsage(final Long accountId, final Long domainId, final Integer usageType, final Date startDate, final Date endDate) {
    +        List<QuotaUsageVO> quotaUsage = findQuotaUsage(accountId, domainId, null, startDate, endDate);
    +        BigDecimal total = new BigDecimal(0);
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            total = total.add(quotaRecord.getQuotaUsed());
    +        }
    +        return total;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaUsageVO> findQuotaUsage(final Long accountId, final Long domainId, final Integer usageType, final Date startDate, final Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        List<QuotaUsageVO> quotaUsageRecords = null;
    +        try {
    +            // TODO instead of max value query with reasonable number and
    +            // iterate
    +            SearchCriteria<QuotaUsageVO> sc = createSearchCriteria();
    +            if (accountId != null) {
    +                sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +            }
    +            /*
    +             * if (isDomainAdmin) { SearchCriteria<DomainVO> sdc =
    +             * _domainDao.createSearchCriteria(); sdc.addOr("path",
    +             * SearchCriteria.Op.LIKE,
    +             * _domainDao.findById(caller.getDomainId()).getPath() + "%");
    +             * List<DomainVO> domains = _domainDao.search(sdc, null); List<Long>
    +             * domainIds = new ArrayList<Long>(); for (DomainVO domain :
    +             * domains) domainIds.add(domain.getId()); sc.addAnd("domainId",
    +             * SearchCriteria.Op.IN, domainIds.toArray());
    +             * s_logger.debug("Account ID=" + accountId); }
    +             */
    --- End diff --
    
    ok


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-132442691
  
    @DaanHoogland @kishankavala @jburwell The documentation to run and check the plugin are in this pull request: https://github.com/apache/cloudstack-docs-admin/pull/30


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605263
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java ---
    @@ -0,0 +1,73 @@
    +//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.List;
    +
    +import javax.ejb.Local;
    +
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.springframework.stereotype.Component;
    +
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaAccountDao.class })
    +public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> implements QuotaAccountDao {
    +
    +    @Override
    +    public List<QuotaAccountVO> listAll() {
    +        List<QuotaAccountVO> result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.listAll();
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaAccountVO findById(Long id) {
    +        QuotaAccountVO result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.findById(id);
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaAccountVO persist(QuotaAccountVO entity) {
    +        QuotaAccountVO result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.persist(entity);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37587088
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    --- End diff --
    
    Why is the underlying list being modified?  My concern is that this method is not re-entrant nor will it function properly if called concurrently.  


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605260
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java ---
    @@ -0,0 +1,73 @@
    +//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.List;
    +
    +import javax.ejb.Local;
    +
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.springframework.stereotype.Component;
    +
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaAccountDao.class })
    +public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> implements QuotaAccountDao {
    +
    +    @Override
    +    public List<QuotaAccountVO> listAll() {
    +        List<QuotaAccountVO> result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.listAll();
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaAccountVO findById(Long id) {
    +        QuotaAccountVO result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.findById(id);
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaAccountVO persist(QuotaAccountVO entity) {
    +        QuotaAccountVO result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.persist(entity);
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public boolean update(Long id, QuotaAccountVO entity) {
    +        boolean result = false;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.update(id, entity);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949627
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37604919
  
    --- Diff: engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java ---
    @@ -469,4 +478,25 @@ public void removeOldUsageRecords(int days) {
                 txn.close();
             }
         }
    +
    +    @SuppressWarnings("deprecation")
    +    public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        s_logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId);
    +        Filter usageFilter = new Filter(UsageVO.class, "startDate", true, 0L, 10000L);
    +        SearchCriteria<UsageVO> sc = 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.NULL);
    +        sc.addOr("quotaCalculated", SearchCriteria.Op.EQ, 0);
    +        s_logger.debug("Getting usage records" + usageFilter.getOrderBy());
    +        Pair<List<UsageVO>, Integer> usageRecords = searchAndCountAllRecords(sc, usageFilter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    This is to switch database.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by DaanHoogland <gi...@git.apache.org>.
Github user DaanHoogland commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-130652414
  
    one big chunk of work Abhi, I have skimmed it and the structure looks good. I must have missed some minor issues in these 7000+ lines of code, though.
    One major issue is that there is no single test in there, nor a test instruction/- description. I am sure I will break it if I poke at the other end of the system a bit.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37623820
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java ---
    @@ -0,0 +1,92 @@
    +// 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.constant;
    +
    +import org.apache.cloudstack.usage.UsageTypes;
    +
    +import java.util.HashMap;
    +
    +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;
    +
    +    private Integer quotaType;
    +    private String quotaName;
    +    private String quotaUnit;
    +    private String description;
    +    private String discriminator;
    +
    +    public QuotaTypes(Integer quotaType, String name, String unit, String description) {
    +        this.quotaType = quotaType;
    +        this.description = description;
    +        this.quotaName = name;
    +        this.quotaUnit = unit;
    +        this.discriminator = "None";
    +    }
    +
    +    public static HashMap<Integer, QuotaTypes> listQuotaTypes() {
    +        final HashMap<Integer, QuotaTypes> quotaTypeList = new HashMap<Integer, QuotaTypes>();
    +        quotaTypeList.put(new Integer(RUNNING_VM), new QuotaTypes(new Integer(RUNNING_VM), "RUNNING_VM", "Compute-Month", "Running Vm Usage"));
    --- End diff --
    
    Understanding the compatibility with the type declarations of existing code, I can see no reason to avoid the constant pool and put unnecessary pressure on the garbage collector by using ``new Integer()`` rather than ``Integer.valueOf()``.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605238
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLaterBalanceEntry(final long accountId, final long domainId, final Date afterThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.GT, afterThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @Override
    +    public void saveQuotaBalance(final List<QuotaBalanceVO> credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            for (QuotaBalanceVO credit : credits) {
    +                persist(credit);
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaBalanceVO> findCreditBalance(final long accountId, final long domainId, final Date lastbalancedate, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.GT, 0);
    +        if ((lastbalancedate != null) && (beforeThis != null) && lastbalancedate.before(beforeThis)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, lastbalancedate, beforeThis);
    +        } else {
    +            return new ArrayList<QuotaBalanceVO>();
    +        }
    +        List<QuotaBalanceVO> qb = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return qb;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalance(final Long accountId, final Long domainId, final Date startDate, final Date endDate) {
    +    // TODO account for series of credits around boundaries
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        List<QuotaBalanceVO> quotaUsageRecords = null;
    +        try {
    +            SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +            if (accountId != null) {
    +                sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +            }
    +            if (domainId != null) {
    +                sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +            }
    +            if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
    +                sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
    +            } else {
    +                return new ArrayList<QuotaBalanceVO>();
    +            }
    +            quotaUsageRecords = listBy(sc);
    +            if (quotaUsageRecords.size() == 0) {
    +                quotaUsageRecords.addAll(lastQuotaBalanceVO(accountId, domainId, startDate));
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +
    +        TransactionLegacy.open(opendb).close();
    +        return quotaUsageRecords;
    +    }
    +
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaBalanceVO> lastQuotaBalanceVO(final Long accountId, final Long domainId, final Date pivotDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        List<QuotaBalanceVO> quotaUsageRecords = null;
    +        List<QuotaBalanceVO> trimmedRecords = new ArrayList<QuotaBalanceVO>();
    +        try {
    +            Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 100L);
    +            // ASSUMPTION there will be less than 100 continuous credit
    +            // transactions
    +            SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +            if (accountId != null) {
    +                sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +            }
    +            if (domainId != null) {
    +                sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +            }
    +            if ((pivotDate != null)) {
    +                sc.addAnd("updatedOn", SearchCriteria.Op.LTEQ, pivotDate);
    +            }
    +            quotaUsageRecords = search(sc, filter);
    +
    +            // get records before startDate to find start balance
    +            for (Iterator<QuotaBalanceVO> it = quotaUsageRecords.iterator(); it.hasNext();) {
    +                QuotaBalanceVO entry = it.next();
    +                s_logger.info("findQuotaBalance Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +                if (entry.getCreditsId() > 0) {
    +                    trimmedRecords.add(entry);
    +                } else {
    +                    trimmedRecords.add(entry);
    +                    break; // add only consecutive credit entries and last balance entry
    +                }
    +            }
    +
    +        } finally {
    +            txn.close();
    +        }
    +
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37952280
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    +            _smtpHost = smtpHost;
    +            _smtpPort = smtpPort;
    +            _smtpUseAuth = smtpUseAuth;
    +            _smtpUsername = smtpUsername;
    +            _smtpPassword = smtpPassword;
    +            _emailSender = emailSender;
    +
    +            if (_smtpHost != null) {
    +                Properties smtpProps = new Properties();
    +                smtpProps.put("mail.smtp.host", smtpHost);
    +                smtpProps.put("mail.smtp.port", smtpPort);
    +                smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtp.user", smtpUsername);
    +                }
    +
    +                smtpProps.put("mail.smtps.host", smtpHost);
    +                smtpProps.put("mail.smtps.port", smtpPort);
    +                smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtps.user", smtpUsername);
    +                }
    +
    +                if ((smtpUsername != null) && (smtpPassword != null)) {
    +                    _smtpSession = Session.getInstance(smtpProps, new Authenticator() {
    +                        @Override
    +                        protected PasswordAuthentication getPasswordAuthentication() {
    +                            return new PasswordAuthentication(smtpUsername, smtpPassword);
    +                        }
    +                    });
    +                } else {
    +                    _smtpSession = Session.getInstance(smtpProps);
    +                }
    +                _smtpSession.setDebug(smtpDebug);
    +            } else {
    +                _smtpSession = null;
    +            }
    +        }
    +
    +        public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException {
    +            if (_smtpSession != null) {
    +                SMTPMessage msg = new SMTPMessage(_smtpSession);
    +                msg.setSender(new InternetAddress(_emailSender, _emailSender));
    +                msg.setFrom(new InternetAddress(_emailSender, _emailSender));
    +
    +                for (String email : emails) {
    +                    if (email != null && !email.isEmpty()) {
    +                        try {
    +                            InternetAddress address = new InternetAddress(email, email);
    +                            msg.addRecipient(Message.RecipientType.TO, address);
    +                        } catch (Exception pokemon) {
    +                            s_logger.error("Exception in creating address for:" + email, pokemon);
    +                        }
    +                    }
    +                }
    +
    +                msg.setSubject(subject);
    +                msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
    +                msg.setContent(body, "text/html; charset=utf-8");
    +                msg.saveChanges();
    +
    +                SMTPTransport smtpTrans = null;
    +                if (_smtpUseAuth) {
    +                    smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                } else {
    +                    smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                }
    +                smtpTrans.connect();
    +                smtpTrans.sendMessage(msg, msg.getAllRecipients());
    +                smtpTrans.close();
    +            } else {
    +                throw new CloudRuntimeException("Unable to create smtp session.");
    +            }
    +        }
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    public static long getDifferenceDays(Date d1, Date d2) {
    +        long diff = d2.getTime() - d1.getTime();
    +        return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return success;
    +    }
    +
    +    public boolean enableAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        AccountVO acctForUpdate = _accountDao.createForUpdate();
    +        acctForUpdate.setState(State.enabled);
    +        acctForUpdate.setNeedsCleanup(false);
    +        success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951326
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    +            ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal onegbcost;
    +            BigDecimal rawusageingb;
    +            BigDecimal networkusage;
    +            onegbcost = tariff.getCurrencyValue();
    +            // s_logger.info("Quotatariff onegbcost=" + onegbcost);
    +            rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
    +            networkusage = rawusageingb.multiply(onegbcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), transferType, networkusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return success;
    +    }
    +
    +    public boolean enableAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        AccountVO acctForUpdate = _accountDao.createForUpdate();
    +        acctForUpdate.setState(State.enabled);
    +        acctForUpdate.setNeedsCleanup(false);
    +        success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Per previous comments, the immediate open and close of a transaction needs needs to be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37952248
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    +            ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal onegbcost;
    +            BigDecimal rawusageingb;
    +            BigDecimal networkusage;
    +            onegbcost = tariff.getCurrencyValue();
    +            // s_logger.info("Quotatariff onegbcost=" + onegbcost);
    +            rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
    +            networkusage = rawusageingb.multiply(onegbcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), transferType, networkusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return success;
    +    }
    +
    +    public boolean enableAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37946859
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDaoImpl.java ---
    @@ -0,0 +1,85 @@
    +// 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.Map;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +
    +import com.cloud.event.UsageEventVO;
    +import com.cloud.utils.db.DB;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +
    +@Component
    +@Local(value = { ServiceOfferingDao.class })
    +@DB()
    +public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Long> implements ServiceOfferingDao {
    +    protected static final Logger s_logger = Logger.getLogger(ServiceOfferingDaoImpl.class);
    +
    +    @Inject
    +    UserVmDetailsDao userVmDetailsDao;
    +
    +    @Override
    +    public ServiceOfferingVO findServiceOffering(final Long vmId, final long serviceOfferingId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
    +        ServiceOfferingVO result;
    +        try {
    +            result = findById(vmId, serviceOfferingId);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    public ServiceOfferingVO findById(Long vmId, long serviceOfferingId) {
    +        ServiceOfferingVO offering = super.findById(serviceOfferingId);
    +        if (offering.isDynamic()) {
    +            offering.setDynamicFlag(true);
    +            if (vmId == null) {
    +                throw new CloudRuntimeException("missing argument vmId");
    +            }
    --- End diff --
    
    By convention, parameter checks should be at the top of the method to fail fast.  The ``com.google.common.base.Preconditions#checkArgument`` method provides a concise, syntactic sugar for checking arguments.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37948942
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Per previous comments, the immediate opening and closing of a transaction should be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582759
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java ---
    @@ -0,0 +1,64 @@
    +//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 com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaEmailTemplatesDao.class })
    +public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase<QuotaEmailTemplatesVO, Long> implements QuotaEmailTemplatesDao {
    +
    +    protected SearchBuilder<QuotaEmailTemplatesVO> QuotaEmailTemplateSearch;
    +
    +    public QuotaEmailTemplatesDaoImpl() {
    +        super();
    +
    +        QuotaEmailTemplateSearch = createSearchBuilder();
    +        QuotaEmailTemplateSearch.and("template_name", QuotaEmailTemplateSearch.entity().getTemplateName(), SearchCriteria.Op.EQ);
    +        QuotaEmailTemplateSearch.done();
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    --- End diff --
    
    This transaction never appears to be closed.  Seems that a ``try { ... } finally { txn.close() }`` block is needed.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949885
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    --- End diff --
    
    Why aren't these attributes declared ``private``?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951112
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    --- End diff --
    
    Why are checked and unchecked exceptions being caught and ignored here?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950196
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    +                } else {
    +                    return qbrecords;
    +                }
    +            } else {
    +                throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    +            }
    +        }
    +
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        if (startDate.after(endDate)) {
    +            throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    +        }
    +        if (endDate.after(_respBldr.startOfNextDay())) {
    +            throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    --- End diff --
    
    It is a parameter error where dates provided are not in right order.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951314
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    +            ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal onegbcost;
    +            BigDecimal rawusageingb;
    +            BigDecimal networkusage;
    +            onegbcost = tariff.getCurrencyValue();
    +            // s_logger.info("Quotatariff onegbcost=" + onegbcost);
    +            rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
    +            networkusage = rawusageingb.multiply(onegbcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), transferType, networkusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return success;
    +    }
    +
    +    public boolean enableAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    Per previous comments, the immediate open and close of a transaction needs needs to be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605245
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLaterBalanceEntry(final long accountId, final long domainId, final Date afterThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.GT, afterThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @Override
    +    public void saveQuotaBalance(final List<QuotaBalanceVO> credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            for (QuotaBalanceVO credit : credits) {
    +                persist(credit);
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605394
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateUpdateCmd.java ---
    @@ -0,0 +1,111 @@
    +//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 com.cloud.user.Account;
    +import org.apache.cloudstack.api.APICommand;
    +import org.apache.cloudstack.api.ApiErrorCode;
    +import org.apache.cloudstack.api.BaseCmd;
    +import org.apache.cloudstack.api.Parameter;
    +import org.apache.cloudstack.api.ServerApiException;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.api.response.SuccessResponse;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.log4j.Logger;
    +
    +import javax.inject.Inject;
    +import java.util.Arrays;
    +
    +@APICommand(name = "quotaEmailTemplateUpdate", responseObject = SuccessResponse.class, description = "Updates existing email templates for quota alerts", since = "4.2.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
    --- End diff --
    
    accepted.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949858
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950577
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605276
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java ---
    @@ -0,0 +1,72 @@
    +//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.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaCreditsDao.class })
    +public class QuotaCreditsDaoImpl extends GenericDaoBase<QuotaCreditsVO, Long> implements QuotaCreditsDao {
    +    @Inject
    +    QuotaBalanceDao _quotaBalanceDao;
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaCreditsVO> findCredits(final long accountId, final long domainId, final Date startDate, final Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaCreditsVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
    +        } else {
    +            return new ArrayList<QuotaCreditsVO>();
    +        }
    +        List<QuotaCreditsVO> qc = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return qc;
    +    }
    +
    +    @Override
    +    public QuotaCreditsVO saveCredits(final QuotaCreditsVO credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        persist(credits);
    +        // make an entry in the balance table
    +        QuotaBalanceVO bal = new QuotaBalanceVO(credits);
    +        _quotaBalanceDao.persist(bal);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605389
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDaoImpl.java ---
    @@ -0,0 +1,85 @@
    +// 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.Map;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +
    +import com.cloud.event.UsageEventVO;
    +import com.cloud.utils.db.DB;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +
    +@Component
    +@Local(value = { ServiceOfferingDao.class })
    +@DB()
    +public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Long> implements ServiceOfferingDao {
    +    protected static final Logger s_logger = Logger.getLogger(ServiceOfferingDaoImpl.class);
    +
    +    @Inject
    +    UserVmDetailsDao userVmDetailsDao;
    +
    +    @Override
    +    public ServiceOfferingVO findServiceOffering(final Long vmId, final long serviceOfferingId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
    +        ServiceOfferingVO result;
    +        try {
    +            result = findById(vmId, serviceOfferingId);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    public ServiceOfferingVO findById(Long vmId, long serviceOfferingId) {
    +        ServiceOfferingVO offering = super.findById(serviceOfferingId);
    +        if (offering.isDynamic()) {
    +            offering.setDynamicFlag(true);
    +            if (vmId == null) {
    +                throw new CloudRuntimeException("missing argument vmId");
    +            }
    --- End diff --
    
    please elaborate.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37587187
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    --- End diff --
    
    Consider using ``com.google.common.base.Preconditions#checkArgument`` for a more expressive, concise, consistent way to validate parameters.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582401
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLaterBalanceEntry(final long accountId, final long domainId, final Date afterThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.GT, afterThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605339
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java ---
    @@ -0,0 +1,64 @@
    +//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 com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaEmailTemplatesDao.class })
    +public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase<QuotaEmailTemplatesVO, Long> implements QuotaEmailTemplatesDao {
    +
    +    protected SearchBuilder<QuotaEmailTemplatesVO> QuotaEmailTemplateSearch;
    +
    +    public QuotaEmailTemplatesDaoImpl() {
    +        super();
    +
    +        QuotaEmailTemplateSearch = createSearchBuilder();
    +        QuotaEmailTemplateSearch.and("template_name", QuotaEmailTemplateSearch.entity().getTemplateName(), SearchCriteria.Op.EQ);
    +        QuotaEmailTemplateSearch.done();
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        SearchCriteria<QuotaEmailTemplatesVO> sc = QuotaEmailTemplateSearch.create();
    +        if (templateName != null) {
    +            sc.setParameters("template_name", templateName);
    +        }
    +        List<QuotaEmailTemplatesVO> result = this.listBy(sc);
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplatesVO template) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        final boolean result = this.update(template.getId(), template);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Accpeted.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947561
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    --- End diff --
    
    ``qb`` is not passed a parameter.  Therefore, the use of ``InvalidParameterValueException`` does not seem appropriate.  Consider ``IllegalStateException``.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950329
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    +                } else {
    +                    return qbrecords;
    +                }
    +            } else {
    +                throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    +            }
    +        }
    +
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37572319
  
    --- Diff: engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java ---
    @@ -469,4 +478,25 @@ public void removeOldUsageRecords(int days) {
                 txn.close();
             }
         }
    +
    +    @SuppressWarnings("deprecation")
    +    public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        s_logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId);
    +        Filter usageFilter = new Filter(UsageVO.class, "startDate", true, 0L, 10000L);
    +        SearchCriteria<UsageVO> sc = 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.NULL);
    +        sc.addOr("quotaCalculated", SearchCriteria.Op.EQ, 0);
    +        s_logger.debug("Getting usage records" + usageFilter.getOrderBy());
    --- End diff --
    
    Debug logging statements should be wrapped in a ``if (s_logger.isDebugEnabled())`` check to avoid the overhead of string concatenation when debug logging is not enabled.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949186
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    +                } else {
    +                    return qbrecords;
    +                }
    +            } else {
    +                throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    +            }
    +        }
    +
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        if (startDate.after(endDate)) {
    +            throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    +        }
    +        if (endDate.after(_respBldr.startOfNextDay())) {
    +            throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    --- End diff --
    
    This error does not seem to pertain to a parameter value. As such, the type should be changed to something more appropriate such as ``IllegalStateException``.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949568
  
    --- Diff: server/src/com/cloud/api/dispatch/ParamProcessWorker.java ---
    @@ -294,6 +294,14 @@ private void setFieldValue(final Field field, final BaseCmd cmdObj, final Object
                         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())) {
    --- End diff --
    
    Consider using ``com.google.common.base.Strings#isNullorEmpty`` method which consolidates the null and blank checks.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37572286
  
    --- Diff: engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java ---
    @@ -469,4 +478,25 @@ public void removeOldUsageRecords(int days) {
                 txn.close();
             }
         }
    +
    +    @SuppressWarnings("deprecation")
    +    public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        s_logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId);
    +        Filter usageFilter = new Filter(UsageVO.class, "startDate", true, 0L, 10000L);
    +        SearchCriteria<UsageVO> sc = 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.NULL);
    +        sc.addOr("quotaCalculated", SearchCriteria.Op.EQ, 0);
    +        s_logger.debug("Getting usage records" + usageFilter.getOrderBy());
    +        Pair<List<UsageVO>, Integer> usageRecords = searchAndCountAllRecords(sc, usageFilter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Why do we immediately open and close a transaction here?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37573045
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java ---
    @@ -0,0 +1,92 @@
    +// 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.constant;
    +
    +import org.apache.cloudstack.usage.UsageTypes;
    +
    +import java.util.HashMap;
    +
    +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;
    +
    +    private Integer quotaType;
    +    private String quotaName;
    +    private String quotaUnit;
    +    private String description;
    +    private String discriminator;
    +
    +    public QuotaTypes(Integer quotaType, String name, String unit, String description) {
    +        this.quotaType = quotaType;
    +        this.description = description;
    +        this.quotaName = name;
    +        this.quotaUnit = unit;
    +        this.discriminator = "None";
    +    }
    +
    +    public static HashMap<Integer, QuotaTypes> listQuotaTypes() {
    +        final HashMap<Integer, QuotaTypes> quotaTypeList = new HashMap<Integer, QuotaTypes>();
    +        quotaTypeList.put(new Integer(RUNNING_VM), new QuotaTypes(new Integer(RUNNING_VM), "RUNNING_VM", "Compute-Month", "Running Vm Usage"));
    --- End diff --
    
    ``new Integer(...)`` bypasses the constant pool -- increasing memory and negatively impacting performance.  Use ``Integer.valueOf()`` instead.  
    
    In order to avoid boilerplate, why not declare the constant values as Integer rather than int?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605569
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    --- End diff --
    
    Please, elaborate.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37716584
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    --- End diff --
    
    The program logic does not require a secondary order on equal values.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37573761
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java ---
    @@ -0,0 +1,92 @@
    +// 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.constant;
    +
    +import org.apache.cloudstack.usage.UsageTypes;
    +
    +import java.util.HashMap;
    +
    +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;
    +
    +    private Integer quotaType;
    +    private String quotaName;
    +    private String quotaUnit;
    +    private String description;
    +    private String discriminator;
    +
    +    public QuotaTypes(Integer quotaType, String name, String unit, String description) {
    +        this.quotaType = quotaType;
    +        this.description = description;
    +        this.quotaName = name;
    +        this.quotaUnit = unit;
    +        this.discriminator = "None";
    +    }
    +
    +    public static HashMap<Integer, QuotaTypes> listQuotaTypes() {
    --- End diff --
    
    Why isn't the quotaTypes map initialized as a static constant? Each time ``getDescription()`` method is called these values are unnecessarily reallocated. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37948934
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    Per previous comments, the immediate opening and closing of a transaction should be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951078
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    --- End diff --
    
    Printing the stack trace goes to console -- incurring overhead and likely being ignored.  It should be removed.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37948706
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    +                    s_logger.error(String.format("Unable to unlock account %s after getting enough quota credits", account.getAccountName()));
    +                }
    +            }
    +        }
    +
    +        String creditor = String.valueOf(Account.ACCOUNT_ID_SYSTEM);
    +        User creditorUser = _userDao.getUser(updatedBy);
    +        if (creditorUser != null) {
    +            creditor = creditorUser.getUsername();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        QuotaCreditsResponse response = new QuotaCreditsResponse(result, creditor);
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    private QuotaEmailTemplateResponse createQuotaEmailResponse(QuotaEmailTemplatesVO template) {
    +        QuotaEmailTemplateResponse response = new QuotaEmailTemplateResponse();
    +        response.setTemplateType(template.getTemplateName());
    +        response.setTemplateSubject(template.getTemplateSubject());
    +        response.setTemplateText(template.getTemplateBody());
    +        response.setLocale(template.getLocale());
    +        response.setLastUpdatedOn(template.getLastUpdated());
    +        return response;
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplateResponse> listQuotaEmailTemplates(QuotaEmailTemplateListCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        final List<QuotaEmailTemplateResponse> responses = new ArrayList<QuotaEmailTemplateResponse>();
    +        for (final QuotaEmailTemplatesVO template : templates) {
    +            responses.add(createQuotaEmailResponse(template));
    +        }
    +        return responses;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplateUpdateCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        final String templateSubject = StringEscapeUtils.escapeJavaScript(cmd.getTemplateSubject());
    +        final String templateBody = StringEscapeUtils.escapeJavaScript(cmd.getTemplateBody());
    +        final String locale = cmd.getLocale();
    +
    +        final List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        if (templates.size() == 1) {
    +            final QuotaEmailTemplatesVO template = templates.get(0);
    +            template.setTemplateSubject(templateSubject);
    +            template.setTemplateBody(templateBody);
    +            if (locale != null) {
    +                template.setLocale(locale);
    +            }
    +            return _quotaEmailTemplateDao.updateQuotaEmailTemplate(template);
    +        }
    +        return false;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("There are no balance entries on or before the requested date.");
    +        }
    +        if (startDate == null) {
    +            startDate = new Date();
    +        }
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaLastBalanceResponse Date=" + entry.getUpdatedOn() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            lastCredits = lastCredits.add(entry.getCreditBalance());
    +        }
    +        resp.setStartQuota(lastCredits);
    +        resp.setStartDate(_quotaService.computeAdjustedTime(startDate));
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(QuotaStatementCmd cmd) {
    +        return _quotaService.getQuotaUsage(cmd.getAccountId(), cmd.getAccountName(), cmd.getDomainId(), cmd.getUsageType(), cmd.getStartDate(), cmd.getEndDate());
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> getQuotaBalance(QuotaBalanceCmd cmd) {
    +        return _quotaService.findQuotaBalanceVO(cmd.getAccountId(), cmd.getAccountName(), cmd.getDomainId(), cmd.getStartDate(), cmd.getEndDate());
    +    }
    +
    +    @Override
    +    public Date startOfNextDay(Date dt) {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(dt);
    +        c.add(Calendar.DATE, 1);
    +        dt = c.getTime();
    +        return dt;
    +    }
    +
    +    @Override
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    --- End diff --
    
    Consider using the utility method mentioned in my previous comment,


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947294
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    --- End diff --
    
    Add the exception to the logging statement to surface the stack trace for debugging.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949195
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    +                } else {
    +                    return qbrecords;
    +                }
    +            } else {
    +                throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    +            }
    +        }
    +
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        if (startDate.after(endDate)) {
    +            throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    --- End diff --
    
    This error does not seem to pertain to a parameter value. As such, the type should be changed to something more appropriate such as ``IllegalStateException``.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949107
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    +                } else {
    +                    return qbrecords;
    +                }
    +            } else {
    +                throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    +            }
    +        }
    +
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Per previous comments, the immediate opening and closing of a transaction should be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950425
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    --- End diff --
    
    InvalidParameterValueException exception indicates to the user that there is something wrong with the parameters here it indicates issues with date values passed/


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605573
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949030
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    --- End diff --
    
    Consider using the ``isListEmpty`` utility method discussed earlier to avoid a potential NPE.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r38060134
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    --- End diff --
    
    it is not same


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-134875791
  
    The Transaction open and close causing churn in database connection is based on the notion that these methods somehow open and close db connections.
    As far as I see from code they do not deal in DB connection and are only used to initialise the database structures when a DB switch needs to be undertaken or a new transaction context needs to be created.
    The suggestion to change this model and the pros and cons should be discussed on mailing list and is clearly separate work than the Usage/Quota Service.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949049
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    --- End diff --
    
    This error does not seem to pertain to a parameter value. As such, the type should be changed to something more appropriate such as IllegalStateException.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950417
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    +            _smtpHost = smtpHost;
    +            _smtpPort = smtpPort;
    +            _smtpUseAuth = smtpUseAuth;
    +            _smtpUsername = smtpUsername;
    +            _smtpPassword = smtpPassword;
    +            _emailSender = emailSender;
    +
    +            if (_smtpHost != null) {
    +                Properties smtpProps = new Properties();
    +                smtpProps.put("mail.smtp.host", smtpHost);
    +                smtpProps.put("mail.smtp.port", smtpPort);
    +                smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtp.user", smtpUsername);
    +                }
    +
    +                smtpProps.put("mail.smtps.host", smtpHost);
    +                smtpProps.put("mail.smtps.port", smtpPort);
    +                smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtps.user", smtpUsername);
    +                }
    +
    +                if ((smtpUsername != null) && (smtpPassword != null)) {
    +                    _smtpSession = Session.getInstance(smtpProps, new Authenticator() {
    +                        @Override
    +                        protected PasswordAuthentication getPasswordAuthentication() {
    +                            return new PasswordAuthentication(smtpUsername, smtpPassword);
    +                        }
    +                    });
    +                } else {
    +                    _smtpSession = Session.getInstance(smtpProps);
    +                }
    +                _smtpSession.setDebug(smtpDebug);
    +            } else {
    +                _smtpSession = null;
    +            }
    +        }
    +
    +        public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException {
    +            if (_smtpSession != null) {
    --- End diff --
    
    Consider simplifying this method by placing the following at the top of the method to simplify the flow and remove an unnecessary ``else`` block:
    ```
    if (_smtpSession == null) {
        throw new RuntimeException("Unable to create smtp session");
    }
    
    // The rest of of the method minus the else block
    ```


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37583163
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java ---
    @@ -0,0 +1,138 @@
    +//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 com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaTariffDao.class })
    +public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> implements QuotaTariffDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaTariffDaoImpl.class.getName());
    +
    +    private final SearchBuilder<QuotaTariffVO> searchUsageType;
    +    private final SearchBuilder<QuotaTariffVO> listAllIncludedUsageType;
    +
    +    public QuotaTariffDaoImpl() {
    +        super();
    +        searchUsageType = createSearchBuilder();
    +        searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        searchUsageType.done();
    +
    +        listAllIncludedUsageType = createSearchBuilder();
    +        listAllIncludedUsageType.and("onorbefore", listAllIncludedUsageType.entity().getEffectiveOn(), SearchCriteria.Op.LTEQ);
    +        listAllIncludedUsageType.and("quotatype", listAllIncludedUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        listAllIncludedUsageType.done();
    +    }
    +
    +    @Override
    +    public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            sc.setParameters("quotatype", quotaType);
    +            result = search(sc, filter);
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        if (result.size() > 0) {
    +            //s_logger.info("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType  + " val=" + result.get(0).getCurrencyValue());
    +            return result.get(0);
    +        } else {
    +            //s_logger.info("Missing quota type " + quotaType);
    +            return null;
    +        }
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listAllTariffPlans(final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> tariffs = new ArrayList<QuotaTariffVO>();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            for (Integer quotaType : QuotaTypes.listQuotaTypes().keySet()) {
    +                sc.setParameters("quotatype", quotaType);
    +                List<QuotaTariffVO> result = search(sc, filter);
    +                if (result.size() > 0) {
    +                    tariffs.add(result.get(0));
    +                    s_logger.info("listAllTariffPlans onorbefore" + effectiveDate +  "quota type " + result.get(0).getDescription() + " , effective Date=" + result.get(0).getEffectiveOn() + " val=" + result.get(0).getCurrencyValue());
    +                }
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding opening/closing transactions on the same line.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37584764
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDaoImpl.java ---
    @@ -0,0 +1,85 @@
    +// 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.Map;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +
    +import com.cloud.event.UsageEventVO;
    +import com.cloud.utils.db.DB;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +
    +@Component
    +@Local(value = { ServiceOfferingDao.class })
    +@DB()
    +public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Long> implements ServiceOfferingDao {
    +    protected static final Logger s_logger = Logger.getLogger(ServiceOfferingDaoImpl.class);
    +
    +    @Inject
    +    UserVmDetailsDao userVmDetailsDao;
    +
    +    @Override
    +    public ServiceOfferingVO findServiceOffering(final Long vmId, final long serviceOfferingId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
    +        ServiceOfferingVO result;
    +        try {
    +            result = findById(vmId, serviceOfferingId);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    public ServiceOfferingVO findById(Long vmId, long serviceOfferingId) {
    +        ServiceOfferingVO offering = super.findById(serviceOfferingId);
    +        if (offering.isDynamic()) {
    +            offering.setDynamicFlag(true);
    +            if (vmId == null) {
    +                throw new CloudRuntimeException("missing argument vmId");
    +            }
    --- End diff --
    
    Move to the top of the method and consider using ``com.google.common.base.Preconditions#checkArgument`` to check the argument.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950915
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605251
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949748
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37948373
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    +                    s_logger.error(String.format("Unable to unlock account %s after getting enough quota credits", account.getAccountName()));
    +                }
    +            }
    +        }
    +
    +        String creditor = String.valueOf(Account.ACCOUNT_ID_SYSTEM);
    +        User creditorUser = _userDao.getUser(updatedBy);
    +        if (creditorUser != null) {
    +            creditor = creditorUser.getUsername();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        QuotaCreditsResponse response = new QuotaCreditsResponse(result, creditor);
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    private QuotaEmailTemplateResponse createQuotaEmailResponse(QuotaEmailTemplatesVO template) {
    +        QuotaEmailTemplateResponse response = new QuotaEmailTemplateResponse();
    +        response.setTemplateType(template.getTemplateName());
    +        response.setTemplateSubject(template.getTemplateSubject());
    +        response.setTemplateText(template.getTemplateBody());
    +        response.setLocale(template.getLocale());
    +        response.setLastUpdatedOn(template.getLastUpdated());
    +        return response;
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplateResponse> listQuotaEmailTemplates(QuotaEmailTemplateListCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        final List<QuotaEmailTemplateResponse> responses = new ArrayList<QuotaEmailTemplateResponse>();
    +        for (final QuotaEmailTemplatesVO template : templates) {
    +            responses.add(createQuotaEmailResponse(template));
    +        }
    +        return responses;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplateUpdateCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        final String templateSubject = StringEscapeUtils.escapeJavaScript(cmd.getTemplateSubject());
    +        final String templateBody = StringEscapeUtils.escapeJavaScript(cmd.getTemplateBody());
    +        final String locale = cmd.getLocale();
    +
    +        final List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        if (templates.size() == 1) {
    +            final QuotaEmailTemplatesVO template = templates.get(0);
    +            template.setTemplateSubject(templateSubject);
    +            template.setTemplateBody(templateBody);
    +            if (locale != null) {
    +                template.setLocale(locale);
    +            }
    +            return _quotaEmailTemplateDao.updateQuotaEmailTemplate(template);
    +        }
    +        return false;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate) {
    +        if (quotaBalance.size() == 0) {
    --- End diff --
    
    Check that quotaBalance is not ``null`` before invoking the ``size`` method.  Consider extracting to a utility method such as the following:
    
    ```
    public boolean isListEmpty(final List list) {
        return list != null : list.isEmpty() : false;
    }
    ```
    
    Also, add a few unit tests to verify the behavior.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950677
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    --- End diff --
    
    ``QuotaTypes.RUNNING_VM`` and ``QuotaTypes.ALLOCATED_VM`` appear to be the same.  Why not consolidate them?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605243
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLaterBalanceEntry(final long accountId, final long domainId, final Date afterThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.GT, afterThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @Override
    +    public void saveQuotaBalance(final List<QuotaBalanceVO> credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            for (QuotaBalanceVO credit : credits) {
    +                persist(credit);
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaBalanceVO> findCreditBalance(final long accountId, final long domainId, final Date lastbalancedate, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.GT, 0);
    +        if ((lastbalancedate != null) && (beforeThis != null) && lastbalancedate.before(beforeThis)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, lastbalancedate, beforeThis);
    +        } else {
    +            return new ArrayList<QuotaBalanceVO>();
    +        }
    +        List<QuotaBalanceVO> qb = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950957
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_SENT));
    --- End diff --
    
    Could the ``QuotaTypes.SNAPSHOT```, ``QuotaTypes.TEMPLATE``, ``QuotaTypes.ISO``, ``QuotaTypes.VOLUME``, ``QuotaTypes.VM_SNAPSHOT``, ``QuotaTypes.LOAD_BALANCER_POLICY``, ``QuotaTypes.PORT_FORWARDING_RULE``, ``QuotaTypes.IP_ADDRESS``, ``QuotaTypes.NETWORK_OFFERING``,  ``QuotaTypes.SECURITY_GROUP``, ``QuotaTypes.VPN_USERS``, ``QuotaTypes.NETWORK_BYTES_RECEIVED``, ``QuotaTypes.NETWORK_BYTES_SENT`` cases be consolidated into the following:
    
    ```
    quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, usageRecord.getUsageType()));
    break;
    ```



---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949824
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    --- End diff --
    
    Why not use a ``List`` rather than a ``String`` and use ``com.google.command.base.Joiner`` to create a comma-separated string when needed?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by DaanHoogland <gi...@git.apache.org>.
Github user DaanHoogland commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37616523
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java ---
    @@ -0,0 +1,64 @@
    +//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 com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaEmailTemplatesDao.class })
    +public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase<QuotaEmailTemplatesVO, Long> implements QuotaEmailTemplatesDao {
    +
    +    protected SearchBuilder<QuotaEmailTemplatesVO> QuotaEmailTemplateSearch;
    +
    +    public QuotaEmailTemplatesDaoImpl() {
    +        super();
    +
    +        QuotaEmailTemplateSearch = createSearchBuilder();
    +        QuotaEmailTemplateSearch.and("template_name", QuotaEmailTemplateSearch.entity().getTemplateName(), SearchCriteria.Op.EQ);
    +        QuotaEmailTemplateSearch.done();
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        SearchCriteria<QuotaEmailTemplatesVO> sc = QuotaEmailTemplateSearch.create();
    +        if (templateName != null) {
    +            sc.setParameters("template_name", templateName);
    +        }
    +        List<QuotaEmailTemplatesVO> result = this.listBy(sc);
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplatesVO template) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        final boolean result = this.update(template.getId(), template);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    t-w-r


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by bhaisaab <gi...@git.apache.org>.
Github user bhaisaab commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r38385699
  
    --- Diff: engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java ---
    @@ -469,4 +478,25 @@ public void removeOldUsageRecords(int days) {
                 txn.close();
             }
         }
    +
    +    @SuppressWarnings("deprecation")
    +    public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    @jburwell the dao uses connection multiplexing; so if we don't switch back to the previous DB several queries will fail as they will be execute on USAGE_DB instead of CLOUD_DB. This pattern is widely used across the codebase.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947194
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Per comments above, the immediate open and close of a database connection needs to be replaced with the newer transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950538
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    +            _smtpHost = smtpHost;
    +            _smtpPort = smtpPort;
    +            _smtpUseAuth = smtpUseAuth;
    +            _smtpUsername = smtpUsername;
    +            _smtpPassword = smtpPassword;
    +            _emailSender = emailSender;
    +
    +            if (_smtpHost != null) {
    +                Properties smtpProps = new Properties();
    +                smtpProps.put("mail.smtp.host", smtpHost);
    +                smtpProps.put("mail.smtp.port", smtpPort);
    +                smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtp.user", smtpUsername);
    +                }
    +
    +                smtpProps.put("mail.smtps.host", smtpHost);
    +                smtpProps.put("mail.smtps.port", smtpPort);
    +                smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtps.user", smtpUsername);
    +                }
    +
    +                if ((smtpUsername != null) && (smtpPassword != null)) {
    +                    _smtpSession = Session.getInstance(smtpProps, new Authenticator() {
    +                        @Override
    +                        protected PasswordAuthentication getPasswordAuthentication() {
    +                            return new PasswordAuthentication(smtpUsername, smtpPassword);
    +                        }
    +                    });
    +                } else {
    +                    _smtpSession = Session.getInstance(smtpProps);
    +                }
    +                _smtpSession.setDebug(smtpDebug);
    +            } else {
    +                _smtpSession = null;
    +            }
    +        }
    +
    +        public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException {
    +            if (_smtpSession != null) {
    +                SMTPMessage msg = new SMTPMessage(_smtpSession);
    +                msg.setSender(new InternetAddress(_emailSender, _emailSender));
    +                msg.setFrom(new InternetAddress(_emailSender, _emailSender));
    +
    +                for (String email : emails) {
    +                    if (email != null && !email.isEmpty()) {
    +                        try {
    +                            InternetAddress address = new InternetAddress(email, email);
    +                            msg.addRecipient(Message.RecipientType.TO, address);
    +                        } catch (Exception pokemon) {
    +                            s_logger.error("Exception in creating address for:" + email, pokemon);
    +                        }
    +                    }
    +                }
    +
    +                msg.setSubject(subject);
    +                msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
    +                msg.setContent(body, "text/html; charset=utf-8");
    +                msg.saveChanges();
    +
    +                SMTPTransport smtpTrans = null;
    +                if (_smtpUseAuth) {
    +                    smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                } else {
    +                    smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                }
    +                smtpTrans.connect();
    +                smtpTrans.sendMessage(msg, msg.getAllRecipients());
    +                smtpTrans.close();
    +            } else {
    +                throw new CloudRuntimeException("Unable to create smtp session.");
    +            }
    +        }
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    public static long getDifferenceDays(Date d1, Date d2) {
    +        long diff = d2.getTime() - d1.getTime();
    +        return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return success;
    +    }
    +
    +    public boolean enableAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        AccountVO acctForUpdate = _accountDao.createForUpdate();
    +        acctForUpdate.setState(State.enabled);
    +        acctForUpdate.setNeedsCleanup(false);
    +        success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Per previous comments, the immediate open and close of a transaction needs needs to be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949700
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    --- End diff --
    
    By coding convention, all if blocks should be wrapped in curly braces.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37948835
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    +                    s_logger.error(String.format("Unable to unlock account %s after getting enough quota credits", account.getAccountName()));
    +                }
    +            }
    +        }
    +
    +        String creditor = String.valueOf(Account.ACCOUNT_ID_SYSTEM);
    +        User creditorUser = _userDao.getUser(updatedBy);
    +        if (creditorUser != null) {
    +            creditor = creditorUser.getUsername();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        QuotaCreditsResponse response = new QuotaCreditsResponse(result, creditor);
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    private QuotaEmailTemplateResponse createQuotaEmailResponse(QuotaEmailTemplatesVO template) {
    +        QuotaEmailTemplateResponse response = new QuotaEmailTemplateResponse();
    +        response.setTemplateType(template.getTemplateName());
    +        response.setTemplateSubject(template.getTemplateSubject());
    +        response.setTemplateText(template.getTemplateBody());
    +        response.setLocale(template.getLocale());
    +        response.setLastUpdatedOn(template.getLastUpdated());
    +        return response;
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplateResponse> listQuotaEmailTemplates(QuotaEmailTemplateListCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        final List<QuotaEmailTemplateResponse> responses = new ArrayList<QuotaEmailTemplateResponse>();
    +        for (final QuotaEmailTemplatesVO template : templates) {
    +            responses.add(createQuotaEmailResponse(template));
    +        }
    +        return responses;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplateUpdateCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        final String templateSubject = StringEscapeUtils.escapeJavaScript(cmd.getTemplateSubject());
    +        final String templateBody = StringEscapeUtils.escapeJavaScript(cmd.getTemplateBody());
    +        final String locale = cmd.getLocale();
    +
    +        final List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        if (templates.size() == 1) {
    +            final QuotaEmailTemplatesVO template = templates.get(0);
    +            template.setTemplateSubject(templateSubject);
    +            template.setTemplateBody(templateBody);
    +            if (locale != null) {
    +                template.setLocale(locale);
    +            }
    +            return _quotaEmailTemplateDao.updateQuotaEmailTemplate(template);
    +        }
    +        return false;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("There are no balance entries on or before the requested date.");
    +        }
    +        if (startDate == null) {
    +            startDate = new Date();
    +        }
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaLastBalanceResponse Date=" + entry.getUpdatedOn() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    --- End diff --
    
    An INFO level log message should contextual information for a system administrator.  This messages lacks that context.  It should either be logged at DEBUG or context information should be added to it.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-133407337
  
    @DaanHoogland in addition to failing to convey the intention of the action, the open then close pattern is incredibly expensive due to rapid, unnecessary churn on the connection pool.  Please see my response above for my thoughts on how we can improve it and avoid the nasty side effects the ``TransactionLegacy`` imposes.  


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605471
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    --- End diff --
    
    No required. Equal values can stay in place. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950512
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    +            _smtpHost = smtpHost;
    +            _smtpPort = smtpPort;
    +            _smtpUseAuth = smtpUseAuth;
    +            _smtpUsername = smtpUsername;
    +            _smtpPassword = smtpPassword;
    +            _emailSender = emailSender;
    +
    +            if (_smtpHost != null) {
    +                Properties smtpProps = new Properties();
    +                smtpProps.put("mail.smtp.host", smtpHost);
    +                smtpProps.put("mail.smtp.port", smtpPort);
    +                smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtp.user", smtpUsername);
    +                }
    +
    +                smtpProps.put("mail.smtps.host", smtpHost);
    +                smtpProps.put("mail.smtps.port", smtpPort);
    +                smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtps.user", smtpUsername);
    +                }
    +
    +                if ((smtpUsername != null) && (smtpPassword != null)) {
    +                    _smtpSession = Session.getInstance(smtpProps, new Authenticator() {
    +                        @Override
    +                        protected PasswordAuthentication getPasswordAuthentication() {
    +                            return new PasswordAuthentication(smtpUsername, smtpPassword);
    +                        }
    +                    });
    +                } else {
    +                    _smtpSession = Session.getInstance(smtpProps);
    +                }
    +                _smtpSession.setDebug(smtpDebug);
    +            } else {
    +                _smtpSession = null;
    +            }
    +        }
    +
    +        public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException {
    +            if (_smtpSession != null) {
    +                SMTPMessage msg = new SMTPMessage(_smtpSession);
    +                msg.setSender(new InternetAddress(_emailSender, _emailSender));
    +                msg.setFrom(new InternetAddress(_emailSender, _emailSender));
    +
    +                for (String email : emails) {
    +                    if (email != null && !email.isEmpty()) {
    +                        try {
    +                            InternetAddress address = new InternetAddress(email, email);
    +                            msg.addRecipient(Message.RecipientType.TO, address);
    +                        } catch (Exception pokemon) {
    +                            s_logger.error("Exception in creating address for:" + email, pokemon);
    +                        }
    +                    }
    +                }
    +
    +                msg.setSubject(subject);
    +                msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
    +                msg.setContent(body, "text/html; charset=utf-8");
    +                msg.saveChanges();
    +
    +                SMTPTransport smtpTrans = null;
    +                if (_smtpUseAuth) {
    +                    smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                } else {
    +                    smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                }
    +                smtpTrans.connect();
    +                smtpTrans.sendMessage(msg, msg.getAllRecipients());
    +                smtpTrans.close();
    +            } else {
    +                throw new CloudRuntimeException("Unable to create smtp session.");
    +            }
    +        }
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    public static long getDifferenceDays(Date d1, Date d2) {
    +        long diff = d2.getTime() - d1.getTime();
    +        return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Per previous comments, the immediate open and close of a transaction needs needs to be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605454
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    --- End diff --
    
    Find bug can find out if the Null can reach that method, if not no use. Otherwise we will end up checking each parameter for null.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947073
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilder.java ---
    @@ -0,0 +1,62 @@
    +//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.response;
    +
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +
    +import java.util.Date;
    +import java.util.List;
    +
    +public interface QuotaResponseBuilder {
    +
    +    QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd);
    +
    +    List<QuotaTariffVO> listQuotaTariffPlans(QuotaTariffListCmd cmd);
    +
    +    QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO configuration);
    +
    +    QuotaStatementResponse createQuotaStatementResponse(List<QuotaUsageVO> quotaUsage);
    +
    +    QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaUsage, Date startDate, Date endDate);
    +
    +    QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate);
    +
    +    QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy, Date despositedOn);
    +
    +    public List<QuotaUsageVO> getQuotaUsage(QuotaStatementCmd cmd);
    --- End diff --
    
    The accessor modifier is unnecessary on an interface definition.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37580929
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDao.java ---
    @@ -0,0 +1,43 @@
    +//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.math.BigDecimal;
    +import java.util.Date;
    +import java.util.List;
    +
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +
    +import com.cloud.utils.db.GenericDao;
    +
    +public interface QuotaBalanceDao extends GenericDao<QuotaBalanceVO, Long> {
    +
    +    void saveQuotaBalance(List<QuotaBalanceVO> credits);
    +
    +    List<QuotaBalanceVO> findCreditBalance(long accountId, long domainId, Date startDate, Date endDate);
    --- End diff --
    
    Why are some ``accountId``s declared as ``long`` and some as ``Long``?  It seems that the type should be consistent across all method declarations.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950573
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950745
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    +                    s_logger.error(String.format("Unable to unlock account %s after getting enough quota credits", account.getAccountName()));
    +                }
    +            }
    +        }
    +
    +        String creditor = String.valueOf(Account.ACCOUNT_ID_SYSTEM);
    +        User creditorUser = _userDao.getUser(updatedBy);
    +        if (creditorUser != null) {
    +            creditor = creditorUser.getUsername();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        QuotaCreditsResponse response = new QuotaCreditsResponse(result, creditor);
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    private QuotaEmailTemplateResponse createQuotaEmailResponse(QuotaEmailTemplatesVO template) {
    +        QuotaEmailTemplateResponse response = new QuotaEmailTemplateResponse();
    +        response.setTemplateType(template.getTemplateName());
    +        response.setTemplateSubject(template.getTemplateSubject());
    +        response.setTemplateText(template.getTemplateBody());
    +        response.setLocale(template.getLocale());
    +        response.setLastUpdatedOn(template.getLastUpdated());
    +        return response;
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplateResponse> listQuotaEmailTemplates(QuotaEmailTemplateListCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        final List<QuotaEmailTemplateResponse> responses = new ArrayList<QuotaEmailTemplateResponse>();
    +        for (final QuotaEmailTemplatesVO template : templates) {
    +            responses.add(createQuotaEmailResponse(template));
    +        }
    +        return responses;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplateUpdateCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        final String templateSubject = StringEscapeUtils.escapeJavaScript(cmd.getTemplateSubject());
    +        final String templateBody = StringEscapeUtils.escapeJavaScript(cmd.getTemplateBody());
    +        final String locale = cmd.getLocale();
    +
    +        final List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        if (templates.size() == 1) {
    +            final QuotaEmailTemplatesVO template = templates.get(0);
    +            template.setTemplateSubject(templateSubject);
    +            template.setTemplateBody(templateBody);
    +            if (locale != null) {
    +                template.setLocale(locale);
    +            }
    +            return _quotaEmailTemplateDao.updateQuotaEmailTemplate(template);
    +        }
    +        return false;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("There are no balance entries on or before the requested date.");
    +        }
    +        if (startDate == null) {
    +            startDate = new Date();
    +        }
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaLastBalanceResponse Date=" + entry.getUpdatedOn() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            lastCredits = lastCredits.add(entry.getCreditBalance());
    +        }
    +        resp.setStartQuota(lastCredits);
    +        resp.setStartDate(_quotaService.computeAdjustedTime(startDate));
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(QuotaStatementCmd cmd) {
    +        return _quotaService.getQuotaUsage(cmd.getAccountId(), cmd.getAccountName(), cmd.getDomainId(), cmd.getUsageType(), cmd.getStartDate(), cmd.getEndDate());
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> getQuotaBalance(QuotaBalanceCmd cmd) {
    +        return _quotaService.findQuotaBalanceVO(cmd.getAccountId(), cmd.getAccountName(), cmd.getDomainId(), cmd.getStartDate(), cmd.getEndDate());
    +    }
    +
    +    @Override
    +    public Date startOfNextDay(Date dt) {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(dt);
    +        c.add(Calendar.DATE, 1);
    +        dt = c.getTime();
    +        return dt;
    +    }
    +
    +    @Override
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    --- End diff --
    
    This is to be consistent with UsageServer. Yes can be used, but this does not screw up with time anyways.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582431
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605166
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java ---
    @@ -0,0 +1,92 @@
    +// 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.constant;
    +
    +import org.apache.cloudstack.usage.UsageTypes;
    +
    +import java.util.HashMap;
    +
    +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;
    +
    +    private Integer quotaType;
    +    private String quotaName;
    +    private String quotaUnit;
    +    private String description;
    +    private String discriminator;
    +
    +    public QuotaTypes(Integer quotaType, String name, String unit, String description) {
    +        this.quotaType = quotaType;
    +        this.description = description;
    +        this.quotaName = name;
    +        this.quotaUnit = unit;
    +        this.discriminator = "None";
    +    }
    +
    +    public static HashMap<Integer, QuotaTypes> listQuotaTypes() {
    --- End diff --
    
    Yes, should be declared static.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605358
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java ---
    @@ -0,0 +1,138 @@
    +//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 com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaTariffDao.class })
    +public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> implements QuotaTariffDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaTariffDaoImpl.class.getName());
    +
    +    private final SearchBuilder<QuotaTariffVO> searchUsageType;
    +    private final SearchBuilder<QuotaTariffVO> listAllIncludedUsageType;
    +
    +    public QuotaTariffDaoImpl() {
    +        super();
    +        searchUsageType = createSearchBuilder();
    +        searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        searchUsageType.done();
    +
    +        listAllIncludedUsageType = createSearchBuilder();
    +        listAllIncludedUsageType.and("onorbefore", listAllIncludedUsageType.entity().getEffectiveOn(), SearchCriteria.Op.LTEQ);
    +        listAllIncludedUsageType.and("quotatype", listAllIncludedUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        listAllIncludedUsageType.done();
    +    }
    +
    +    @Override
    +    public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            sc.setParameters("quotatype", quotaType);
    +            result = search(sc, filter);
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        if (result.size() > 0) {
    +            //s_logger.info("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType  + " val=" + result.get(0).getCurrencyValue());
    +            return result.get(0);
    +        } else {
    +            //s_logger.info("Missing quota type " + quotaType);
    +            return null;
    +        }
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listAllTariffPlans(final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> tariffs = new ArrayList<QuotaTariffVO>();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            for (Integer quotaType : QuotaTypes.listQuotaTypes().keySet()) {
    +                sc.setParameters("quotatype", quotaType);
    +                List<QuotaTariffVO> result = search(sc, filter);
    +                if (result.size() > 0) {
    +                    tariffs.add(result.get(0));
    +                    s_logger.info("listAllTariffPlans onorbefore" + effectiveDate +  "quota type " + result.get(0).getDescription() + " , effective Date=" + result.get(0).getEffectiveOn() + " val=" + result.get(0).getCurrencyValue());
    +                }
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37572081
  
    --- Diff: engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java ---
    @@ -469,4 +478,25 @@ public void removeOldUsageRecords(int days) {
                 txn.close();
             }
         }
    +
    +    @SuppressWarnings("deprecation")
    +    public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    Why do we immediately open and close a transaction here?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by DaanHoogland <gi...@git.apache.org>.
Github user DaanHoogland commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37616481
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java ---
    @@ -0,0 +1,72 @@
    +//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.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaCreditsDao.class })
    +public class QuotaCreditsDaoImpl extends GenericDaoBase<QuotaCreditsVO, Long> implements QuotaCreditsDao {
    +    @Inject
    +    QuotaBalanceDao _quotaBalanceDao;
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaCreditsVO> findCredits(final long accountId, final long domainId, final Date startDate, final Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaCreditsVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
    +        } else {
    +            return new ArrayList<QuotaCreditsVO>();
    +        }
    +        List<QuotaCreditsVO> qc = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return qc;
    +    }
    +
    +    @Override
    +    public QuotaCreditsVO saveCredits(final QuotaCreditsVO credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    --- End diff --
    
    please do not close in finally but have the compiler do it for you.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582453
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java ---
    @@ -0,0 +1,73 @@
    +//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.List;
    +
    +import javax.ejb.Local;
    +
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.springframework.stereotype.Component;
    +
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaAccountDao.class })
    +public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> implements QuotaAccountDao {
    +
    +    @Override
    +    public List<QuotaAccountVO> listAll() {
    +        List<QuotaAccountVO> result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.listAll();
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaAccountVO findById(Long id) {
    +        QuotaAccountVO result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.findById(id);
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaAccountVO persist(QuotaAccountVO entity) {
    +        QuotaAccountVO result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.persist(entity);
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public boolean update(Long id, QuotaAccountVO entity) {
    +        boolean result = false;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.update(id, entity);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949667
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947741
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    --- End diff --
    
    Catching ``Exception`` is broad -- catching all checked and unchecked exceptions.  Why are we catching all and ignoring all exceptions here?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605235
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java ---
    @@ -0,0 +1,73 @@
    +//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.List;
    +
    +import javax.ejb.Local;
    +
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.springframework.stereotype.Component;
    +
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaAccountDao.class })
    +public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> implements QuotaAccountDao {
    +
    +    @Override
    +    public List<QuotaAccountVO> listAll() {
    +        List<QuotaAccountVO> result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    comment as above,


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605399
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaCreditsCmd.java ---
    @@ -0,0 +1,143 @@
    +//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 com.cloud.user.Account;
    +
    +import org.apache.cloudstack.api.APICommand;
    +import org.apache.cloudstack.api.ApiConstants;
    +import org.apache.cloudstack.api.ApiErrorCode;
    +import org.apache.cloudstack.api.BaseCmd;
    +import org.apache.cloudstack.api.Parameter;
    +import org.apache.cloudstack.api.ServerApiException;
    +import org.apache.cloudstack.api.response.DomainResponse;
    +import org.apache.cloudstack.api.response.QuotaCreditsResponse;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.log4j.Logger;
    +
    +import javax.inject.Inject;
    +
    +@APICommand(name = "quotaCredits", responseObject = QuotaCreditsResponse.class, description = "Add +-credits to an account", since = "4.2.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
    --- End diff --
    
    ok


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950304
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    +                } else {
    +                    return qbrecords;
    +                }
    +            } else {
    +                throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    +            }
    +        }
    +
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-134245543
  
    @abhi An connection pool makes acquiring a scarce resource less expensive, but it is not cost-free.  By taking a connection when the pool is under load, the following could occur:
    
    * This code may request a connection when none are available causing the connection pool to create another ad hoc connection or blocking the thread until a connection becomes available -- dependent on the pool configuration.
    * This code may exhaust the last connection in the pool causing other thread(s) to create ad hoc connection(s) or to block waiting for a connection to become available -- dependent on the pool configuration.
    
    There are variety of patterns that can be employed to address this problem.   I lean towards a construct such as the following:
    
    ```
    Transaction transaction = new Transaction(USAGE_DB).attempt(new TransactionWork() {
    
          public void execute(final Connection connection) throws SQLException{
    
                 // Do database work ...
          }
    
    }
    ```
    
    The ``Transaction.attempt`` method wraps the call to the passed ``TransactionWork.execute`` method with the following boilerplate that appears throughout the code base:
    
    1. Capturing the current database ID from ``TransactionLegacy``
    2. Open a connection to the requested database
    3. Handling exceptions including translation and proper logging
    4. Aborting/commiting/closing the transaction via ``TransactionLegacy`` as needed
    5. If necessary, switching ``TransactionLegacy`` back to the previous database ID
    
    These steps assume that ``TransactionLegacy`` has a ``switch`` method that changes the ``ThreadLocal`` tracking the current database ID without consuming a connection from the pool.
    
    I prefer this approach because it allows DAO to declare transactional blocks in a side-effect free manner.  This form of code should be immune to a replacement of ``TransationLegacy`` because it only relies on the JDBC API and makes no assumptions about the call flow.



---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-136326260
  
    I will re-review later today (31 August 2015).


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by bhaisaab <gi...@git.apache.org>.
Github user bhaisaab commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-130660478
  
    @DaanHoogland I think the aim here is to bring the work out for everyone to review early on, Abhi tells me he's already working on unit and marvin tests.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582427
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37583437
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java ---
    @@ -0,0 +1,138 @@
    +//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 com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaTariffDao.class })
    +public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> implements QuotaTariffDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaTariffDaoImpl.class.getName());
    +
    +    private final SearchBuilder<QuotaTariffVO> searchUsageType;
    +    private final SearchBuilder<QuotaTariffVO> listAllIncludedUsageType;
    +
    +    public QuotaTariffDaoImpl() {
    +        super();
    +        searchUsageType = createSearchBuilder();
    +        searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        searchUsageType.done();
    +
    +        listAllIncludedUsageType = createSearchBuilder();
    +        listAllIncludedUsageType.and("onorbefore", listAllIncludedUsageType.entity().getEffectiveOn(), SearchCriteria.Op.LTEQ);
    +        listAllIncludedUsageType.and("quotatype", listAllIncludedUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        listAllIncludedUsageType.done();
    +    }
    +
    +    @Override
    +    public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            sc.setParameters("quotatype", quotaType);
    +            result = search(sc, filter);
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        if (result.size() > 0) {
    +            //s_logger.info("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType  + " val=" + result.get(0).getCurrencyValue());
    +            return result.get(0);
    +        } else {
    +            //s_logger.info("Missing quota type " + quotaType);
    +            return null;
    +        }
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listAllTariffPlans(final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> tariffs = new ArrayList<QuotaTariffVO>();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            for (Integer quotaType : QuotaTypes.listQuotaTypes().keySet()) {
    +                sc.setParameters("quotatype", quotaType);
    +                List<QuotaTariffVO> result = search(sc, filter);
    +                if (result.size() > 0) {
    +                    tariffs.add(result.get(0));
    +                    s_logger.info("listAllTariffPlans onorbefore" + effectiveDate +  "quota type " + result.get(0).getDescription() + " , effective Date=" + result.get(0).getEffectiveOn() + " val=" + result.get(0).getCurrencyValue());
    +                }
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        return tariffs;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaTariff(QuotaTariffVO plan) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); // Switch
    +                                                                                    // to
    +        boolean result = false;
    +        try {
    +            // Usage DB
    +            result = this.update(plan.getId(), plan);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close(); // Switch back
    --- End diff --
    
    See previous comments regarding opening/closing transactions on the same line.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-134027847
  
    @jburwell  @DaanHoogland There is a underlying connection pool to save on expensive db connection operations. Moreover this cost should be similar to opening and closing transactions without switching DB.
    I think we should divide the DB transaction issue into two:
    1. The expense of the operation and how it can be avoided. My initial thoughts are that this is trivial and non issue.
    2. The way a DB switch is affected, is there a better pattern ?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605216
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java ---
    @@ -0,0 +1,92 @@
    +// 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.constant;
    +
    +import org.apache.cloudstack.usage.UsageTypes;
    +
    +import java.util.HashMap;
    +
    +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;
    +
    +    private Integer quotaType;
    +    private String quotaName;
    +    private String quotaUnit;
    +    private String description;
    +    private String discriminator;
    +
    +    public QuotaTypes(Integer quotaType, String name, String unit, String description) {
    +        this.quotaType = quotaType;
    +        this.description = description;
    +        this.quotaName = name;
    +        this.quotaUnit = unit;
    +        this.discriminator = "None";
    +    }
    +
    +    public static HashMap<Integer, QuotaTypes> listQuotaTypes() {
    +        final HashMap<Integer, QuotaTypes> quotaTypeList = new HashMap<Integer, QuotaTypes>();
    +        quotaTypeList.put(new Integer(RUNNING_VM), new QuotaTypes(new Integer(RUNNING_VM), "RUNNING_VM", "Compute-Month", "Running Vm Usage"));
    +        quotaTypeList.put(new Integer(ALLOCATED_VM), new QuotaTypes(new Integer(ALLOCATED_VM), "ALLOCATED_VM", "Compute-Month", "Allocated Vm Usage"));
    +        quotaTypeList.put(new Integer(IP_ADDRESS), new QuotaTypes(new Integer(IP_ADDRESS), "IP_ADDRESS", "IP-Month", "IP Address Usage"));
    +        quotaTypeList.put(new Integer(NETWORK_BYTES_SENT), new QuotaTypes(new Integer(NETWORK_BYTES_SENT), "NETWORK_BYTES_SENT", "GB", "Network Usage (Bytes Sent)"));
    +        quotaTypeList.put(new Integer(NETWORK_BYTES_RECEIVED), new QuotaTypes(new Integer(NETWORK_BYTES_RECEIVED), "NETWORK_BYTES_RECEIVED", "GB", "Network Usage (Bytes Received)"));
    +        quotaTypeList.put(new Integer(VOLUME), new QuotaTypes(new Integer(VOLUME), "VOLUME", "GB-Month", "Volume Usage"));
    +        quotaTypeList.put(new Integer(TEMPLATE), new QuotaTypes(new Integer(TEMPLATE), "TEMPLATE", "GB-Month", "Template Usage"));
    +        quotaTypeList.put(new Integer(ISO), new QuotaTypes(new Integer(ISO), "ISO", "GB-Month", "ISO Usage"));
    +        quotaTypeList.put(new Integer(SNAPSHOT), new QuotaTypes(new Integer(SNAPSHOT), "SNAPSHOT", "GB-Month", "Snapshot Usage"));
    +        quotaTypeList.put(new Integer(SECURITY_GROUP), new QuotaTypes(new Integer(SECURITY_GROUP), "SECURITY_GROUP", "Policy-Month", "Security Group Usage"));
    +        quotaTypeList.put(new Integer(LOAD_BALANCER_POLICY), new QuotaTypes(new Integer(LOAD_BALANCER_POLICY), "LOAD_BALANCER_POLICY", "Policy-Month", "Load Balancer Usage"));
    +        quotaTypeList.put(new Integer(PORT_FORWARDING_RULE), new QuotaTypes(new Integer(PORT_FORWARDING_RULE), "PORT_FORWARDING_RULE", "Policy-Month", "Port Forwarding Usage"));
    +        quotaTypeList.put(new Integer(NETWORK_OFFERING), new QuotaTypes(new Integer(NETWORK_OFFERING), "NETWORK_OFFERING", "Policy-Month", "Network Offering Usage"));
    +        quotaTypeList.put(new Integer(VPN_USERS), new QuotaTypes(new Integer(VPN_USERS), "VPN_USERS", "Policy-Month", "VPN users usage"));
    +        quotaTypeList.put(new Integer(VM_DISK_IO_READ), new QuotaTypes(new Integer(VM_DISK_IO_READ), "VM_DISK_IO_READ", "GB", "VM Disk usage(I/O Read)"));
    +        quotaTypeList.put(new Integer(VM_DISK_IO_WRITE), new QuotaTypes(new Integer(VM_DISK_IO_WRITE), "VM_DISK_IO_WRITE", "GB", "VM Disk usage(I/O Write)"));
    +        quotaTypeList.put(new Integer(VM_DISK_BYTES_READ), new QuotaTypes(new Integer(VM_DISK_BYTES_READ), "VM_DISK_BYTES_READ", "GB", "VM Disk usage(Bytes Read)"));
    +        quotaTypeList.put(new Integer(VM_DISK_BYTES_WRITE), new QuotaTypes(new Integer(VM_DISK_BYTES_WRITE), "VPN_USERS", "GB", "VM Disk usage(Bytes Write)"));
    +        quotaTypeList.put(new Integer(VM_SNAPSHOT), new QuotaTypes(new Integer(VM_SNAPSHOT), "VM_SNAPSHOT", "GB-Month", "VM Snapshot storage usage"));
    +        quotaTypeList.put(new Integer(CPU_CLOCK_RATE), new QuotaTypes(new Integer(CPU_CLOCK_RATE), "CPU_CLOCK_RATE", "Compute-Month", "Quota tariff for using 1 CPU of clock rate 100MHz"));
    +        quotaTypeList.put(new Integer(CPU_NUMBER), new QuotaTypes(new Integer(CPU_NUMBER), "CPU_NUMBER", "Compute-Month", "Quota tariff for running VM that has 1vCPU"));
    +        quotaTypeList.put(new Integer(MEMORY), new QuotaTypes(new Integer(MEMORY), "MEMORY", "Compute-Month", "Quota tariff for using 1MB or RAM for 1 hour"));
    +        return quotaTypeList;
    --- End diff --
    
    Ok


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950222
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    --- End diff --
    
    Why aren't all parameters declared final?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37627680
  
    --- Diff: engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java ---
    @@ -469,4 +478,25 @@ public void removeOldUsageRecords(int days) {
                 txn.close();
             }
         }
    +
    +    @SuppressWarnings("deprecation")
    +    public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        s_logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId);
    +        Filter usageFilter = new Filter(UsageVO.class, "startDate", true, 0L, 10000L);
    +        SearchCriteria<UsageVO> sc = 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.NULL);
    +        sc.addOr("quotaCalculated", SearchCriteria.Op.EQ, 0);
    +        s_logger.debug("Getting usage records" + usageFilter.getOrderBy());
    +        Pair<List<UsageVO>, Integer> usageRecords = searchAndCountAllRecords(sc, usageFilter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Reading through the ``TransactionLegacy`` code, this approach to switching the databases imposes in an incredibly high overhead because it actually consumes a connection from the pool and then throws it away in order to manipulate a thread local that tracks which the current connection pool.  At a minimum, we should consider adding a ``switch`` or ``use`` method that only modifies the thread local tracking the current connection pool.
    
    The larger issue, which is out of scope for this PR, is that we essentially have a global, mutable state.  This pattern of behavior appears to be present to change the connection pool used for the set of database queries executed within the scope of this method.  If the method fails to switch the connection pool back to the original value, subsequent operations would fail.  Within ``TransactionLegacy``, a thread local variable is used to track this state.  Since threads are reused within application, this data is effectively global.  We should refactor this class to create a new object instance for each transaction that points to the proper connection pool within its scope. By reducing the scope to an instance, we can create a side effect free transaction/connection management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582361
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLaterBalanceEntry(final long accountId, final long domainId, final Date afterThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.GT, afterThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @Override
    +    public void saveQuotaBalance(final List<QuotaBalanceVO> credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            for (QuotaBalanceVO credit : credits) {
    +                persist(credit);
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaBalanceVO> findCreditBalance(final long accountId, final long domainId, final Date lastbalancedate, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.GT, 0);
    +        if ((lastbalancedate != null) && (beforeThis != null) && lastbalancedate.before(beforeThis)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, lastbalancedate, beforeThis);
    +        } else {
    +            return new ArrayList<QuotaBalanceVO>();
    +        }
    +        List<QuotaBalanceVO> qb = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return qb;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalance(final Long accountId, final Long domainId, final Date startDate, final Date endDate) {
    +    // TODO account for series of credits around boundaries
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        List<QuotaBalanceVO> quotaUsageRecords = null;
    +        try {
    +            SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +            if (accountId != null) {
    +                sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +            }
    +            if (domainId != null) {
    +                sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +            }
    +            if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
    +                sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
    +            } else {
    +                return new ArrayList<QuotaBalanceVO>();
    +            }
    +            quotaUsageRecords = listBy(sc);
    +            if (quotaUsageRecords.size() == 0) {
    +                quotaUsageRecords.addAll(lastQuotaBalanceVO(accountId, domainId, startDate));
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +
    +        TransactionLegacy.open(opendb).close();
    +        return quotaUsageRecords;
    +    }
    +
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaBalanceVO> lastQuotaBalanceVO(final Long accountId, final Long domainId, final Date pivotDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        List<QuotaBalanceVO> quotaUsageRecords = null;
    +        List<QuotaBalanceVO> trimmedRecords = new ArrayList<QuotaBalanceVO>();
    +        try {
    +            Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 100L);
    +            // ASSUMPTION there will be less than 100 continuous credit
    +            // transactions
    +            SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +            if (accountId != null) {
    +                sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +            }
    +            if (domainId != null) {
    +                sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +            }
    +            if ((pivotDate != null)) {
    +                sc.addAnd("updatedOn", SearchCriteria.Op.LTEQ, pivotDate);
    +            }
    +            quotaUsageRecords = search(sc, filter);
    +
    +            // get records before startDate to find start balance
    +            for (Iterator<QuotaBalanceVO> it = quotaUsageRecords.iterator(); it.hasNext();) {
    +                QuotaBalanceVO entry = it.next();
    +                s_logger.info("findQuotaBalance Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +                if (entry.getCreditsId() > 0) {
    +                    trimmedRecords.add(entry);
    +                } else {
    +                    trimmedRecords.add(entry);
    +                    break; // add only consecutive credit entries and last balance entry
    +                }
    +            }
    +
    +        } finally {
    +            txn.close();
    +        }
    +
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

Re: [GitHub] cloudstack pull request: Quota master

Posted by Abhinandan Prateek <ab...@shapeblue.com>.
Kishan,

  With your familiarity with usage, I think it makes sense to review this plugin.

Regards,
-abhi

> On 13-Aug-2015, at 6:29 pm, DaanHoogland <gi...@git.apache.org> wrote:
>
> Github user DaanHoogland commented on the pull request:
>
>    https://github.com/apache/cloudstack/pull/689#issuecomment-130661556
>
>    @bhaisaab well it looks promising.
>
>
> ---
> If your project is set up for it, you can reply to this email and have your
> reply appear on GitHub as well. If your project does not have this feature
> enabled and wishes so, or if the feature is enabled but not working, please
> contact infrastructure at infrastructure@apache.org or file a JIRA ticket
> with INFRA.
> ---

Find out more about ShapeBlue and our range of CloudStack related services

IaaS Cloud Design & Build<http://shapeblue.com/iaas-cloud-design-and-build//>
CSForge – rapid IaaS deployment framework<http://shapeblue.com/csforge/>
CloudStack Consulting<http://shapeblue.com/cloudstack-consultancy/>
CloudStack Software Engineering<http://shapeblue.com/cloudstack-software-engineering/>
CloudStack Infrastructure Support<http://shapeblue.com/cloudstack-infrastructure-support/>
CloudStack Bootcamp Training Courses<http://shapeblue.com/cloudstack-training/>

This email and any attachments to it may be confidential and are intended solely for the use of the individual to whom it is addressed. Any views or opinions expressed are solely those of the author and do not necessarily represent those of Shape Blue Ltd or related companies. If you are not the intended recipient of this email, you must neither take any action based upon its contents, nor copy or show it to anyone. Please contact the sender if you believe you have received this email in error. Shape Blue Ltd is a company incorporated in England & Wales. ShapeBlue Services India LLP is a company incorporated in India and is operated under license from Shape Blue Ltd. Shape Blue Brasil Consultoria Ltda is a company incorporated in Brasil and is operated under license from Shape Blue Ltd. ShapeBlue SA Pty Ltd is a company registered by The Republic of South Africa and is traded under license from Shape Blue Ltd. ShapeBlue is a registered trademark.

[GitHub] cloudstack pull request: Quota master

Posted by DaanHoogland <gi...@git.apache.org>.
Github user DaanHoogland commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-130661556
  
    @bhaisaab well it looks promising.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949444
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    +                } else {
    +                    return qbrecords;
    +                }
    +            } else {
    +                throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    +            }
    +        }
    +
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    --- End diff --
    
    Lines 209-231 appear to be similar, if not identical, to lines 148-179 of plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java.  Please consolidate these blocks (and any others) into a shared utility method.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950553
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37628117
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    --- End diff --
    
    Findbugs is a static analysis tool that finds NPE conditions based on the flow expressed by the code configuration provided.  While all null dereferences it identifies are valid, it cannot identify all potential null dereferences because the JAR may be used in another, unevaluated context where a null reference is passed.  Therefore, static analysis is not a substitute for defensive programming best practice, and all de-referenced parameters should be defensively checked to be non-null.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37628774
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    --- End diff --
    
    ``checkArgument`` provides a consistent way to check arguments (e.g. always throwing ``IllegalArgumentException``) and allows the check to be one line rather than a 3 line if block.  Given the number of argument checks typically required, ``checkArgument`` can significantly improve code readability.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37572729
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java ---
    @@ -0,0 +1,92 @@
    +// 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.constant;
    +
    +import org.apache.cloudstack.usage.UsageTypes;
    +
    +import java.util.HashMap;
    +
    +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;
    +
    +    private Integer quotaType;
    +    private String quotaName;
    +    private String quotaUnit;
    +    private String description;
    +    private String discriminator;
    --- End diff --
    
    Why aren't these attributes declared final?  They do not change over the lifecycle of the class and set in the constructor.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947002
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaBalanceCmd.java ---
    @@ -0,0 +1,136 @@
    +//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.Date;
    +import java.util.List;
    +
    +import javax.inject.Inject;
    +
    +import org.apache.log4j.Logger;
    +import org.apache.cloudstack.api.APICommand;
    +import org.apache.cloudstack.api.ApiConstants;
    +import org.apache.cloudstack.api.BaseCmd;
    +import org.apache.cloudstack.api.Parameter;
    +import org.apache.cloudstack.api.response.AccountResponse;
    +import org.apache.cloudstack.api.response.DomainResponse;
    +import org.apache.cloudstack.api.response.QuotaBalanceResponse;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.api.response.QuotaStatementItemResponse;
    +
    +import com.cloud.user.Account;
    +
    +@APICommand(name = "quotaBalance", responseObject = QuotaStatementItemResponse.class, description = "Create a quota balance statement", since = "4.2.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
    +public class QuotaBalanceCmd extends BaseCmd {
    +
    +    public static final Logger s_logger = Logger.getLogger(QuotaBalanceCmd.class.getName());
    +
    +    private static final String s_name = "quotabalanceresponse";
    +
    +    @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, 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;
    +
    +    @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "End date range for quota query. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-03.")
    +    private Date endDate;
    +
    +    @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "Start date range quota query. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-01.")
    +    private Date startDate;
    +
    +    @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "List usage records for the specified account")
    +    private Long accountId;
    +
    +    @Inject
    +    QuotaResponseBuilder _responseBuilder;
    +
    +    public Long getAccountId() {
    +        return accountId;
    +    }
    +
    +    public void setAccountId(Long accountId) {
    +        this.accountId = accountId;
    +    }
    +
    +    public String getAccountName() {
    +        return accountName;
    +    }
    +
    +    public void setAccountName(String accountName) {
    +        this.accountName = accountName;
    +    }
    +
    +    public Long getDomainId() {
    +        return domainId;
    +    }
    +
    +    public void setDomainId(Long domainId) {
    +        this.domainId = domainId;
    +    }
    +
    +    public Date getEndDate() {
    +        return endDate == null ? null : _responseBuilder.startOfNextDay(endDate);
    +    }
    +
    +    public void setEndDate(Date endDate) {
    +        this.endDate = endDate;
    +    }
    +
    +    public Date getStartDate() {
    +        return startDate;
    +    }
    +
    +    public void setStartDate(Date startDate) {
    +        this.startDate = startDate;
    +    }
    +
    +    public QuotaBalanceCmd() {
    +        super();
    +    }
    +
    +    @Override
    +    public String getCommandName() {
    +        return s_name;
    +    }
    +
    +    @Override
    +    public long getEntityOwnerId() {
    +        Long accountId = _accountService.getActiveAccountByName(accountName, domainId).getAccountId();
    +        if (accountId == null) {
    +            return CallContext.current().getCallingAccount().getId();
    +        }
    +        return Account.ACCOUNT_ID_SYSTEM;
    +    }
    +
    +    @Override
    +    public void execute() {
    +        List<QuotaBalanceVO> quotaUsage = _responseBuilder.getQuotaBalance(this);
    +
    +        QuotaBalanceResponse response;
    +        if (getEndDate() == null) {
    --- End diff --
    
    Why use the ``getEndDate()`` method in the ``if`` check and then use the attribute when building the response?  Seems appropriate to use either the method or the attribute consistently.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37946936
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaBalanceCmd.java ---
    @@ -0,0 +1,136 @@
    +//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.Date;
    +import java.util.List;
    +
    +import javax.inject.Inject;
    +
    +import org.apache.log4j.Logger;
    +import org.apache.cloudstack.api.APICommand;
    +import org.apache.cloudstack.api.ApiConstants;
    +import org.apache.cloudstack.api.BaseCmd;
    +import org.apache.cloudstack.api.Parameter;
    +import org.apache.cloudstack.api.response.AccountResponse;
    +import org.apache.cloudstack.api.response.DomainResponse;
    +import org.apache.cloudstack.api.response.QuotaBalanceResponse;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.api.response.QuotaStatementItemResponse;
    +
    +import com.cloud.user.Account;
    +
    +@APICommand(name = "quotaBalance", responseObject = QuotaStatementItemResponse.class, description = "Create a quota balance statement", since = "4.2.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
    --- End diff --
    
    The value of the ``since`` parameter should be ``4.6.0`` rather than ``4.2.0``.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37952287
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    +            _smtpHost = smtpHost;
    +            _smtpPort = smtpPort;
    +            _smtpUseAuth = smtpUseAuth;
    +            _smtpUsername = smtpUsername;
    +            _smtpPassword = smtpPassword;
    +            _emailSender = emailSender;
    +
    +            if (_smtpHost != null) {
    +                Properties smtpProps = new Properties();
    +                smtpProps.put("mail.smtp.host", smtpHost);
    +                smtpProps.put("mail.smtp.port", smtpPort);
    +                smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtp.user", smtpUsername);
    +                }
    +
    +                smtpProps.put("mail.smtps.host", smtpHost);
    +                smtpProps.put("mail.smtps.port", smtpPort);
    +                smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtps.user", smtpUsername);
    +                }
    +
    +                if ((smtpUsername != null) && (smtpPassword != null)) {
    +                    _smtpSession = Session.getInstance(smtpProps, new Authenticator() {
    +                        @Override
    +                        protected PasswordAuthentication getPasswordAuthentication() {
    +                            return new PasswordAuthentication(smtpUsername, smtpPassword);
    +                        }
    +                    });
    +                } else {
    +                    _smtpSession = Session.getInstance(smtpProps);
    +                }
    +                _smtpSession.setDebug(smtpDebug);
    +            } else {
    +                _smtpSession = null;
    +            }
    +        }
    +
    +        public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException {
    +            if (_smtpSession != null) {
    +                SMTPMessage msg = new SMTPMessage(_smtpSession);
    +                msg.setSender(new InternetAddress(_emailSender, _emailSender));
    +                msg.setFrom(new InternetAddress(_emailSender, _emailSender));
    +
    +                for (String email : emails) {
    +                    if (email != null && !email.isEmpty()) {
    +                        try {
    +                            InternetAddress address = new InternetAddress(email, email);
    +                            msg.addRecipient(Message.RecipientType.TO, address);
    +                        } catch (Exception pokemon) {
    +                            s_logger.error("Exception in creating address for:" + email, pokemon);
    +                        }
    +                    }
    +                }
    +
    +                msg.setSubject(subject);
    +                msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
    +                msg.setContent(body, "text/html; charset=utf-8");
    +                msg.saveChanges();
    +
    +                SMTPTransport smtpTrans = null;
    +                if (_smtpUseAuth) {
    +                    smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                } else {
    +                    smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                }
    +                smtpTrans.connect();
    +                smtpTrans.sendMessage(msg, msg.getAllRecipients());
    +                smtpTrans.close();
    +            } else {
    +                throw new CloudRuntimeException("Unable to create smtp session.");
    +            }
    +        }
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    public static long getDifferenceDays(Date d1, Date d2) {
    +        long diff = d2.getTime() - d1.getTime();
    +        return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return success;
    +    }
    +
    +    public boolean enableAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605268
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java ---
    @@ -0,0 +1,73 @@
    +//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.List;
    +
    +import javax.ejb.Local;
    +
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.springframework.stereotype.Component;
    +
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaAccountDao.class })
    +public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> implements QuotaAccountDao {
    +
    +    @Override
    +    public List<QuotaAccountVO> listAll() {
    +        List<QuotaAccountVO> result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.listAll();
    +        TransactionLegacy.open(opendb).close();
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaAccountVO findById(Long id) {
    +        QuotaAccountVO result = null;
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        result = super.findById(id);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37627849
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java ---
    @@ -0,0 +1,64 @@
    +//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 com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaEmailTemplatesDao.class })
    +public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase<QuotaEmailTemplatesVO, Long> implements QuotaEmailTemplatesDao {
    +
    +    protected SearchBuilder<QuotaEmailTemplatesVO> QuotaEmailTemplateSearch;
    +
    +    public QuotaEmailTemplatesDaoImpl() {
    +        super();
    +
    +        QuotaEmailTemplateSearch = createSearchBuilder();
    +        QuotaEmailTemplateSearch.and("template_name", QuotaEmailTemplateSearch.entity().getTemplateName(), SearchCriteria.Op.EQ);
    +        QuotaEmailTemplateSearch.done();
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    --- End diff --
    
    See @DaanHoogland note above and consider a try with resources rather than a ``try { ... } finally { ... }`` block.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37573927
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java ---
    @@ -0,0 +1,92 @@
    +// 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.constant;
    +
    +import org.apache.cloudstack.usage.UsageTypes;
    +
    +import java.util.HashMap;
    +
    +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;
    +
    +    private Integer quotaType;
    +    private String quotaName;
    +    private String quotaUnit;
    +    private String description;
    +    private String discriminator;
    +
    +    public QuotaTypes(Integer quotaType, String name, String unit, String description) {
    +        this.quotaType = quotaType;
    +        this.description = description;
    +        this.quotaName = name;
    +        this.quotaUnit = unit;
    +        this.discriminator = "None";
    +    }
    +
    +    public static HashMap<Integer, QuotaTypes> listQuotaTypes() {
    +        final HashMap<Integer, QuotaTypes> quotaTypeList = new HashMap<Integer, QuotaTypes>();
    +        quotaTypeList.put(new Integer(RUNNING_VM), new QuotaTypes(new Integer(RUNNING_VM), "RUNNING_VM", "Compute-Month", "Running Vm Usage"));
    +        quotaTypeList.put(new Integer(ALLOCATED_VM), new QuotaTypes(new Integer(ALLOCATED_VM), "ALLOCATED_VM", "Compute-Month", "Allocated Vm Usage"));
    +        quotaTypeList.put(new Integer(IP_ADDRESS), new QuotaTypes(new Integer(IP_ADDRESS), "IP_ADDRESS", "IP-Month", "IP Address Usage"));
    +        quotaTypeList.put(new Integer(NETWORK_BYTES_SENT), new QuotaTypes(new Integer(NETWORK_BYTES_SENT), "NETWORK_BYTES_SENT", "GB", "Network Usage (Bytes Sent)"));
    +        quotaTypeList.put(new Integer(NETWORK_BYTES_RECEIVED), new QuotaTypes(new Integer(NETWORK_BYTES_RECEIVED), "NETWORK_BYTES_RECEIVED", "GB", "Network Usage (Bytes Received)"));
    +        quotaTypeList.put(new Integer(VOLUME), new QuotaTypes(new Integer(VOLUME), "VOLUME", "GB-Month", "Volume Usage"));
    +        quotaTypeList.put(new Integer(TEMPLATE), new QuotaTypes(new Integer(TEMPLATE), "TEMPLATE", "GB-Month", "Template Usage"));
    +        quotaTypeList.put(new Integer(ISO), new QuotaTypes(new Integer(ISO), "ISO", "GB-Month", "ISO Usage"));
    +        quotaTypeList.put(new Integer(SNAPSHOT), new QuotaTypes(new Integer(SNAPSHOT), "SNAPSHOT", "GB-Month", "Snapshot Usage"));
    +        quotaTypeList.put(new Integer(SECURITY_GROUP), new QuotaTypes(new Integer(SECURITY_GROUP), "SECURITY_GROUP", "Policy-Month", "Security Group Usage"));
    +        quotaTypeList.put(new Integer(LOAD_BALANCER_POLICY), new QuotaTypes(new Integer(LOAD_BALANCER_POLICY), "LOAD_BALANCER_POLICY", "Policy-Month", "Load Balancer Usage"));
    +        quotaTypeList.put(new Integer(PORT_FORWARDING_RULE), new QuotaTypes(new Integer(PORT_FORWARDING_RULE), "PORT_FORWARDING_RULE", "Policy-Month", "Port Forwarding Usage"));
    +        quotaTypeList.put(new Integer(NETWORK_OFFERING), new QuotaTypes(new Integer(NETWORK_OFFERING), "NETWORK_OFFERING", "Policy-Month", "Network Offering Usage"));
    +        quotaTypeList.put(new Integer(VPN_USERS), new QuotaTypes(new Integer(VPN_USERS), "VPN_USERS", "Policy-Month", "VPN users usage"));
    +        quotaTypeList.put(new Integer(VM_DISK_IO_READ), new QuotaTypes(new Integer(VM_DISK_IO_READ), "VM_DISK_IO_READ", "GB", "VM Disk usage(I/O Read)"));
    +        quotaTypeList.put(new Integer(VM_DISK_IO_WRITE), new QuotaTypes(new Integer(VM_DISK_IO_WRITE), "VM_DISK_IO_WRITE", "GB", "VM Disk usage(I/O Write)"));
    +        quotaTypeList.put(new Integer(VM_DISK_BYTES_READ), new QuotaTypes(new Integer(VM_DISK_BYTES_READ), "VM_DISK_BYTES_READ", "GB", "VM Disk usage(Bytes Read)"));
    +        quotaTypeList.put(new Integer(VM_DISK_BYTES_WRITE), new QuotaTypes(new Integer(VM_DISK_BYTES_WRITE), "VPN_USERS", "GB", "VM Disk usage(Bytes Write)"));
    +        quotaTypeList.put(new Integer(VM_SNAPSHOT), new QuotaTypes(new Integer(VM_SNAPSHOT), "VM_SNAPSHOT", "GB-Month", "VM Snapshot storage usage"));
    +        quotaTypeList.put(new Integer(CPU_CLOCK_RATE), new QuotaTypes(new Integer(CPU_CLOCK_RATE), "CPU_CLOCK_RATE", "Compute-Month", "Quota tariff for using 1 CPU of clock rate 100MHz"));
    +        quotaTypeList.put(new Integer(CPU_NUMBER), new QuotaTypes(new Integer(CPU_NUMBER), "CPU_NUMBER", "Compute-Month", "Quota tariff for running VM that has 1vCPU"));
    +        quotaTypeList.put(new Integer(MEMORY), new QuotaTypes(new Integer(MEMORY), "MEMORY", "Compute-Month", "Quota tariff for using 1MB or RAM for 1 hour"));
    +        return quotaTypeList;
    --- End diff --
    
    As a defensive measure against concurrent modification, the return should wrapped in ``Collections.unmodifiableMap`` or use ``com.google.collect.ImmutableMap`` as the ``Map`` implementation. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950380
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    +                } else {
    +                    return qbrecords;
    +                }
    +            } else {
    +                throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    --- End diff --
    
    InvalidParameterValueException exception indicates to the user that there is something wrong with the parameters here it indicates issues with date values passed/


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37585845
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaCreditsCmd.java ---
    @@ -0,0 +1,143 @@
    +//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 com.cloud.user.Account;
    +
    +import org.apache.cloudstack.api.APICommand;
    +import org.apache.cloudstack.api.ApiConstants;
    +import org.apache.cloudstack.api.ApiErrorCode;
    +import org.apache.cloudstack.api.BaseCmd;
    +import org.apache.cloudstack.api.Parameter;
    +import org.apache.cloudstack.api.ServerApiException;
    +import org.apache.cloudstack.api.response.DomainResponse;
    +import org.apache.cloudstack.api.response.QuotaCreditsResponse;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.log4j.Logger;
    +
    +import javax.inject.Inject;
    +
    +@APICommand(name = "quotaCredits", responseObject = QuotaCreditsResponse.class, description = "Add +-credits to an account", since = "4.2.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
    --- End diff --
    
    The ``since`` attribute says ``4.2.0``, but this API is new for ``4.6.0``.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947704
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    --- End diff --
    
    Please remove commented code -- it contributes to cruft accumulation.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605271
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java ---
    @@ -0,0 +1,72 @@
    +//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.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaCreditsDao.class })
    +public class QuotaCreditsDaoImpl extends GenericDaoBase<QuotaCreditsVO, Long> implements QuotaCreditsDao {
    +    @Inject
    +    QuotaBalanceDao _quotaBalanceDao;
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaCreditsVO> findCredits(final long accountId, final long domainId, final Date startDate, final Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaCreditsVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
    +        } else {
    +            return new ArrayList<QuotaCreditsVO>();
    +        }
    +        List<QuotaCreditsVO> qc = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37627808
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java ---
    @@ -0,0 +1,72 @@
    +//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.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaCreditsDao.class })
    +public class QuotaCreditsDaoImpl extends GenericDaoBase<QuotaCreditsVO, Long> implements QuotaCreditsDao {
    +    @Inject
    +    QuotaBalanceDao _quotaBalanceDao;
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaCreditsVO> findCredits(final long accountId, final long domainId, final Date startDate, final Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaCreditsVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
    +        } else {
    +            return new ArrayList<QuotaCreditsVO>();
    +        }
    +        List<QuotaCreditsVO> qc = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return qc;
    +    }
    +
    +    @Override
    +    public QuotaCreditsVO saveCredits(final QuotaCreditsVO credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    --- End diff --
    
    @DaanHoogland do you mean using a try with resources construct?  +1 to that idea.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37586539
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaTariffListCmd.java ---
    @@ -0,0 +1,93 @@
    +//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 com.cloud.user.Account;
    +
    +import org.apache.cloudstack.api.APICommand;
    +import org.apache.cloudstack.api.ApiConstants;
    +import org.apache.cloudstack.api.BaseListCmd;
    +import org.apache.cloudstack.api.Parameter;
    +import org.apache.cloudstack.api.response.ListResponse;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.api.response.QuotaTariffResponse;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +
    +import javax.inject.Inject;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@APICommand(name = "quotaTariffList", responseObject = QuotaTariffResponse.class, description = "Lists all quota tariff plans", since = "4.6.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
    +public class QuotaTariffListCmd extends BaseListCmd {
    +    public static final Logger s_logger = Logger.getLogger(QuotaTariffListCmd.class.getName());
    +    private static final String s_name = "quotatarifflistresponse";
    +
    +    @Inject
    +    QuotaResponseBuilder _responseBuilder;
    +
    +    @Parameter(name = ApiConstants.USAGE_TYPE, type = CommandType.INTEGER, required = false, description = "Usage type of the resource")
    +    private Integer usageType;
    +
    +    @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, description = "The effective start date on/after which the quota tariff is effective and older tariffs are no longer used for the usage type. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-03.")
    +    private Date effectiveDate;
    +
    +    public QuotaTariffListCmd() {
    +        super();
    +    }
    +
    +    @Override
    +    public void execute() {
    +        final List<QuotaTariffVO> result = _responseBuilder.listQuotaTariffPlans(this);
    +
    +        final List<QuotaTariffResponse> responses = new ArrayList<QuotaTariffResponse>();
    +        for (final QuotaTariffVO resource : result) {
    +            s_logger.info("Result desc=" + resource.getDescription() + " date=" + resource.getEffectiveOn() + " val=" + resource.getCurrencyValue());
    --- End diff --
    
    This message seems like a DEBUG rather than INFO level.  To my way of thinking, an INFO level message should provide information that a system administrator can use to understand the operation of the system.  This message does not appear to provide such information.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582547
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java ---
    @@ -0,0 +1,72 @@
    +//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.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaCreditsDao.class })
    +public class QuotaCreditsDaoImpl extends GenericDaoBase<QuotaCreditsVO, Long> implements QuotaCreditsDao {
    +    @Inject
    +    QuotaBalanceDao _quotaBalanceDao;
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaCreditsVO> findCredits(final long accountId, final long domainId, final Date startDate, final Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaCreditsVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
    +        } else {
    +            return new ArrayList<QuotaCreditsVO>();
    +        }
    +        List<QuotaCreditsVO> qc = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return qc;
    +    }
    +
    +    @Override
    +    public QuotaCreditsVO saveCredits(final QuotaCreditsVO credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        persist(credits);
    +        // make an entry in the balance table
    +        QuotaBalanceVO bal = new QuotaBalanceVO(credits);
    +        _quotaBalanceDao.persist(bal);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951236
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    +            ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal onegbcost;
    +            BigDecimal rawusageingb;
    +            BigDecimal networkusage;
    +            onegbcost = tariff.getCurrencyValue();
    +            // s_logger.info("Quotatariff onegbcost=" + onegbcost);
    +            rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
    +            networkusage = rawusageingb.multiply(onegbcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), transferType, networkusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    Per previous comments, the immediate open and close of a transaction needs needs to be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950152
  
    --- Diff: server/src/com/cloud/api/dispatch/ParamProcessWorker.java ---
    @@ -294,6 +294,14 @@ private void setFieldValue(final Field field, final BaseCmd cmdObj, final Object
                         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())) {
    --- End diff --
    
    I personally do not like to include a google package just to validate data.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605247
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLaterBalanceEntry(final long accountId, final long domainId, final Date afterThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.GT, afterThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947139
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    --- End diff --
    
    Please remove commented code -- it is cruft.  git provides the means to track changes in code including removed code.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-134855999
  
    @jburwell was looking at com.cloud.utils.db.Transaction class the db is hardcoded in there:
    
    execute()....
    short databaseId = TransactionLegacy.CLOUD_DB;
    
    I think whole revamping of transactions is a separate sub-project that we should work on and should not be clubbed with Quota changes.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605332
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java ---
    @@ -0,0 +1,64 @@
    +//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 com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaEmailTemplatesDao.class })
    +public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase<QuotaEmailTemplatesVO, Long> implements QuotaEmailTemplatesDao {
    +
    +    protected SearchBuilder<QuotaEmailTemplatesVO> QuotaEmailTemplateSearch;
    +
    +    public QuotaEmailTemplatesDaoImpl() {
    +        super();
    +
    +        QuotaEmailTemplateSearch = createSearchBuilder();
    +        QuotaEmailTemplateSearch.and("template_name", QuotaEmailTemplateSearch.entity().getTemplateName(), SearchCriteria.Op.EQ);
    +        QuotaEmailTemplateSearch.done();
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    --- End diff --
    
    Accepted.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37587109
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    --- End diff --
    
    Why not use a for each loop for greater code conciseness?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951210
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    +            ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal onegbcost;
    +            BigDecimal rawusageingb;
    +            BigDecimal networkusage;
    +            onegbcost = tariff.getCurrencyValue();
    +            // s_logger.info("Quotatariff onegbcost=" + onegbcost);
    +            rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
    +            networkusage = rawusageingb.multiply(onegbcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), transferType, networkusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    --- End diff --
    
    Consider using the ``DateUtils.addDaysTo`` utility method suggested previously.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947237
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    --- End diff --
    
    Logging to INFO should provide contextual information useable by system administrators.  This message seems more appropriate as DEBUG or it needs more context to be useful by a system administrator.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37948903
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    --- End diff --
    
    An INFO level log message should contextual information for a system administrator. This messages lacks that context. It should either be logged at DEBUG or context information should be added to it.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947918
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    +                    s_logger.error(String.format("Unable to unlock account %s after getting enough quota credits", account.getAccountName()));
    +                }
    +            }
    +        }
    +
    +        String creditor = String.valueOf(Account.ACCOUNT_ID_SYSTEM);
    +        User creditorUser = _userDao.getUser(updatedBy);
    +        if (creditorUser != null) {
    +            creditor = creditorUser.getUsername();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        QuotaCreditsResponse response = new QuotaCreditsResponse(result, creditor);
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    private QuotaEmailTemplateResponse createQuotaEmailResponse(QuotaEmailTemplatesVO template) {
    +        QuotaEmailTemplateResponse response = new QuotaEmailTemplateResponse();
    +        response.setTemplateType(template.getTemplateName());
    +        response.setTemplateSubject(template.getTemplateSubject());
    +        response.setTemplateText(template.getTemplateBody());
    +        response.setLocale(template.getLocale());
    +        response.setLastUpdatedOn(template.getLastUpdated());
    +        return response;
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplateResponse> listQuotaEmailTemplates(QuotaEmailTemplateListCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        final List<QuotaEmailTemplateResponse> responses = new ArrayList<QuotaEmailTemplateResponse>();
    +        for (final QuotaEmailTemplatesVO template : templates) {
    +            responses.add(createQuotaEmailResponse(template));
    +        }
    +        return responses;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplateUpdateCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        final String templateSubject = StringEscapeUtils.escapeJavaScript(cmd.getTemplateSubject());
    +        final String templateBody = StringEscapeUtils.escapeJavaScript(cmd.getTemplateBody());
    +        final String locale = cmd.getLocale();
    +
    +        final List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        if (templates.size() == 1) {
    +            final QuotaEmailTemplatesVO template = templates.get(0);
    +            template.setTemplateSubject(templateSubject);
    +            template.setTemplateBody(templateBody);
    +            if (locale != null) {
    +                template.setLocale(locale);
    +            }
    +            return _quotaEmailTemplateDao.updateQuotaEmailTemplate(template);
    +        }
    +        return false;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("There are no balance entries on or before the requested date.");
    +        }
    +        if (startDate == null) {
    +            startDate = new Date();
    +        }
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaLastBalanceResponse Date=" + entry.getUpdatedOn() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            lastCredits = lastCredits.add(entry.getCreditBalance());
    +        }
    +        resp.setStartQuota(lastCredits);
    +        resp.setStartDate(_quotaService.computeAdjustedTime(startDate));
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(QuotaStatementCmd cmd) {
    +        return _quotaService.getQuotaUsage(cmd.getAccountId(), cmd.getAccountName(), cmd.getDomainId(), cmd.getUsageType(), cmd.getStartDate(), cmd.getEndDate());
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> getQuotaBalance(QuotaBalanceCmd cmd) {
    +        return _quotaService.findQuotaBalanceVO(cmd.getAccountId(), cmd.getAccountName(), cmd.getDomainId(), cmd.getStartDate(), cmd.getEndDate());
    +    }
    +
    +    @Override
    +    public Date startOfNextDay(Date dt) {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(dt);
    +        c.add(Calendar.DATE, 1);
    +        dt = c.getTime();
    --- End diff --
    
    Consider extracting lines 403-406 to a utility method such as ``DateUtil.addDaysTo(final Date date, final int numDays)`` and adding unit tests to verify behavior.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950620
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    --- End diff --
    
    Since this is the first release the debug messages have been left purposefully there in order to assist in debugging. These will be removed in later version.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951126
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949093
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    +            } else {
    +                return qbrecords;
    +            }
    +        } else {
    +            Date adjustedStartDate = computeAdjustedTime(startDate);
    +            if (endDate.after(_respBldr.startOfNextDay())) {
    +                throw new InvalidParameterValueException("Incorrect Date Range. End date:" + endDate + " should not be in future. ");
    +            } else if (startDate.before(endDate)) {
    +                Date adjustedEndDate = computeAdjustedTime(endDate);
    +                s_logger.debug("getQuotaBalance2: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate);
    +                List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.findQuotaBalance(accountId, domainId, adjustedStartDate, adjustedEndDate);
    +                s_logger.info("getQuotaBalance3: Found records size=" + qbrecords.size());
    +                if (qbrecords.size() == 0) {
    +                    throw new InvalidParameterValueException("Incorrect Date range there are no quota records between these dates start date " + adjustedStartDate + " and end date:" + endDate);
    +                } else {
    +                    return qbrecords;
    +                }
    +            } else {
    +                throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
    +            }
    +        }
    +
    +    }
    +
    +    @Override
    +    public List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    Per previous comments, the immediate opening and closing of a transaction should be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by karuturi <gi...@git.apache.org>.
Github user karuturi commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-136326696
  
    @bhaisaab lot of code :) .. I will review tomorrow.
    Can you review 755 if possible? ;) 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951139
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
    +            quotaUsgage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostpergb).multiply(noofgbinuse);
    +            // s_logger.info(" No of GB In use = " + noofgbinuse +
    +            // " onehour cost=" + onehourcostpergb);
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947679
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    Per previous comments, the immediate open and close of a transaction needs needs to be replaced with the new transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek closed the pull request at:

    https://github.com/apache/cloudstack/pull/689


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37583531
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaUsageDaoImpl.java ---
    @@ -0,0 +1,106 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +
    +import com.cloud.utils.Pair;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaUsageDao.class })
    +public class QuotaUsageDaoImpl extends GenericDaoBase<QuotaUsageVO, Long> implements QuotaUsageDao {
    +
    +    @Override
    +    public Pair<List<QuotaUsageVO>, Integer> searchAndCountAllRecords(SearchCriteria<QuotaUsageVO> sc, Filter filter) {
    +        return listAndCountIncludingRemovedBy(sc, filter);
    +    }
    +
    +    @Override
    +    public void saveQuotaUsage(List<QuotaUsageVO> records) {
    +        for (QuotaUsageVO usageRecord : records) {
    +            persist(usageRecord);
    +        }
    +    }
    +
    +    @Override
    +    public BigDecimal findTotalQuotaUsage(final Long accountId, final Long domainId, final Integer usageType, final Date startDate, final Date endDate) {
    +        List<QuotaUsageVO> quotaUsage = findQuotaUsage(accountId, domainId, null, startDate, endDate);
    +        BigDecimal total = new BigDecimal(0);
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            total = total.add(quotaRecord.getQuotaUsed());
    +        }
    +        return total;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaUsageVO> findQuotaUsage(final Long accountId, final Long domainId, final Integer usageType, final Date startDate, final Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        List<QuotaUsageVO> quotaUsageRecords = null;
    +        try {
    +            // TODO instead of max value query with reasonable number and
    +            // iterate
    +            SearchCriteria<QuotaUsageVO> sc = createSearchCriteria();
    +            if (accountId != null) {
    +                sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +            }
    +            /*
    +             * if (isDomainAdmin) { SearchCriteria<DomainVO> sdc =
    +             * _domainDao.createSearchCriteria(); sdc.addOr("path",
    +             * SearchCriteria.Op.LIKE,
    +             * _domainDao.findById(caller.getDomainId()).getPath() + "%");
    +             * List<DomainVO> domains = _domainDao.search(sdc, null); List<Long>
    +             * domainIds = new ArrayList<Long>(); for (DomainVO domain :
    +             * domains) domainIds.add(domain.getId()); sc.addAnd("domainId",
    +             * SearchCriteria.Op.IN, domainIds.toArray());
    +             * s_logger.debug("Account ID=" + accountId); }
    +             */
    --- End diff --
    
    Please remove commented code as it is cruft that obscures the rest of the method. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950862
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    --- End diff --
    
    i think that was done due to code that is commented now. Should be removed.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-134480002
  
    I think we should not be using TransactionLegacy anymore. It was used in first place just to be consistent with UsageServer and due to time constraints. I think if we should revamp this by using the new translation framework and do it like:
    
    Transaction.execute(new TransactionCallback<Object>() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
            // ... do something ...
            return myresult;
        }
    });
    
    This is described here by Darren https://cwiki.apache.org/confluence/display/CLOUDSTACK/Database+Transactions.
    
    I propose that we revamp the Quota with the new Transaction model and as well as the usage server as just the one without the other is still half work. Will work on that.



---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951192
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    +            ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal onegbcost;
    +            BigDecimal rawusageingb;
    +            BigDecimal networkusage;
    +            onegbcost = tariff.getCurrencyValue();
    +            // s_logger.info("Quotatariff onegbcost=" + onegbcost);
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605347
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java ---
    @@ -0,0 +1,138 @@
    +//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 com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaTariffDao.class })
    +public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> implements QuotaTariffDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaTariffDaoImpl.class.getName());
    +
    +    private final SearchBuilder<QuotaTariffVO> searchUsageType;
    +    private final SearchBuilder<QuotaTariffVO> listAllIncludedUsageType;
    +
    +    public QuotaTariffDaoImpl() {
    +        super();
    +        searchUsageType = createSearchBuilder();
    +        searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        searchUsageType.done();
    +
    +        listAllIncludedUsageType = createSearchBuilder();
    +        listAllIncludedUsageType.and("onorbefore", listAllIncludedUsageType.entity().getEffectiveOn(), SearchCriteria.Op.LTEQ);
    +        listAllIncludedUsageType.and("quotatype", listAllIncludedUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        listAllIncludedUsageType.done();
    +    }
    +
    +    @Override
    +    public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            sc.setParameters("quotatype", quotaType);
    +            result = search(sc, filter);
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        if (result.size() > 0) {
    +            //s_logger.info("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType  + " val=" + result.get(0).getCurrencyValue());
    --- End diff --
    
    ok


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37582377
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLaterBalanceEntry(final long accountId, final long domainId, final Date afterThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.GT, afterThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    +        return quotab.size() > 0 ? quotab.get(0) : null;
    +    }
    +
    +    @Override
    +    public void saveQuotaBalance(final List<QuotaBalanceVO> credits) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            for (QuotaBalanceVO credit : credits) {
    +                persist(credit);
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public List<QuotaBalanceVO> findCreditBalance(final long accountId, final long domainId, final Date lastbalancedate, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.GT, 0);
    +        if ((lastbalancedate != null) && (beforeThis != null) && lastbalancedate.before(beforeThis)) {
    +            sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, lastbalancedate, beforeThis);
    +        } else {
    +            return new ArrayList<QuotaBalanceVO>();
    +        }
    +        List<QuotaBalanceVO> qb = search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    See previous comments regarding apparently needless open/close of a database transaction.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37947252
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    Per comments above, the immediate open and close of a database connection needs to be replaced with the newer transaction management mechanism.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605406
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaTariffListCmd.java ---
    @@ -0,0 +1,93 @@
    +//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 com.cloud.user.Account;
    +
    +import org.apache.cloudstack.api.APICommand;
    +import org.apache.cloudstack.api.ApiConstants;
    +import org.apache.cloudstack.api.BaseListCmd;
    +import org.apache.cloudstack.api.Parameter;
    +import org.apache.cloudstack.api.response.ListResponse;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.api.response.QuotaTariffResponse;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +
    +import javax.inject.Inject;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@APICommand(name = "quotaTariffList", responseObject = QuotaTariffResponse.class, description = "Lists all quota tariff plans", since = "4.6.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
    +public class QuotaTariffListCmd extends BaseListCmd {
    +    public static final Logger s_logger = Logger.getLogger(QuotaTariffListCmd.class.getName());
    +    private static final String s_name = "quotatarifflistresponse";
    +
    +    @Inject
    +    QuotaResponseBuilder _responseBuilder;
    +
    +    @Parameter(name = ApiConstants.USAGE_TYPE, type = CommandType.INTEGER, required = false, description = "Usage type of the resource")
    +    private Integer usageType;
    +
    +    @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, description = "The effective start date on/after which the quota tariff is effective and older tariffs are no longer used for the usage type. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-03.")
    +    private Date effectiveDate;
    +
    +    public QuotaTariffListCmd() {
    +        super();
    +    }
    +
    +    @Override
    +    public void execute() {
    +        final List<QuotaTariffVO> result = _responseBuilder.listQuotaTariffPlans(this);
    +
    +        final List<QuotaTariffResponse> responses = new ArrayList<QuotaTariffResponse>();
    +        for (final QuotaTariffVO resource : result) {
    +            s_logger.info("Result desc=" + resource.getDescription() + " date=" + resource.getEffectiveOn() + " val=" + resource.getCurrencyValue());
    --- End diff --
    
    will check,


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950673
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaStatementResponse statement = new QuotaStatementResponse();
    +
    +        HashMap<Integer, QuotaTariffVO> quotaTariffMap = new HashMap<Integer, QuotaTariffVO>();
    +        List<QuotaTariffVO> result = _quotaTariffDao.listAll();
    +
    +        for (QuotaTariffVO quotaTariff : result) {
    +            quotaTariffMap.put(quotaTariff.getUsageType(), quotaTariff);
    +            // add dummy record for each usage type
    +            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
    +            dummy.setUsageType(quotaTariff.getUsageType());
    +            dummy.setQuotaUsed(new BigDecimal(0));
    +            quotaUsage.add(dummy);
    +        }
    +
    +        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
    +            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
    +                if (o1.getUsageType() == o2.getUsageType())
    +                    return 0;
    +                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
    +            }
    +        });
    +
    +        List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
    +        QuotaStatementItemResponse lineitem;
    +        int type = -1;
    +        BigDecimal usage = new BigDecimal(0);
    +        BigDecimal totalUsage = new BigDecimal(0);
    +        quotaUsage.add(new QuotaUsageVO());// boundary
    +        QuotaUsageVO prev = quotaUsage.get(0);
    +        // s_logger.info("createQuotaStatementResponse record count=" + quotaUsage.size());
    +        for (final QuotaUsageVO quotaRecord : quotaUsage) {
    +            // s_logger.info("createQuotaStatementResponse Type=" + quotaRecord.getUsageType() + " usage=" + usage + " name" + quotaRecord.getUsageItemId());
    +            if (type != quotaRecord.getUsageType()) {
    +                if (type != -1) {
    +                    lineitem = new QuotaStatementItemResponse();
    +                    lineitem.setUsageType(type);
    +                    lineitem.setQuotaUsed(usage);
    +                    lineitem.setAccountId(prev.getAccountId());
    +                    lineitem.setDomainId(prev.getDomainId());
    +                    lineitem.setStartDate(prev.getStartDate());
    +                    lineitem.setEndDate(prev.getEndDate());
    +                    lineitem.setUsageUnit(quotaTariffMap.get(type).getUsageUnit());
    +                    lineitem.setUsageName(quotaTariffMap.get(type).getUsageName());
    +                    lineitem.setObjectName("quotausage");
    +                    items.add(lineitem);
    +                    totalUsage = totalUsage.add(usage);
    +                    usage = new BigDecimal(0);
    +                }
    +                type = quotaRecord.getUsageType();
    +            }
    +            prev = quotaRecord;
    +            usage = usage.add(quotaRecord.getQuotaUsed());
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        statement.setLineItem(items);
    +        statement.setTotalQuota(totalUsage);
    +        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        statement.setObjectName("statement");
    +        return statement;
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
    +        List<QuotaTariffVO> result = new ArrayList<QuotaTariffVO>();
    +        Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
    +        Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
    +        s_logger.info("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
    +        if (cmd.getUsageType() != null) {
    +            QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
    +            if (tariffPlan != null) {
    +                result.add(tariffPlan);
    +            }
    +        } else {
    +            result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate);
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
    +        final int quotaType = cmd.getUsageType();
    +        final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
    +        final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
    +        final Date now = _quotaService.computeAdjustedTime(new Date());
    +        // if effective date is in the past return error
    +        if (effectiveDate.compareTo(now) < 0) {
    +            throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
    +        }
    +        QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
    +        if (quotaConstant == null) {
    +            throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        QuotaTariffVO result = null;
    +        try {
    +            result = new QuotaTariffVO();
    +            result.setUsageType(quotaType);
    +            result.setUsageName(quotaConstant.getQuotaName());
    +            result.setUsageUnit(quotaConstant.getQuotaUnit());
    +            result.setUsageDiscriminator(quotaConstant.getDiscriminator());
    +            result.setCurrencyValue(quotaCost);
    +            result.setEffectiveOn(effectiveDate);
    +            result.setUpdatedOn(now);
    +            result.setUpdatedBy(cmd.getEntityOwnerId());
    +
    +            s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
    +            _quotaTariffDao.addQuotaTariff(result);
    +        } catch (Exception pokemon) {
    +            s_logger.error("Error in update quota tariff plan: " + pokemon);
    +        } finally {
    +            TransactionLegacy.open(opendb).close();
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy) {
    +        Date depositDate = new Date();
    +        Date adjustedStartDate = _quotaService.computeAdjustedTime(depositDate);
    +        QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, adjustedStartDate);
    +
    +        if (qb != null) {
    +            throw new InvalidParameterValueException("Incorrect deposit date: " + adjustedStartDate + " there are balance entries after this date");
    +        }
    +
    +        return addQuotaCredits(accountId, domainId, amount, updatedBy, adjustedStartDate);
    +    }
    +
    +    @Override
    +    public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        QuotaCreditsVO result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
    +            s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
    +            credits.setUpdatedOn(despositedOn);
    +            result = _quotaCreditsDao.saveCredits(credits);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        final AccountVO account = _accountDao.findById(accountId);
    +        final boolean lockAccountEnforcement = QuotaConfig.QuotaEnableEnforcement.value().equalsIgnoreCase("true");
    +        final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
    +        if (lockAccountEnforcement && (currentAccountBalance.compareTo(new BigDecimal(0)) >= 0)) {
    +            if (account.getState() == Account.State.locked) {
    +                try {
    +                    _regionMgr.enableAccount(account.getAccountName(), domainId, accountId);
    +                    // _quotaMgr.sendQuotaAlert(account, currentAccountBalance, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_UNLOCK_ACCOUNT);
    +                } catch (Exception e) {
    +                    s_logger.error(String.format("Unable to unlock account %s after getting enough quota credits", account.getAccountName()));
    +                }
    +            }
    +        }
    +
    +        String creditor = String.valueOf(Account.ACCOUNT_ID_SYSTEM);
    +        User creditorUser = _userDao.getUser(updatedBy);
    +        if (creditorUser != null) {
    +            creditor = creditorUser.getUsername();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        QuotaCreditsResponse response = new QuotaCreditsResponse(result, creditor);
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    private QuotaEmailTemplateResponse createQuotaEmailResponse(QuotaEmailTemplatesVO template) {
    +        QuotaEmailTemplateResponse response = new QuotaEmailTemplateResponse();
    +        response.setTemplateType(template.getTemplateName());
    +        response.setTemplateSubject(template.getTemplateSubject());
    +        response.setTemplateText(template.getTemplateBody());
    +        response.setLocale(template.getLocale());
    +        response.setLastUpdatedOn(template.getLastUpdated());
    +        return response;
    +    }
    +
    +    @Override
    +    public List<QuotaEmailTemplateResponse> listQuotaEmailTemplates(QuotaEmailTemplateListCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        final List<QuotaEmailTemplateResponse> responses = new ArrayList<QuotaEmailTemplateResponse>();
    +        for (final QuotaEmailTemplatesVO template : templates) {
    +            responses.add(createQuotaEmailResponse(template));
    +        }
    +        return responses;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaEmailTemplate(QuotaEmailTemplateUpdateCmd cmd) {
    +        final String templateName = cmd.getTemplateName();
    +        final String templateSubject = StringEscapeUtils.escapeJavaScript(cmd.getTemplateSubject());
    +        final String templateBody = StringEscapeUtils.escapeJavaScript(cmd.getTemplateBody());
    +        final String locale = cmd.getLocale();
    +
    +        final List<QuotaEmailTemplatesVO> templates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(templateName);
    +        if (templates.size() == 1) {
    +            final QuotaEmailTemplatesVO template = templates.get(0);
    +            template.setTemplateSubject(templateSubject);
    +            template.setTemplateBody(templateBody);
    +            if (locale != null) {
    +                template.setLocale(locale);
    +            }
    +            return _quotaEmailTemplateDao.updateQuotaEmailTemplate(template);
    +        }
    +        return false;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("There are no balance entries on or before the requested date.");
    +        }
    +        if (startDate == null) {
    +            startDate = new Date();
    +        }
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaLastBalanceResponse Date=" + entry.getUpdatedOn() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    --- End diff --
    
    Since this is the first release the debug messages have been left purposefully there in order to assist in debugging. These will be removed in later version.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by bhaisaab <gi...@git.apache.org>.
Github user bhaisaab commented on the pull request:

    https://github.com/apache/cloudstack/pull/689#issuecomment-136660371
  
    New PR here: https://github.com/apache/cloudstack/pull/768


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37571366
  
    --- Diff: engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java ---
    @@ -469,4 +478,25 @@ public void removeOldUsageRecords(int days) {
                 txn.close();
             }
         }
    +
    +    @SuppressWarnings("deprecation")
    +    public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        s_logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId);
    --- End diff --
    
    Debug logging statements should be wrapped in a ``if (s_logger.isDebugEnabled())`` check to avoid the overhead of string concatenation when debug logging is not enabled.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37585892
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaStatementCmd.java ---
    @@ -0,0 +1,143 @@
    +//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.Date;
    +import java.util.List;
    +
    +import javax.inject.Inject;
    +
    +import org.apache.log4j.Logger;
    +import org.apache.cloudstack.api.APICommand;
    +import org.apache.cloudstack.api.ApiConstants;
    +import org.apache.cloudstack.api.BaseCmd;
    +import org.apache.cloudstack.api.Parameter;
    +import org.apache.cloudstack.api.response.AccountResponse;
    +import org.apache.cloudstack.api.response.DomainResponse;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.api.response.QuotaStatementResponse;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.api.response.QuotaStatementItemResponse;
    +
    +import com.cloud.user.Account;
    +
    +@APICommand(name = "quotaStatement", responseObject = QuotaStatementItemResponse.class, description = "Create a quota statement", since = "4.2.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
    --- End diff --
    
    The ``since`` attribute says ``4.2.0``, but this API is new for ``4.6.0``.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37716634
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    --- End diff --
    
    The list is for consumption in that method itself. Yes if it was being passed around from that method then the operation or overhead of copying would be useful.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37952242
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    +            vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal ruleusage;
    +            BigDecimal onehourcost;
    +            onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcost=" + onehourcost);
    +            ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal onegbcost;
    +            BigDecimal rawusageingb;
    +            BigDecimal networkusage;
    +            onegbcost = tariff.getCurrencyValue();
    +            // s_logger.info("Quotatariff onegbcost=" + onegbcost);
    +            rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
    +            networkusage = rawusageingb.multiply(onegbcost);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), transferType, networkusage, usageRecord.getStartDate(),
    +                    usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        Account account = _accountDao.findById(accountId);
    +        if (account != null) {
    +            if (account.getState().equals(State.locked)) {
    +                return true; // already locked, no-op
    +            } else if (account.getState().equals(State.enabled)) {
    +                AccountVO acctForUpdate = _accountDao.createForUpdate();
    +                acctForUpdate.setState(State.locked);
    +                success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +            } else {
    +                if (s_logger.isInfoEnabled()) {
    +                    s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
    +                }
    +            }
    +        } else {
    +            s_logger.warn("Failed to lock account " + accountId + ", account not found.");
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return success;
    +    }
    +
    +    public boolean enableAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +        boolean success = false;
    +        AccountVO acctForUpdate = _accountDao.createForUpdate();
    +        acctForUpdate.setState(State.enabled);
    +        acctForUpdate.setNeedsCleanup(false);
    +        success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605231
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDao.java ---
    @@ -0,0 +1,43 @@
    +//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.math.BigDecimal;
    +import java.util.Date;
    +import java.util.List;
    +
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +
    +import com.cloud.utils.db.GenericDao;
    +
    +public interface QuotaBalanceDao extends GenericDao<QuotaBalanceVO, Long> {
    +
    +    void saveQuotaBalance(List<QuotaBalanceVO> credits);
    +
    +    List<QuotaBalanceVO> findCreditBalance(long accountId, long domainId, Date startDate, Date endDate);
    --- End diff --
    
    Will check.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37587233
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    +            } else {
    +                consecutive = false;
    +            }
    +        }
    +
    +        if (quota_activity > 0 && quotaBalance.size() > 0) {
    +            // order is desc last item is the start item
    +            QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
    +            QuotaBalanceVO endItem = quotaBalance.get(0);
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(startItem.getCreditBalance());
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
    +        } else if (quota_activity > 0) {
    +            // order is desc last item is the start item
    +            resp.setStartDate(startDate);
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndDate(endDate);
    +            resp.setEndQuota(new BigDecimal(0).add(lastCredits));
    +        } else {
    +            resp.setStartQuota(new BigDecimal(0));
    +            resp.setEndQuota(new BigDecimal(0));
    +        }
    +        resp.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        resp.setObjectName("balance");
    +        return resp;
    +    }
    +
    +    @Override
    +    public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
    +        if (quotaUsage == null || quotaUsage.size() == 0) {
    +            throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
    +        }
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    --- End diff --
    
    See previous comments about opening and closing a transaction without using it.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605101
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java ---
    @@ -0,0 +1,92 @@
    +// 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.constant;
    +
    +import org.apache.cloudstack.usage.UsageTypes;
    +
    +import java.util.HashMap;
    +
    +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;
    +
    +    private Integer quotaType;
    +    private String quotaName;
    +    private String quotaUnit;
    +    private String description;
    +    private String discriminator;
    +
    +    public QuotaTypes(Integer quotaType, String name, String unit, String description) {
    +        this.quotaType = quotaType;
    +        this.description = description;
    +        this.quotaName = name;
    +        this.quotaUnit = unit;
    +        this.discriminator = "None";
    +    }
    +
    +    public static HashMap<Integer, QuotaTypes> listQuotaTypes() {
    +        final HashMap<Integer, QuotaTypes> quotaTypeList = new HashMap<Integer, QuotaTypes>();
    +        quotaTypeList.put(new Integer(RUNNING_VM), new QuotaTypes(new Integer(RUNNING_VM), "RUNNING_VM", "Compute-Month", "Running Vm Usage"));
    --- End diff --
    
    These are defined in usage server and the idea was to avoid changes to existing code.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37952302
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java ---
    @@ -0,0 +1,471 @@
    +//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 com.cloud.domain.DomainVO;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.UserVO;
    +import com.cloud.user.Account.State;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.DateUtil;
    +import com.cloud.utils.NumbersUtil;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.TransactionLegacy;
    +import com.cloud.utils.exception.CloudRuntimeException;
    +import com.sun.mail.smtp.SMTPMessage;
    +import com.sun.mail.smtp.SMTPSSLTransport;
    +import com.sun.mail.smtp.SMTPTransport;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.commons.lang3.text.StrSubstitutor;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.mail.Authenticator;
    +import javax.mail.Message;
    +import javax.mail.MessagingException;
    +import javax.mail.PasswordAuthentication;
    +import javax.mail.Session;
    +import javax.mail.URLName;
    +import javax.mail.internet.InternetAddress;
    +import javax.naming.ConfigurationException;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Properties;
    +import java.util.concurrent.TimeUnit;
    +
    +@Component
    +@Local(value = QuotaAlertManager.class)
    +public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaAlertManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsage;
    +
    +    private EmailQuotaAlert _emailQuotaAlert;
    +    private boolean _lockAccountEnforcement = false;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    boolean _smtpDebug = false;
    +
    +    int _pid = 0;
    +
    +    public QuotaAlertManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key());
    +        int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25);
    +        String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
    +        boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
    +        String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
    +        String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
    +        String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
    +        _lockAccountEnforcement = configs.get(QuotaConfig.QuotaEnableEnforcement.key()).equalsIgnoreCase("true");
    +        _emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, _smtpDebug);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Alert Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Alert Manager");
    +        }
    +        return true;
    +    }
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public void sendMonthlyStatement() {
    +        Date now = new Date();
    +        Calendar aCalendar = Calendar.getInstance();
    +        aCalendar.add(Calendar.MONTH, -1);
    +        aCalendar.set(Calendar.DATE, 1);
    +        aCalendar.set(Calendar.HOUR, 0);
    +        aCalendar.set(Calendar.MINUTE, 0);
    +        aCalendar.set(Calendar.SECOND, 0);
    +        Date firstDateOfPreviousMonth = aCalendar.getTime();
    +        aCalendar.set(Calendar.DATE, aCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
    +        Date lastDateOfPreviousMonth = aCalendar.getTime(); // actually the first day of this month
    +        //s_logger.info("firstDateOfPreviousMonth" + firstDateOfPreviousMonth + " lastDateOfPreviousMonth" + lastDateOfPreviousMonth);
    +
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            if (quotaAccount.getQuotaBalance() == null) continue; // no quota usage for this account ever, ignore
    +            Date lastStatementDate = quotaAccount.getLastStatementDate();
    +            if (now.getDate() < 6) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                if (lastStatementDate == null) {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    // send statement
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                } else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
    +                    s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
    +                } else {
    +                    BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
    +                    s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
    +                    deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
    +                }
    +            } else {
    +                s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
    +            }
    +
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota STATEMENT email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    @Override
    +    public void checkAndSendQuotaAlertEmails() {
    +        s_logger.info("Running checkAndSendQuotaAlertEmails");
    +        List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>();
    +        final BigDecimal zeroBalance = new BigDecimal(0);
    +        for (final QuotaAccountVO quotaAccount : _quotaAcc.listAll()) {
    +            s_logger.info("checkAndSendQuotaAlertEmails accId=" + quotaAccount.getId());
    +            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
    +            Date balanceDate = quotaAccount.getQuotaBalanceDate();
    +            Date alertDate = quotaAccount.getQuotaAlertDate();
    +            int lockable = quotaAccount.getQuotaEnforce();
    +            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
    +            if (accountBalance != null) {
    +                AccountVO account = _accountDao.findById(quotaAccount.getId());
    +                // s_logger.info("Check id " + account.getId() + " bal="+ accountBalance + " alertDate"+ alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
    +                if (accountBalance.compareTo(zeroBalance) <= 0) {
    +                    if (_lockAccountEnforcement && (lockable == 1)) {
    +                        if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
    +                            lockAccount(account.getId());
    +                        }
    +                    }
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
    +                    }
    +                } else if (accountBalance.compareTo(thresholdBalance) <= 0) {
    +                    if (alertDate == null || (balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1)) {
    +                        deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
    +                    }
    +                }
    +            }
    +        }
    +
    +        for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
    +            s_logger.debug("Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
    +            sendQuotaAlert(emailToBeSent);
    +        }
    +    }
    +
    +    private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
    +        final AccountVO account = emailToBeSent.getAccount();
    +        final BigDecimal balance = emailToBeSent.getQuotaBalance();
    +        final BigDecimal usage = emailToBeSent.getQuotaUsage();
    +        final QuotaConfig.QuotaEmailTemplateTypes emailType = emailToBeSent.getEmailTemplateType();
    +
    +        final List<QuotaEmailTemplatesVO> emailTemplates = _quotaEmailTemplateDao.listAllQuotaEmailTemplates(emailType.toString());
    +        if (emailTemplates != null && emailTemplates.get(0) != null) {
    +            final QuotaEmailTemplatesVO emailTemplate = emailTemplates.get(0);
    +
    +            final DomainVO accountDomain = _domainDao.findByIdIncludingRemoved(account.getDomainId());
    +            final List<UserVO> usersInAccount = _userDao.listByAccount(account.getId());
    +
    +            String userNames = "";
    +            final List<String> emailRecipients = new ArrayList<String>();
    +            for (UserVO user : usersInAccount) {
    +                userNames += String.format("%s <%s>,", user.getUsername(), user.getEmail());
    +                emailRecipients.add(user.getEmail());
    +            }
    +            if (userNames.endsWith(",")) {
    +                userNames = userNames.substring(0, userNames.length() - 1);
    +            }
    +
    +            final Map<String, String> optionMap = new HashMap<String, String>();
    +            optionMap.put("accountName", account.getAccountName());
    +            optionMap.put("accountID", account.getUuid());
    +            optionMap.put("accountUsers", userNames);
    +            optionMap.put("domainName", accountDomain.getName());
    +            optionMap.put("domainID", accountDomain.getUuid());
    +            optionMap.put("quotaBalance", QuotaConfig.QuotaCurrencySymbol.value() + " " + balance.toString());
    +            if (emailType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                optionMap.put("quotaUsage", QuotaConfig.QuotaCurrencySymbol.value() + " " + usage.toString());
    +            }
    +
    +            // s_logger.info("accountName" + account.getAccountName() + "accountID" + account.getUuid() + "accountUsers" + userNames + "domainName" + accountDomain.getName() + "domainID"
    +            // + accountDomain.getUuid());
    +
    +            final StrSubstitutor templateEngine = new StrSubstitutor(optionMap);
    +            final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
    +            final String body = templateEngine.replace(emailTemplate.getTemplateBody());
    +            try {
    +                _emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
    +                emailToBeSent.sentSuccessfully();
    +            } catch (Exception e) {
    +                s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
    +                        account.getUuid(), emailRecipients, e));
    +            }
    +        } else {
    +            s_logger.error(String.format("No quota email template found for type %s, cannot send quota alert email to account %s(%s)", emailType, account.getAccountName(), account.getUuid()));
    +        }
    +    }
    +
    +    class DeferredQuotaEmail {
    +        AccountVO account;
    +        QuotaAccountVO quotaAccount;
    +        QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
    +        BigDecimal quotaUsage;
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, BigDecimal quotaUsage, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = quotaUsage;
    +        }
    +
    +        public DeferredQuotaEmail(AccountVO account, QuotaAccountVO quotaAccount, QuotaConfig.QuotaEmailTemplateTypes emailTemplateType) {
    +            this.account = account;
    +            this.quotaAccount = quotaAccount;
    +            this.emailTemplateType = emailTemplateType;
    +            this.quotaUsage = new BigDecimal(-1);
    +        }
    +
    +        public AccountVO getAccount() {
    +            return account;
    +        }
    +
    +        public BigDecimal getQuotaBalance() {
    +            return quotaAccount.getQuotaBalance();
    +        }
    +
    +        public BigDecimal getQuotaUsage() {
    +            return quotaUsage;
    +        }
    +
    +        public QuotaConfig.QuotaEmailTemplateTypes getEmailTemplateType() {
    +            return emailTemplateType;
    +        }
    +
    +        public void sentSuccessfully() {
    +            if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
    +                quotaAccount.setLastStatementDate(new Date());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +            else {
    +                quotaAccount.setQuotaAlertDate(new Date());
    +                quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
    +                _quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
    +            }
    +        }
    +    };
    +
    +    class EmailQuotaAlert {
    +        private Session _smtpSession;
    +        private final String _smtpHost;
    +        private int _smtpPort = -1;
    +        private boolean _smtpUseAuth = false;
    +        private final String _smtpUsername;
    +        private final String _smtpPassword;
    +        private final String _emailSender;
    +
    +        public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
    +            _smtpHost = smtpHost;
    +            _smtpPort = smtpPort;
    +            _smtpUseAuth = smtpUseAuth;
    +            _smtpUsername = smtpUsername;
    +            _smtpPassword = smtpPassword;
    +            _emailSender = emailSender;
    +
    +            if (_smtpHost != null) {
    +                Properties smtpProps = new Properties();
    +                smtpProps.put("mail.smtp.host", smtpHost);
    +                smtpProps.put("mail.smtp.port", smtpPort);
    +                smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtp.user", smtpUsername);
    +                }
    +
    +                smtpProps.put("mail.smtps.host", smtpHost);
    +                smtpProps.put("mail.smtps.port", smtpPort);
    +                smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
    +                if (smtpUsername != null) {
    +                    smtpProps.put("mail.smtps.user", smtpUsername);
    +                }
    +
    +                if ((smtpUsername != null) && (smtpPassword != null)) {
    +                    _smtpSession = Session.getInstance(smtpProps, new Authenticator() {
    +                        @Override
    +                        protected PasswordAuthentication getPasswordAuthentication() {
    +                            return new PasswordAuthentication(smtpUsername, smtpPassword);
    +                        }
    +                    });
    +                } else {
    +                    _smtpSession = Session.getInstance(smtpProps);
    +                }
    +                _smtpSession.setDebug(smtpDebug);
    +            } else {
    +                _smtpSession = null;
    +            }
    +        }
    +
    +        public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException {
    +            if (_smtpSession != null) {
    +                SMTPMessage msg = new SMTPMessage(_smtpSession);
    +                msg.setSender(new InternetAddress(_emailSender, _emailSender));
    +                msg.setFrom(new InternetAddress(_emailSender, _emailSender));
    +
    +                for (String email : emails) {
    +                    if (email != null && !email.isEmpty()) {
    +                        try {
    +                            InternetAddress address = new InternetAddress(email, email);
    +                            msg.addRecipient(Message.RecipientType.TO, address);
    +                        } catch (Exception pokemon) {
    +                            s_logger.error("Exception in creating address for:" + email, pokemon);
    +                        }
    +                    }
    +                }
    +
    +                msg.setSubject(subject);
    +                msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
    +                msg.setContent(body, "text/html; charset=utf-8");
    +                msg.saveChanges();
    +
    +                SMTPTransport smtpTrans = null;
    +                if (_smtpUseAuth) {
    +                    smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                } else {
    +                    smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
    +                }
    +                smtpTrans.connect();
    +                smtpTrans.sendMessage(msg, msg.getAllRecipients());
    +                smtpTrans.close();
    +            } else {
    +                throw new CloudRuntimeException("Unable to create smtp session.");
    +            }
    +        }
    +    }
    +
    +    public Date startOfNextDay() {
    +        Calendar c = Calendar.getInstance();
    +        c.setTime(new Date());
    +        c.add(Calendar.DATE, 1);
    +        Date dt = c.getTime();
    +        return dt;
    +    }
    +
    +    public static long getDifferenceDays(Date d1, Date d2) {
    +        long diff = d2.getTime() - d1.getTime();
    +        return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
    +    }
    +
    +    protected boolean lockAccount(long accountId) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    --- End diff --
    
    LegacyTranscaction is in use in cloud almost everywhere. While this is not a reason to continue its use. My thinking is that this should be delinked from Quota changes and looked at in the right perspective.
    We should make a joint effort to clear this up.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605531
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java ---
    @@ -0,0 +1,419 @@
    +//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.response;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.User;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.user.dao.UserDao;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.quota.QuotaService;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
    +import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
    +import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.region.RegionManager;
    +import org.apache.commons.lang.StringEscapeUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Collections;
    +import java.util.Comparator;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +@Component
    +@Local(value = QuotaResponseBuilderImpl.class)
    +public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
    +    private static final Logger s_logger = Logger.getLogger(QuotaResponseBuilderImpl.class.getName());
    +
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaCreditsDao _quotaCreditsDao;
    +    @Inject
    +    private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
    +
    +    @Inject
    +    private UserDao _userDao;
    +    @Inject
    +    private QuotaService _quotaService;
    +    @Inject
    +    AccountDao _accountDao;
    +    @Inject
    +    private RegionManager _regionMgr;
    +
    +    @Override
    +    public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
    +        final QuotaTariffResponse response = new QuotaTariffResponse();
    +        response.setUsageType(tariff.getUsageType());
    +        response.setUsageName(tariff.getUsageName());
    +        response.setUsageUnit(tariff.getUsageUnit());
    +        response.setUsageDiscriminator(tariff.getUsageDiscriminator());
    +        response.setTariffValue(tariff.getCurrencyValue());
    +        response.setEffectiveOn(tariff.getEffectiveOn());
    +        response.setDescription(tariff.getDescription());
    +        response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
    +        return response;
    +    }
    +
    +    @Override
    +    public QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate, Date endDate) {
    +        if (quotaBalance.size() == 0) {
    +            new InvalidParameterValueException("The request period does not contain balance entries.");
    +        }
    +        Collections.sort(quotaBalance, new Comparator<QuotaBalanceVO>() {
    +            public int compare(QuotaBalanceVO o1, QuotaBalanceVO o2) {
    +                return o2.getUpdatedOn().compareTo(o1.getUpdatedOn()); // desc
    +            }
    +        });
    +
    +        int quota_activity = quotaBalance.size();
    +        QuotaBalanceResponse resp = new QuotaBalanceResponse();
    +        BigDecimal lastCredits = new BigDecimal(0);
    +        boolean consecutive = true;
    +        for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
    +            QuotaBalanceVO entry = it.next();
    +            s_logger.info("createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
    +            if (entry.getCreditsId() > 0) {
    +                if (consecutive) {
    +                    lastCredits = lastCredits.add(entry.getCreditBalance());
    +                }
    +                resp.addCredits(entry);
    +                it.remove();
    --- End diff --
    
    That is the right way to remove from a list while iterating.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605363
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java ---
    @@ -0,0 +1,138 @@
    +//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 com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaTariffDao.class })
    +public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> implements QuotaTariffDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaTariffDaoImpl.class.getName());
    +
    +    private final SearchBuilder<QuotaTariffVO> searchUsageType;
    +    private final SearchBuilder<QuotaTariffVO> listAllIncludedUsageType;
    +
    +    public QuotaTariffDaoImpl() {
    +        super();
    +        searchUsageType = createSearchBuilder();
    +        searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        searchUsageType.done();
    +
    +        listAllIncludedUsageType = createSearchBuilder();
    +        listAllIncludedUsageType.and("onorbefore", listAllIncludedUsageType.entity().getEffectiveOn(), SearchCriteria.Op.LTEQ);
    +        listAllIncludedUsageType.and("quotatype", listAllIncludedUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        listAllIncludedUsageType.done();
    +    }
    +
    +    @Override
    +    public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            sc.setParameters("quotatype", quotaType);
    +            result = search(sc, filter);
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        if (result.size() > 0) {
    +            //s_logger.info("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType  + " val=" + result.get(0).getCurrencyValue());
    +            return result.get(0);
    +        } else {
    +            //s_logger.info("Missing quota type " + quotaType);
    +            return null;
    +        }
    +    }
    +
    +    @Override
    +    public List<QuotaTariffVO> listAllTariffPlans(final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> tariffs = new ArrayList<QuotaTariffVO>();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            for (Integer quotaType : QuotaTypes.listQuotaTypes().keySet()) {
    +                sc.setParameters("quotatype", quotaType);
    +                List<QuotaTariffVO> result = search(sc, filter);
    +                if (result.size() > 0) {
    +                    tariffs.add(result.get(0));
    +                    s_logger.info("listAllTariffPlans onorbefore" + effectiveDate +  "quota type " + result.get(0).getDescription() + " , effective Date=" + result.get(0).getEffectiveOn() + " val=" + result.get(0).getCurrencyValue());
    +                }
    +            }
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        return tariffs;
    +    }
    +
    +    @Override
    +    public boolean updateQuotaTariff(QuotaTariffVO plan) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); // Switch
    +                                                                                    // to
    +        boolean result = false;
    +        try {
    +            // Usage DB
    +            result = this.update(plan.getId(), plan);
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close(); // Switch back
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37950517
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    --- End diff --
    
    InvalidParameterValueException exception indicates to the user that there is something wrong with the parameters here it indicates issues with date values passed/


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605257
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java ---
    @@ -0,0 +1,201 @@
    +//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.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.Iterator;
    +import java.util.List;
    +
    +import javax.ejb.Local;
    +
    +import org.springframework.stereotype.Component;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.log4j.Logger;
    +
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +@Component
    +@Local(value = { QuotaBalanceDao.class })
    +public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaBalanceDaoImpl.class.getName());
    +
    +    @SuppressWarnings("deprecation")
    +    @Override
    +    public QuotaBalanceVO findLastBalanceEntry(final long accountId, final long domainId, final Date beforeThis) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
    +        Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
    +        SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
    +        sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
    +        sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
    +        sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
    +        sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
    +        List<QuotaBalanceVO> quotab = this.search(sc, filter);
    +        TransactionLegacy.open(opendb).close();
    --- End diff --
    
    check above.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37583025
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java ---
    @@ -0,0 +1,138 @@
    +//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 com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaTariffDao.class })
    +public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> implements QuotaTariffDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaTariffDaoImpl.class.getName());
    +
    +    private final SearchBuilder<QuotaTariffVO> searchUsageType;
    +    private final SearchBuilder<QuotaTariffVO> listAllIncludedUsageType;
    +
    +    public QuotaTariffDaoImpl() {
    +        super();
    +        searchUsageType = createSearchBuilder();
    +        searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        searchUsageType.done();
    +
    +        listAllIncludedUsageType = createSearchBuilder();
    +        listAllIncludedUsageType.and("onorbefore", listAllIncludedUsageType.entity().getEffectiveOn(), SearchCriteria.Op.LTEQ);
    +        listAllIncludedUsageType.and("quotatype", listAllIncludedUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        listAllIncludedUsageType.done();
    +    }
    +
    +    @Override
    +    public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            sc.setParameters("quotatype", quotaType);
    +            result = search(sc, filter);
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        if (result.size() > 0) {
    +            //s_logger.info("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType  + " val=" + result.get(0).getCurrencyValue());
    +            return result.get(0);
    +        } else {
    +            //s_logger.info("Missing quota type " + quotaType);
    --- End diff --
    
    Please remove commented code.  Git provides a better history of code changes without obscurity.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605032
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java ---
    @@ -0,0 +1,92 @@
    +// 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.constant;
    +
    +import org.apache.cloudstack.usage.UsageTypes;
    +
    +import java.util.HashMap;
    +
    +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;
    +
    +    private Integer quotaType;
    +    private String quotaName;
    +    private String quotaUnit;
    +    private String description;
    +    private String discriminator;
    --- End diff --
    
    Accepted.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37949008
  
    --- Diff: plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java ---
    @@ -0,0 +1,296 @@
    +//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 com.cloud.configuration.Config;
    +import com.cloud.domain.dao.DomainDao;
    +import com.cloud.exception.InvalidParameterValueException;
    +import com.cloud.exception.PermissionDeniedException;
    +import com.cloud.user.Account;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.dao.AccountDao;
    +import com.cloud.utils.component.ManagerBase;
    +import com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.api.command.QuotaBalanceCmd;
    +import org.apache.cloudstack.api.command.QuotaCreditsCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
    +import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
    +import org.apache.cloudstack.api.command.QuotaStatementCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffListCmd;
    +import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
    +import org.apache.cloudstack.api.response.QuotaResponseBuilder;
    +import org.apache.cloudstack.context.CallContext;
    +import org.apache.cloudstack.framework.config.ConfigKey;
    +import org.apache.cloudstack.framework.config.Configurable;
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaConfig;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaService.class)
    +public class QuotaServiceImpl extends ManagerBase implements QuotaService, Configurable, QuotaConfig {
    +    private static final Logger s_logger = Logger.getLogger(QuotaServiceImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private DomainDao _domainDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private QuotaResponseBuilder _respBldr;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    public QuotaServiceImpl() {
    +        super();
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +        String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString());
    +        String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString());
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +        return true;
    +    }
    +
    +    @Override
    +    public List<Class<?>> getCommands() {
    +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
    +        if (!isQuotaServiceEnabled()) {
    +            return cmdList;
    +        }
    +        cmdList.add(QuotaStatementCmd.class);
    +        cmdList.add(QuotaBalanceCmd.class);
    +        cmdList.add(QuotaTariffListCmd.class);
    +        cmdList.add(QuotaTariffUpdateCmd.class);
    +        cmdList.add(QuotaCreditsCmd.class);
    +        cmdList.add(QuotaEmailTemplateListCmd.class);
    +        cmdList.add(QuotaEmailTemplateUpdateCmd.class);
    +        return cmdList;
    +    }
    +
    +    @Override
    +    public String getConfigComponentName() {
    +        return "QUOTA-PLUGIN";
    +    }
    +
    +    @Override
    +    public ConfigKey<?>[] getConfigKeys() {
    +        return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, QuotaSmtpUser,
    +                QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender };
    +    }
    +
    +    public Boolean isQuotaServiceEnabled() {
    +        return QuotaPluginEnabled.value();
    +    }
    +
    +    @Override
    +    public List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
    +
    +        Account userAccount = null;
    +        Account caller = CallContext.current().getCallingAccount();
    +
    +        // if accountId is not specified, use accountName and domainId
    +        if ((accountId == null) && (accountName != null) && (domainId != null)) {
    +            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
    +                Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
    +                List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
    +                if (accounts.size() > 0) {
    +                    userAccount = accounts.get(0);
    +                }
    +                if (userAccount != null) {
    +                    accountId = userAccount.getId();
    +                } else {
    +                    throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
    +                }
    +            } else {
    +                throw new PermissionDeniedException("Invalid Domain Id or Account");
    +            }
    +        }
    +        TransactionLegacy.open(opendb).close();
    +
    +        startDate = startDate == null ? new Date() : startDate;
    +
    +        if (endDate == null) {
    +            // adjust start date to end of day as there is no end date
    +            Date adjustedStartDate = computeAdjustedTime(_respBldr.startOfNextDay(startDate));
    +            s_logger.debug("getQuotaBalance1: Getting quota balance records for account: " + accountId + ", domainId: " + domainId + ", on or before " + adjustedStartDate);
    +            List<QuotaBalanceVO> qbrecords = _quotaBalanceDao.lastQuotaBalanceVO(accountId, domainId, adjustedStartDate);
    +            s_logger.info("Found records size=" + qbrecords.size());
    +            if (qbrecords.size() == 0) {
    +                throw new InvalidParameterValueException("Incorrect Date there are no quota records before this date " + adjustedStartDate);
    --- End diff --
    
    This error does not seem to pertain to a parameter value. As such, the type should be changed to something more appropriate such as IllegalStateException.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by abhinandanprateek <gi...@git.apache.org>.
Github user abhinandanprateek commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37605350
  
    --- Diff: framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java ---
    @@ -0,0 +1,138 @@
    +//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 com.cloud.utils.db.Filter;
    +import com.cloud.utils.db.GenericDaoBase;
    +import com.cloud.utils.db.SearchBuilder;
    +import com.cloud.utils.db.SearchCriteria;
    +import com.cloud.utils.db.TransactionLegacy;
    +
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +
    +import java.util.ArrayList;
    +import java.util.Date;
    +import java.util.List;
    +
    +@Component
    +@Local(value = { QuotaTariffDao.class })
    +public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> implements QuotaTariffDao {
    +    private static final Logger s_logger = Logger.getLogger(QuotaTariffDaoImpl.class.getName());
    +
    +    private final SearchBuilder<QuotaTariffVO> searchUsageType;
    +    private final SearchBuilder<QuotaTariffVO> listAllIncludedUsageType;
    +
    +    public QuotaTariffDaoImpl() {
    +        super();
    +        searchUsageType = createSearchBuilder();
    +        searchUsageType.and("usage_type", searchUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        searchUsageType.done();
    +
    +        listAllIncludedUsageType = createSearchBuilder();
    +        listAllIncludedUsageType.and("onorbefore", listAllIncludedUsageType.entity().getEffectiveOn(), SearchCriteria.Op.LTEQ);
    +        listAllIncludedUsageType.and("quotatype", listAllIncludedUsageType.entity().getUsageType(), SearchCriteria.Op.EQ);
    +        listAllIncludedUsageType.done();
    +    }
    +
    +    @Override
    +    public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        List<QuotaTariffVO> result = null;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
    +            final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
    +            sc.setParameters("onorbefore", effectiveDate);
    +            sc.setParameters("quotatype", quotaType);
    +            result = search(sc, filter);
    +        } finally {
    +            txn.close();
    +        }
    +        // Switch back
    +        TransactionLegacy.open(opendb).close();
    +        if (result.size() > 0) {
    +            //s_logger.info("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType  + " val=" + result.get(0).getCurrencyValue());
    +            return result.get(0);
    +        } else {
    +            //s_logger.info("Missing quota type " + quotaType);
    --- End diff --
    
    ok


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951028
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] cloudstack pull request: Quota master

Posted by jburwell <gi...@git.apache.org>.
Github user jburwell commented on a diff in the pull request:

    https://github.com/apache/cloudstack/pull/689#discussion_r37951176
  
    --- Diff: usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java ---
    @@ -0,0 +1,465 @@
    +//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 com.cloud.usage.UsageVO;
    +import com.cloud.usage.dao.UsageDao;
    +import com.cloud.user.Account;
    +//import com.cloud.user.AccountManager;
    +import com.cloud.user.AccountVO;
    +import com.cloud.user.Account.State;
    +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.TransactionLegacy;
    +
    +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
    +import org.apache.cloudstack.quota.constant.QuotaTypes;
    +import org.apache.cloudstack.quota.dao.QuotaAccountDao;
    +import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
    +import org.apache.cloudstack.quota.dao.QuotaTariffDao;
    +import org.apache.cloudstack.quota.dao.QuotaUsageDao;
    +import org.apache.cloudstack.quota.vo.QuotaAccountVO;
    +import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
    +import org.apache.cloudstack.quota.vo.QuotaTariffVO;
    +import org.apache.cloudstack.quota.vo.QuotaUsageVO;
    +import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
    +import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
    +import org.apache.cloudstack.utils.usage.UsageUtils;
    +import org.apache.log4j.Logger;
    +import org.springframework.stereotype.Component;
    +
    +import javax.ejb.Local;
    +import javax.inject.Inject;
    +import javax.naming.ConfigurationException;
    +
    +import java.math.BigDecimal;
    +import java.math.RoundingMode;
    +import java.util.ArrayList;
    +import java.util.Calendar;
    +import java.util.Date;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.TimeZone;
    +
    +@Component
    +@Local(value = QuotaManager.class)
    +public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
    +    private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
    +
    +    @Inject
    +    private AccountDao _accountDao;
    +    @Inject
    +    private QuotaAccountDao _quotaAcc;
    +    @Inject
    +    private UsageDao _usageDao;
    +    @Inject
    +    private QuotaTariffDao _quotaTariffDao;
    +    @Inject
    +    private QuotaUsageDao _quotaUsageDao;
    +    @Inject
    +    private ServiceOfferingDao _serviceOfferingDao;
    +    @Inject
    +    private QuotaBalanceDao _quotaBalanceDao;
    +    @Inject
    +    private ConfigurationDao _configDao;
    +
    +    private TimeZone _usageTimezone;
    +    private int _aggregationDuration = 0;
    +
    +    final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
    +    final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
    +    final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
    +
    +    int _pid = 0;
    +
    +    public QuotaManagerImpl() {
    +        super();
    +    }
    +
    +    private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
    +        for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
    +            dbParams.put(param.getKey(), (String) param.getValue());
    +        }
    +    }
    +
    +    @Override
    +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
    +        super.configure(name, params);
    +
    +        Map<String, String> configs = _configDao.getConfiguration(params);
    +
    +        if (params != null) {
    +            mergeConfigs(configs, params);
    +        }
    +
    +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
    +        String timeZoneStr = configs.get("usage.aggregation.timezone");
    +
    +        if (timeZoneStr == null) {
    +            timeZoneStr = "GMT";
    +        }
    +        _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
    +
    +        _aggregationDuration = Integer.parseInt(aggregationRange);
    +        if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
    +            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
    +            _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
    +        }
    +        s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
    +
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean start() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Starting Quota Manager");
    +        }
    +        _pid = Integer.parseInt(System.getProperty("pid"));
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean stop() {
    +        if (s_logger.isInfoEnabled()) {
    +            s_logger.info("Stopping Quota Manager");
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean calculateQuotaUsage() {
    +        final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
    +        boolean jobResult = false;
    +        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
    +        try {
    +            // get all the active accounts for which there is usage
    +            List<AccountVO> accounts = _accountDao.listAll();
    +            for (AccountVO account : accounts) { // START ACCOUNT
    +                Pair<List<? extends UsageVO>, Integer> usageRecords = null;
    +                List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
    +                do {
    +                    s_logger.info("Account =" + account.getAccountName());
    +                    usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
    +                    s_logger.debug("Usage records found " + usageRecords.second());
    +                    for (UsageVO usageRecord : usageRecords.first()) {
    +                        BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
    +                        switch (usageRecord.getUsageType()) {
    +                        case QuotaTypes.RUNNING_VM:
    +                            quotalistforaccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.ALLOCATED_VM:
    +                            quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
    +                            break;
    +                        case QuotaTypes.SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.TEMPLATE:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.TEMPLATE));
    +                            break;
    +                        case QuotaTypes.ISO:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.ISO));
    +                            break;
    +                        case QuotaTypes.VOLUME:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VOLUME));
    +                            break;
    +                        case QuotaTypes.VM_SNAPSHOT:
    +                            quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, QuotaTypes.VM_SNAPSHOT));
    +                            break;
    +                        case QuotaTypes.LOAD_BALANCER_POLICY:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.LOAD_BALANCER_POLICY));
    +                            break;
    +                        case QuotaTypes.PORT_FORWARDING_RULE:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.PORT_FORWARDING_RULE));
    +                            break;
    +                        case QuotaTypes.IP_ADDRESS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.IP_ADDRESS));
    +                            break;
    +                        case QuotaTypes.NETWORK_OFFERING:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.NETWORK_OFFERING));
    +                            break;
    +                        case QuotaTypes.SECURITY_GROUP:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.SECURITY_GROUP));
    +                            break;
    +                        case QuotaTypes.VPN_USERS:
    +                            quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, QuotaTypes.VPN_USERS));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_RECEIVED:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, QuotaTypes.NETWORK_BYTES_RECEIVED));
    +                            break;
    +                        case QuotaTypes.NETWORK_BYTES_SENT:
    +                            quotalistforaccount.add(updateQuotaNetwork(usageRecord, 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;
    +                        }
    +                    }
    +                } while ((usageRecords != null) && !usageRecords.first().isEmpty());
    +                // list of quotas for this account
    +                s_logger.info("Quota entries size = " + quotalistforaccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
    +                if (quotalistforaccount.size() > 0) { // balance to be processed
    +                    quotalistforaccount.add(new QuotaUsageVO());
    +                    Date startDate = quotalistforaccount.get(0).getStartDate();
    +                    Date endDate = quotalistforaccount.get(0).getEndDate();
    +                    BigDecimal aggrUsage = new BigDecimal(0);
    +                    for (QuotaUsageVO entry : quotalistforaccount) {
    +                        if (startDate.compareTo(entry.getStartDate()) != 0) {
    +                            QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
    +                            Date lastbalancedate;
    +                            if (lastrealbalanceentry != null) {
    +                                lastbalancedate = lastrealbalanceentry.getUpdatedOn();
    +                                aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
    +                            } else {
    +                                lastbalancedate = new Date(0);
    +                            }
    +
    +                            List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
    +                            for (QuotaBalanceVO credit : creditsrcvd) {
    +                                aggrUsage = aggrUsage.add(credit.getCreditBalance());
    +                            }
    +
    +                            QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
    +                            // s_logger.info("Balance entry=" + aggrUsage + " on Date=" + endDate);
    +                            _quotaBalanceDao.persist(newbalance);
    +                            aggrUsage = new BigDecimal(0);
    +                        }
    +                        startDate = entry.getStartDate();
    +                        endDate = entry.getEndDate();
    +                        aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
    +                    }
    +                    // update is quota_accounts
    +                    QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
    +                    s_logger.info("Updating quota account bal=" + aggrUsage + " date=" + endDate);
    +                    if (quota_account == null) {
    +                        quota_account = new QuotaAccountVO(account.getAccountId());
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.persist(quota_account);
    +                    } else {
    +                        quota_account.setQuotaBalance(aggrUsage);
    +                        quota_account.setQuotaBalanceDate(endDate);
    +                        _quotaAcc.update(account.getAccountId(), quota_account);
    +                    }
    +                }// balance processed
    +            } // END ACCOUNT
    +            jobResult = true;
    +        } catch (Exception e) {
    +            s_logger.error("Quota Manager error", e);
    +            e.printStackTrace();
    +        } finally {
    +            txn.close();
    +        }
    +        TransactionLegacy.open(opendb).close();
    +        return jobResult;
    +    }
    +
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal quotaUsgage;
    +            BigDecimal onehourcostpergb;
    +            BigDecimal noofgbinuse;
    +            onehourcostpergb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, 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.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(), quotaUsgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +        }
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quota_usage;
    +    }
    +
    +    @DB
    +    private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
    +        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()
    +        // + ", aggrR=" + aggregationRatio);
    +        // get service offering details
    +        ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
    +        rawusage = new BigDecimal(usageRecord.getRawUsage());
    +
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
    +            onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER, cpuquotausgage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
    +            onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE, speedquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
    +            onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +        tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            vmusage = rawusage.multiply(onehourcostforvmusage);
    +            quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
    +                    usageRecord.getStartDate(), usageRecord.getEndDate());
    +            _quotaUsageDao.persist(quota_usage);
    +            quotalist.add(quota_usage);
    +        }
    +
    +        usageRecord.setQuotaCalculated(1);
    +        _usageDao.persist(usageRecord);
    +        return quotalist;
    +    }
    +
    +    @DB
    +    private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
    +        QuotaUsageVO quota_usage = null;
    +        QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
    +        if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
    +            BigDecimal vmusage;
    +            BigDecimal onehourcostforvmusage;
    +            onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
    +            // s_logger.info("Quotatariff onehourcostforvmusage=" +
    +            // onehourcostforvmusage);
    --- End diff --
    
    Please remove commented code as it builds up cruft.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---