/************************************************************************* * (c) Copyright 2017 Hewlett Packard Enterprise Development Company LP * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. ************************************************************************/ package com.eucalyptus.portal.monthlyreport; import com.eucalyptus.auth.euare.Accounts; import com.eucalyptus.auth.principal.AccountFullName; import com.eucalyptus.component.annotation.ComponentPart; import com.eucalyptus.portal.BucketUploadableActivities; import com.eucalyptus.portal.S3UploadException; import com.eucalyptus.portal.awsusage.AwsUsageRecords; import com.eucalyptus.portal.common.Portal; import com.eucalyptus.portal.workflow.AwsUsageRecord; import com.eucalyptus.portal.workflow.BillingActivityException; import com.eucalyptus.portal.workflow.MonthlyReportActivities; import com.eucalyptus.portal.workflow.MonthlyUsageRecord; import com.google.common.collect.Lists; import org.apache.log4j.Logger; import java.io.ByteArrayInputStream; import java.io.UnsupportedEncodingException; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; import java.util.stream.Collectors; @ComponentPart(Portal.class) public class MonthlyReportActivitiesImpl extends BucketUploadableActivities implements MonthlyReportActivities { private static Logger LOG = Logger.getLogger( MonthlyReportActivitiesImpl.class ); @Override public List<String> listCandidateAccounts() throws BillingActivityException { try { return Accounts.listAllAccounts().stream() // TODO: should we filter out system accounts? .map(e -> e.getAccountNumber()) .collect(Collectors.toList()); } catch (final Exception ex) { throw new BillingActivityException("Failed to list candidate accounts"); } } @Override public List<AwsUsageRecord> queryMonthlyAwsUsage(final String accountId, final String service, final String year, final String month, final Date queryEnd) throws BillingActivityException { try { /// query aws usage records from the beginning of month, to until this moment final Calendar calFrom = Calendar.getInstance(); calFrom.set(Integer.parseInt(year), Integer.parseInt(month)-1, 1, 0, 0); final Date queryBegin = calFrom.getTime(); return Lists.newArrayList(AwsUsageRecords.getInstance().queryMonthly( accountId, service, null, null, queryBegin, queryEnd)); } catch (final Exception ex) { throw new BillingActivityException("Failed to query the latest aws usage reports", ex); } } @Override public List<MonthlyUsageRecord> transform(List<AwsUsageRecord> awsUsage) throws BillingActivityException { try { return awsUsage.stream() .map(MonthlyReports.transform) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toList()); } catch (final Exception ex) { throw new BillingActivityException("Failed to transform aws usage records to monthly records", ex); } } @Override public void persist(String accountId, String year, String month, List<MonthlyUsageRecord> monthly) throws BillingActivityException { try { final AccountFullName owner = AccountFullName.getInstance(accountId); final List<MonthlyReportEntry> entries = monthly.stream() .map(MonthlyReports.instantiate) .collect(Collectors.toList()); MonthlyReports.getInstance().createOrUpdate(owner, year, month, entries); } catch (final Exception ex) { throw new BillingActivityException("Failed to persist monthly report entries", ex); } } @Override public void uploadToS3Bucket(String accountId, String year, String month) throws BillingActivityException { final StringBuilder sb = new StringBuilder(); sb.append("\"InvoiceID\",\"PayerAccountId\",\"LinkedAccountId\",\"RecordType\",\"RecordID\",\"BillingPeriodStartDate\"," + "\"BillingPeriodEndDate\",\"InvoiceDate\",\"PayerAccountName\",\"LinkedAccountName\",\"TaxationAddress\"," + "\"PayerPONumber\",\"ProductCode\",\"ProductName\",\"SellerOfRecord\",\"UsageType\",\"Operation\",\"RateId\"," + "\"ItemDescription\",\"UsageStartDate\",\"UsageEndDate\",\"UsageQuantity\",\"BlendedRate\",\"CurrencyCode\"," + "\"CostBeforeTax\",\"Credits\",\"TaxAmount\",\"TaxType\",\"TotalCost\""); try { MonthlyReports.getInstance() .lookupReport(AccountFullName.getInstance(accountId), year, month).stream() .forEach( r -> sb.append(String.format("\n%s", r.toString())) ); } catch (final NoSuchElementException ex) { LOG.warn("No monthly record is found in persistence storage"); return; } try { if (month!=null && month.length()<=1) { month = "0"+month; } final String keyName = String.format("%s-aws-billing-csv-%s-%s.csv", accountId, year, month); if (this.upload(accountId, keyName, new ByteArrayInputStream( sb.toString().getBytes("UTF-8")))) { LOG.debug(String.format("Monthly report for account " + accountId + " has been uploaded")); } else { LOG.warn("Failed to upload monthly report to s3 bucket for account " + accountId); } } catch (final UnsupportedEncodingException ex) { LOG.warn("Failed to create input stream out of report"); } catch (final S3UploadException ex) { LOG.warn("Failed to upload monthly report to s3 bucket", ex); } } }