diff --git a/usage/src/main/java/com/cloud/usage/parser/BackupUsageParser.java b/usage/src/main/java/com/cloud/usage/parser/BackupUsageParser.java index bacf706cf179..531bc4bcd279 100644 --- a/usage/src/main/java/com/cloud/usage/parser/BackupUsageParser.java +++ b/usage/src/main/java/com/cloud/usage/parser/BackupUsageParser.java @@ -17,6 +17,7 @@ package com.cloud.usage.parser; +import java.text.DecimalFormat; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -25,6 +26,7 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.utils.Pair; import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.usage.UsageTypes; import org.apache.log4j.Logger; @@ -68,38 +70,91 @@ public static boolean parse(AccountVO account, Date startDate, Date endDate) { return true; } - final Map vmUsageMap = new HashMap<>(); + Map> bkpUsageMap = new HashMap>(); + final Map vmUsageMap = new HashMap<>(); for (final UsageBackupVO usageBackup : usageBackups) { final Long vmId = usageBackup.getVmId(); final Long zoneId = usageBackup.getZoneId(); + String key = vmId + "-" + usageBackup.getBackupOfferingId() + "-" + zoneId; final Long offeringId = usageBackup.getBackupOfferingId(); - if (vmUsageMap.get(vmId) == null) { - vmUsageMap.put(vmId, new BackupUsageParser.BackupInfo(new Backup.Metric(0L, 0L), zoneId, vmId, offeringId)); + if (vmUsageMap.get(key) == null) { + vmUsageMap.put(key, new BackupUsageParser.BackupInfo(new Backup.Metric(0L, 0L), zoneId, vmId, offeringId)); } - final Backup.Metric metric = vmUsageMap.get(vmId).getMetric(); + final Backup.Metric metric = vmUsageMap.get(key).getMetric(); metric.setBackupSize(metric.getBackupSize() + usageBackup.getSize()); metric.setDataSize(metric.getDataSize() + usageBackup.getProtectedSize()); + long duration = getUsageDuration(usageBackup, startDate, endDate); + updateBackupUsageData(bkpUsageMap, key, vmId, duration); } - for (final BackupInfo backupInfo : vmUsageMap.values()) { - final Long vmId = backupInfo.getVmId(); - final Long zoneId = backupInfo.getZoneId(); - final Long offeringId = backupInfo.getOfferingId(); - final Double rawUsage = (double) backupInfo.getMetric().getBackupSize(); - final Double sizeGib = rawUsage / (1024.0 * 1024.0 * 1024.0); - final String description = String.format("Backup usage VM ID: %d", vmId); - final String usageDisplay = String.format("%.4f GiB", sizeGib); - - final UsageVO usageRecord = - new UsageVO(zoneId, account.getAccountId(), account.getDomainId(), description, usageDisplay, - UsageTypes.BACKUP, rawUsage, vmId, null, offeringId, null, vmId, - backupInfo.getMetric().getBackupSize(), backupInfo.getMetric().getDataSize(), startDate, endDate); - s_usageDao.persist(usageRecord); + for (String bkpIdKey : bkpUsageMap.keySet()) { + Pair bkptimeInfo = bkpUsageMap.get(bkpIdKey); + long useTime = bkptimeInfo.second(); + + // Only create a usage record if we have a runningTime of bigger than zero. + if (useTime > 0L) { + BackupUsageParser.BackupInfo info = vmUsageMap.get(bkpIdKey); + createUsageRecord(UsageTypes.BACKUP, account, useTime, startDate, endDate, info); + } } return true; } + private static void createUsageRecord(int type, AccountVO account, long runningTime, Date startDate, Date endDate, BackupInfo backupInfo) { + // Our smallest increment is hourly for now + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Total running time " + runningTime + "ms"); + } + final Long vmId = backupInfo.getVmId(); + final Long zoneId = backupInfo.getZoneId(); + final Long offeringId = backupInfo.getOfferingId(); + float usage = runningTime / 1000f / 60f / 60f; + + DecimalFormat dFormat = new DecimalFormat("#.######"); + String usageDisplay = dFormat.format(usage); + + final String description = String.format("Backup usage VM ID: %d", vmId); + + final UsageVO usageRecord = + new UsageVO(zoneId, account.getAccountId(), account.getDomainId(), description, usageDisplay + " Hrs", + type, (double) usage, vmId, null, offeringId, null, vmId, + backupInfo.getMetric().getBackupSize(), backupInfo.getMetric().getDataSize(), startDate, endDate); + s_usageDao.persist(usageRecord); + } + + private static long getUsageDuration(UsageBackupVO usageBackup, Date startDate, Date endDate) { + Date backupCreateDate = usageBackup.getCreated(); + Date backupDeleteDate = usageBackup.getRemoved(); + + if ((backupDeleteDate == null) || backupDeleteDate.after(endDate)) { + backupDeleteDate = endDate; + } + + // clip the start date to the beginning of our aggregation range if the vm has been running for a while + if (backupCreateDate.before(startDate)) { + backupCreateDate = startDate; + } + + if (backupCreateDate.after(endDate)) { + //Ignore records created after endDate + return 0L; + } + return (backupDeleteDate.getTime() - backupCreateDate.getTime()) + 1; // make sure this is an inclusive check for milliseconds (i.e. use n - m + 1 to find total number of millis to charge) + } + + private static void updateBackupUsageData(Map> usageDataMap, String key, long vmId, long duration) { + Pair backupUsageInfo = usageDataMap.get(key); + if (backupUsageInfo == null) { + backupUsageInfo = new Pair(vmId, duration); + } else { + Long runningTime = backupUsageInfo.second(); + runningTime = runningTime + duration; + backupUsageInfo = new Pair(backupUsageInfo.first(), runningTime); + } + usageDataMap.put(key, backupUsageInfo); + } + static class BackupInfo { Backup.Metric metric; Long zoneId;