quota: implement quota email alert methods/classes and template engine to substitute variable options

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2015-07-24 12:40:33 +05:30
parent 9afd5791be
commit 355b2e6330
4 changed files with 178 additions and 4 deletions

View File

@ -127,8 +127,13 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] { QuotaPluginEnabled, QuotaPeriodType, QuotaPeriod, QuotaGenerateActivity, QuotaEmailRecordOutgoing, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaLimitCritical,
QuotaLimitIncremental, QuotaSmtpHost, QuotaSmtpTimeout, QuotaSmtpUser, QuotaSmtpPassword, QuotaSmtpPort, QuotaSmtpAuthType };
return new ConfigKey<?>[] {
QuotaPluginEnabled,QuotaPeriodType, QuotaPeriod,
QuotaGenerateActivity, QuotaEmailRecordOutgoing, QuotaEnableEnforcement,
QuotaCurrencySymbol, QuotaLimitCritical, QuotaLimitIncremental,
QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout,
QuotaSmtpUser, QuotaSmtpPassword,
QuotaSmtpAuthType, QuotaSmtpSender };
}
@Override

View File

@ -63,7 +63,10 @@ public interface QuotaConfig {
"Quota SMTP port", true);
public static final ConfigKey<String> QuotaSmtpAuthType = new ConfigKey<String>("Advanced", String.class, "quota.usage.smtp.useAuth", "",
"Quota SMTP authorization type", true);
"If true, use secure SMTP authentication when sending emails.", true);
public static final ConfigKey<String> QuotaSmtpSender = new ConfigKey<String>("Advanced", String.class, "quota.usage.smtp.sender", "",
"Sender of quota alert email (will be in the From header of the email)", true);
enum QuotaEmailTemplateTypes {
QUOTA_LOW, QUOTA_EMPTY

View File

@ -17,37 +17,61 @@
package org.apache.cloudstack.quota.job;
import com.cloud.configuration.Config;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.usage.UsageVO;
import com.cloud.usage.dao.UsageDao;
import com.cloud.user.AccountVO;
import com.cloud.user.UserVO;
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.Pair;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.DB;
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.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
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.utils.usage.UsageUtils;
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.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;
@Component
@ -58,12 +82,18 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
@Inject
private AccountDao _accountDao;
@Inject
private UserDao _userDao;
@Inject
private DomainDao _domainDao;
@Inject
private UsageDao _usageDao;
@Inject
private QuotaTariffDao _quotaTariffDao;
@Inject
private QuotaUsageDao _quotaUsageDao;
@Inject
private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
@Inject
private ServiceOfferingDao _serviceOfferingDao;
@Inject
private QuotaBalanceDao _quotaBalanceDao;
@ -73,6 +103,8 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
private TimeZone _usageTimezone;
private int _aggregationDuration = 0;
private EmailQuotaAlert _emailQuotaAlert;
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);
@ -97,6 +129,17 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
_aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
}
s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
final String smtpHost = QuotaConfig.QuotaSmtpHost.value();
int smtpPort = NumbersUtil.parseInt(QuotaConfig.QuotaSmtpPort.value(), 25);
String useAuthStr = QuotaConfig.QuotaSmtpAuthType.value();
boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
String smtpUsername = QuotaConfig.QuotaSmtpUser.value();
String smtpPassword = QuotaConfig.QuotaSmtpPassword.value();
String emailSender = QuotaConfig.QuotaSmtpSender.value();
boolean smtpDebug = false;
_emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, smtpDebug);
return true;
}
@ -113,7 +156,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
do {
s_logger.info("Account =" + account.getAccountName());
usageRecords = _usageDao.getUsageRecords(account.getAccountId(), account.getDomainId());
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);
@ -363,4 +406,126 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
return quota_usage;
}
private void sendQuotaAlert(QuotaConfig.QuotaEmailTemplateTypes emailType, AccountVO account, QuotaBalanceVO balance) {
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());
}
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.getCreditBalance().toString());
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);
} catch (Exception e) {
s_logger.error(String.format("Unable to send quota alert email to account %s (%s) due to error: %s", account.getAccountName(), account.getUuid(), 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 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 send quota alert email");
}
}
}
}

View File

@ -623,6 +623,7 @@ var g_quotaCurrency = '';
templateBodyTextArea.appendTo(emailTemplateForm);
saveTemplateButton.appendTo(emailTemplateForm);
$('<hr>').appendTo(emailTemplateForm);
$('<p>').html("These options can be used in template as ${variable}: quotaBalance, accountName, accountId, accountUsers, domainName, domainId").appendTo(emailTemplateForm);
emailTemplateForm.appendTo(manageTemplatesView);
manageTemplatesView.appendTo($node);