package org.openstack.atlas.usagerefactor; import org.apache.commons.logging.LogFactory; import org.openstack.atlas.service.domain.entities.LoadBalancer; import org.openstack.atlas.service.domain.entities.Usage; import org.openstack.atlas.service.domain.events.UsageEvent; import org.openstack.atlas.service.domain.exceptions.EntityNotFoundException; import org.openstack.atlas.service.domain.pojos.LbIdAccountId; import org.openstack.atlas.service.domain.repository.UsageRepository; import org.openstack.atlas.service.domain.services.LoadBalancerService; import org.openstack.atlas.service.domain.usage.BitTag; import org.openstack.atlas.service.domain.usage.BitTags; import org.openstack.atlas.service.domain.usage.entities.LoadBalancerMergedHostUsage; import org.openstack.atlas.usagerefactor.helpers.RollupUsageHelper; import org.openstack.atlas.util.common.CalendarUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.*; import static org.openstack.atlas.service.domain.events.UsageEvent.*; @Component public class UsageRollupProcessorImpl implements UsageRollupProcessor { final org.apache.commons.logging.Log LOG = LogFactory.getLog(UsageRollupProcessorImpl.class); @Autowired private UsageRepository usageRepository; @Autowired private LoadBalancerService loadbalancerService; private Map<Integer, Integer> tagsCache = new HashMap<Integer, Integer>(); private Map<Integer, Integer> numVipsCache = new HashMap<Integer, Integer>(); private Set<Integer> suspendedLbsCache = new HashSet<Integer>(); @Override public Map<LbIdAccountId, List<LoadBalancerMergedHostUsage>> groupUsagesByLbIdAccountId(List<LoadBalancerMergedHostUsage> lbMergedHostUsages) { Map<LbIdAccountId, List<LoadBalancerMergedHostUsage>> usagesByLbId = new HashMap<LbIdAccountId, List<LoadBalancerMergedHostUsage>>(); for (LoadBalancerMergedHostUsage LoadBalancerMergedHostUsage : lbMergedHostUsages) { List<LoadBalancerMergedHostUsage> usageList; LbIdAccountId key = new LbIdAccountId(LoadBalancerMergedHostUsage.getLoadbalancerId(), LoadBalancerMergedHostUsage.getAccountId()); if (!usagesByLbId.containsKey(key)) { usageList = new ArrayList<LoadBalancerMergedHostUsage>(); usagesByLbId.put(key, usageList); } usageList = usagesByLbId.get(key); usageList.add(LoadBalancerMergedHostUsage); } return usagesByLbId; } @Override public List<Usage> processRecords(List<LoadBalancerMergedHostUsage> lbMergedHostUsages, Calendar hourToProcess, Set<LbIdAccountId> lbsActiveDuringHour) { List<Usage> processedRecords = new ArrayList<Usage>(); Map<LbIdAccountId, List<LoadBalancerMergedHostUsage>> usagesByLbId = groupUsagesByLbIdAccountId(lbMergedHostUsages); for (LbIdAccountId lbActiveDuringHour : lbsActiveDuringHour) { if (!usagesByLbId.containsKey(lbActiveDuringHour)) { usagesByLbId.put(lbActiveDuringHour, new ArrayList<LoadBalancerMergedHostUsage>()); } } for (LbIdAccountId lbIdAccountId : usagesByLbId.keySet()) { List<LoadBalancerMergedHostUsage> lbMergedHostRecordsForLoadBalancer = usagesByLbId.get(lbIdAccountId); List<Usage> processedRecordsForLb = processRecordsForLb(lbIdAccountId.getAccountId(), lbIdAccountId.getLbId(), lbMergedHostRecordsForLoadBalancer, hourToProcess); processedRecords.addAll(processedRecordsForLb); } return processedRecords; } /* @param lbMergedHostUsageRecordsForLoadBalancer: Expected to be in order by pollTime. */ @Override public List<Usage> processRecordsForLb(Integer accountId, Integer lbId, List<LoadBalancerMergedHostUsage> lbMergedHostUsageRecordsForLoadBalancer, Calendar hourToProcess) { List<Usage> processedRecords = new ArrayList<Usage>(); Calendar previousHour; Calendar hourToStop; boolean previousHourRecordExists = false; boolean isFirstRecordOfHourToProcess = true; LoadBalancerMergedHostUsage mostRecentPreviousRecord = null; hourToProcess = CalendarUtils.stripOutMinsAndSecs(hourToProcess); previousHour = CalendarUtils.stripOutMinsAndSecs(hourToProcess); previousHour.add(Calendar.HOUR, -1); hourToStop = CalendarUtils.stripOutMinsAndSecs(hourToProcess); hourToStop.add(Calendar.HOUR, 1); if (lbMergedHostUsageRecordsForLoadBalancer == null || lbMergedHostUsageRecordsForLoadBalancer.isEmpty()) { Usage zeroedOutUsage = initializeRecordForLb(accountId, lbId, hourToProcess, hourToStop); if (zeroedOutUsage.getEventType() == null && suspendedLbsCache.contains(lbId)) { zeroedOutUsage.setEventType(SUSPENDED_LOADBALANCER.name()); } processedRecords.add(zeroedOutUsage); return processedRecords; } final LoadBalancerMergedHostUsage firstRecordInList = lbMergedHostUsageRecordsForLoadBalancer.get(0); if (firstRecordInList.getEventType() != null && firstRecordInList.getEventType().equals(CREATE_LOADBALANCER) && firstRecordInList.getPollTime().compareTo(hourToStop) >= 0) { return processedRecords; } Usage newRecordForLb = initializeRecordForLb(firstRecordInList, hourToProcess, hourToStop); for (LoadBalancerMergedHostUsage lbMergedHostUsage : lbMergedHostUsageRecordsForLoadBalancer) { Calendar pollTime = lbMergedHostUsage.getPollTime(); boolean equalToHourToProcess = pollTime.equals(hourToProcess); UsageEvent event = lbMergedHostUsage.getEventType(); boolean recordHasEvent = event != null; boolean withinHourToProcess = CalendarUtils.isBetween(pollTime, hourToProcess, hourToStop, true); boolean withinPreviousHour = CalendarUtils.isBetween(pollTime, previousHour, hourToProcess, false); boolean allowedToAddBandwidth = true; if (pollTime.before(hourToProcess)) { if (withinPreviousHour) { previousHourRecordExists = true; mostRecentPreviousRecord = lbMergedHostUsage; if (mostRecentPreviousRecord.getEventType() != null) { if (mostRecentPreviousRecord.getEventType().equals(DELETE_LOADBALANCER)) { return processedRecords; } else if (mostRecentPreviousRecord.getEventType().equals(SUSPEND_LOADBALANCER) || mostRecentPreviousRecord.getEventType().equals(SUSPENDED_LOADBALANCER)) { newRecordForLb.setEventType(SUSPENDED_LOADBALANCER.name()); suspendedLbsCache.add(lbId); } else { newRecordForLb.setEventType(null); } if (mostRecentPreviousRecord.getEventType().equals(UNSUSPEND_LOADBALANCER)) { suspendedLbsCache.remove(lbId); } if (mostRecentPreviousRecord.getEventType().equals(CREATE_VIRTUAL_IP) || mostRecentPreviousRecord.getEventType().equals(DELETE_VIRTUAL_IP)) { newRecordForLb.setNumVips(lbMergedHostUsage.getNumVips()); numVipsCache.put(lbId, lbMergedHostUsage.getNumVips()); } } } newRecordForLb.setTags(lbMergedHostUsage.getTagsBitmask()); continue; } if ((!previousHourRecordExists && isFirstRecordOfHourToProcess) || equalToHourToProcess) { newRecordForLb = initializeRecordForLb(lbMergedHostUsage, hourToProcess, hourToStop); allowedToAddBandwidth = false; } if (withinHourToProcess) { RollupUsageHelper.calculateAndSetAverageConcurrentConnections(newRecordForLb, lbMergedHostUsage); if (allowedToAddBandwidth) { RollupUsageHelper.calculateAndSetBandwidth(newRecordForLb, lbMergedHostUsage); } if (equalToHourToProcess && newRecordForLb.getNumberOfPolls() > 0) { newRecordForLb.setNumberOfPolls(newRecordForLb.getNumberOfPolls() - 1); } if (recordHasEvent) { if (event.equals(SUSPEND_LOADBALANCER) || event.equals(SUSPENDED_LOADBALANCER)) { suspendedLbsCache.add(lbId); } else if (event.equals(UNSUSPEND_LOADBALANCER)) { suspendedLbsCache.remove(lbId); } if (event.equals(CREATE_LOADBALANCER) || equalToHourToProcess) { processedRecords.clear(); newRecordForLb.setStartTime(pollTime); newRecordForLb.setEventType(event.name()); newRecordForLb.setAverageConcurrentConnections(0.0); newRecordForLb.setAverageConcurrentConnectionsSsl(0.0); newRecordForLb.setIncomingTransfer(0l); newRecordForLb.setIncomingTransferSsl(0l); newRecordForLb.setOutgoingTransfer(0l); newRecordForLb.setOutgoingTransferSsl(0l); if (!equalToHourToProcess) { newRecordForLb.setNumberOfPolls(1); } } else if (event.equals(SUSPENDED_LOADBALANCER)) { if (previousHourRecordExists && mostRecentPreviousRecord.getEventType() != null && (mostRecentPreviousRecord.getEventType().equals(SUSPEND_LOADBALANCER) || mostRecentPreviousRecord.getEventType().equals(SUSPENDED_LOADBALANCER))) { newRecordForLb.setEventType(SUSPENDED_LOADBALANCER.name()); newRecordForLb.setEndTime(hourToStop); newRecordForLb.setNumberOfPolls(0); } } else { if (isFirstRecordOfHourToProcess && !previousHourRecordExists) { int tags = getTags(lbId); int numVips = getNumVips(lbId); newRecordForLb.setTags(tags); newRecordForLb.setNumVips(numVips); } if (event.equals(UNSUSPEND_LOADBALANCER)) { newRecordForLb.setNumberOfPolls(0); } newRecordForLb.setEndTime(pollTime); processedRecords.add(newRecordForLb); newRecordForLb = initializeRecordForLb(lbMergedHostUsage, pollTime, hourToStop); newRecordForLb.setEventType(event.name()); } if (event.equals(CREATE_VIRTUAL_IP) || event.equals(DELETE_VIRTUAL_IP)) { newRecordForLb.setNumVips(lbMergedHostUsage.getNumVips()); numVipsCache.put(lbId, lbMergedHostUsage.getNumVips()); } if (event.equals(DELETE_LOADBALANCER)) { newRecordForLb.setEndTime(pollTime); } } isFirstRecordOfHourToProcess = false; } } if (newRecordForLb.getStartTime().before(hourToStop)) { if (newRecordForLb.getEventType() == null && suspendedLbsCache.contains(lbId)) { newRecordForLb.setEventType(SUSPENDED_LOADBALANCER.name()); } processedRecords.add(newRecordForLb); } return processedRecords; } private int getTags(Integer lbId) { int mostRecentTagsBitmask; try { if (!tagsCache.containsKey(lbId)) { Usage mostRecentUsageForLoadBalancer = usageRepository.getMostRecentUsageForLoadBalancer(lbId); mostRecentTagsBitmask = mostRecentUsageForLoadBalancer.getTags(); tagsCache.put(lbId, mostRecentTagsBitmask); } else { mostRecentTagsBitmask = tagsCache.get(lbId); } } catch (EntityNotFoundException e) { // TODO: Put an alert and monitor it! LOG.error("Unable to get proper tags for record. Please verify manually!", e); BitTags bitTags = loadbalancerService.getCurrentBitTags(lbId); bitTags.flipTagOff(BitTag.SSL); bitTags.flipTagOff(BitTag.SSL_MIXED_MODE); mostRecentTagsBitmask = bitTags.toInt(); tagsCache.put(lbId, mostRecentTagsBitmask); } return mostRecentTagsBitmask; } private int getNumVips(Integer lbId) { final int DEFAULT_NUM_VIPS = 1; int numVips = DEFAULT_NUM_VIPS; try { if (!numVipsCache.containsKey(lbId)) { Usage mostRecentUasageForLoadBalancer = usageRepository.getMostRecentUsageForLoadBalancer(lbId); numVips = mostRecentUasageForLoadBalancer.getNumVips(); numVipsCache.put(lbId, numVips); } else { numVips = numVipsCache.get(lbId); } } catch (EntityNotFoundException e) { // TODO: Put an alert and monitor it! LOG.error("Unable to get proper vips for record. Please verify manually!", e); numVipsCache.put(lbId, numVips); } return numVips; } private Usage initializeRecordForLb(Integer accountId, Integer loadbalancerId, Calendar startTime, Calendar endTime) { Usage usage = new Usage(); LoadBalancer lb = new LoadBalancer(); lb.setAccountId(accountId); lb.setId(loadbalancerId); usage.setLoadbalancer(lb); usage.setAccountId(accountId); usage.setStartTime(startTime); usage.setEndTime(endTime); usage.setTags(getTags(loadbalancerId)); usage.setNumVips(getNumVips(loadbalancerId)); usage.setCorrected(false); usage.setNeedsPushed(true); usage.setEntryVersion(0); return usage; } private Usage initializeRecordForLb(LoadBalancerMergedHostUsage lbMergedHostUsage, Calendar startTime, Calendar endTime) { Usage usage = new Usage(); LoadBalancer lb = new LoadBalancer(); lb.setId(lbMergedHostUsage.getLoadbalancerId()); lb.setAccountId(lbMergedHostUsage.getAccountId()); usage.setLoadbalancer(lb); usage.setAccountId(lbMergedHostUsage.getAccountId()); usage.setStartTime(startTime); usage.setEndTime(endTime); usage.setTags(lbMergedHostUsage.getTagsBitmask()); usage.setNumVips(lbMergedHostUsage.getNumVips()); usage.setCorrected(false); usage.setNeedsPushed(true); usage.setEntryVersion(0); return usage; } }