Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 109 additions & 5 deletions cost-estimator/estimate-costs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3

import pprint
import subprocess
import sys
import os.path
Expand All @@ -14,6 +15,7 @@
import argparse
import math
import re
import pyairtable


class AWSResource:
Expand Down Expand Up @@ -468,7 +470,8 @@ def test_authenticated(service):


class Account:
def __init__(self, orgs, space_names):
def __init__(self, name, orgs, space_names):
self.name = name
self.org_names = orgs
self.space_names = space_names if space_names else []

Expand All @@ -486,6 +489,7 @@ def __init__(self, orgs, space_names):
self.input_workbook_file = None
self.output_workbook_file = None
self.reporter = Reporter()
self.airtable = Airtable()

def report_orgs(self):
for org_name in self.org_names:
Expand Down Expand Up @@ -536,6 +540,86 @@ def report_summary(self, reporter):
for key, value in sorted(self.es_total_instance_plans.items()):
reporter.log(f" {key}: {value}")

def upload_airtable_estimate(self, airtable, account_name):
headline = f"Cost estimate for org: {self.org_names}"
if len(self.space_names) > 0:
headline += f", spaces: {self.space_names}"

price_lookup= airtable.price_dict()
print(f"Fetched {len(price_lookup)} Price Records from AirTable")

summary_record_id = airtable.summary_table.create(
{"Source": "cost-estimator", "Account": account_name, "Description": headline}
)['id']

airtable_batch = []

# memory-quota
if len(self.space_names) == 0:
at_memory_quota = self.memory_quota / 1024
else:
# If we are producing an estimate for a set of space(s), then the memory usage is
# whatever memory is used by those spaces. Round up the memory usage to the nearest
# integer because we charge for memory on a per GB basis, so any partial use of a GB
# should be treated as a whole GB for accounting purposes
at_memory_quota = math.ceil(
self.memory_usage / 1024
)
airtable_batch.append({
"Resource Summary": [summary_record_id],
"Resource Pricelist": [price_lookup['memory-quota']],
"Units": at_memory_quota
})
# rds-storage
if self.rds_total_allocation > 0:
airtable_batch.append({
"Resource Summary": [summary_record_id],
"Resource Pricelist": [price_lookup['rds-storage']],
"Units": math.ceil(self.rds_total_allocation)
})
# s3-storage
if self.s3_total_storage > 0:
airtable_batch.append({
"Resource Summary": [summary_record_id],
"Resource Pricelist": [price_lookup['s3-storage']],
"Units": math.ceil(self.s3_total_storage / ( 1024 * 1024 * 1024 ))
})

# es-storage
if self.es_total_volume_storage > 0:
airtable_batch.append({
"Resource Summary": [summary_record_id],
"Resource Pricelist": [price_lookup['es-storage']],
"Units": math.ceil(self.es_total_volume_storage)
})
# rds-plans
if len(self.rds_total_instance_plans) > 0:
for plan, units in sorted(self.rds_total_instance_plans.items()):
airtable_batch.append({
"Resource Summary": [summary_record_id],
"Resource Pricelist": [price_lookup[plan]],
"Units": units
})
# redis-plans
if len(self.redis_total_instance_plans) > 0:
for plan, units in sorted(self.redis_total_instance_plans.items()):
airtable_batch.append({
"Resource Summary": [summary_record_id],
"Resource Pricelist": [price_lookup[plan]],
"Units": units
})
# es-plans
if len(self.es_total_instance_plans) > 0:
for plan, units in sorted(self.es_total_instance_plans.items()):
airtable_batch.append({
"Resource Summary": [summary_record_id],
"Resource Pricelist": [price_lookup[plan]],
"Units": units
})
result = airtable.resource_table.batch_create(airtable_batch)
print(f"Created {len(result)} AirTable records")


def generate_cost_estimate(self, reporter):
estimate_map = {
# Usage
Expand Down Expand Up @@ -631,6 +715,24 @@ def generate_cost_estimate(self, reporter):
workbook.save(filename=self.output_workbook_file)
print(f"Saved cost estimate to: {self.output_workbook_file}")

class Airtable:
QUOTE_BASE_ID='appprdUNzFPO9avLd'
RESOURCE_ENTRY_TABLE_ID='tbl5fX9qzivwgMnD2'
RESOURCE_PRICING_TABLE_ID='tblr5evoP1pKGcUxl'
RESOURCE_SUMMARY_TABLE_ID='tblCj7JYRsYlqtruU'

def __init__(self):
self.api = pyairtable.Api(os.environ['AIRTABLE_API_KEY'])
self.summary_table = self.api.table(self.QUOTE_BASE_ID, self.RESOURCE_SUMMARY_TABLE_ID)
self.pricing_table = self.api.table(self.QUOTE_BASE_ID, self.RESOURCE_PRICING_TABLE_ID)
self.resource_table = self.api.table(self.QUOTE_BASE_ID, self.RESOURCE_ENTRY_TABLE_ID)

def price_dict(self):
price_dict = {}
for rp in self.pricing_table.all(fields=['Name']):
price_dict[rp['fields']['Name']] = (rp['id'])
return price_dict


class Reporter:
"""
Expand Down Expand Up @@ -725,10 +827,11 @@ def main():
print("space names only allowed when specifying a single organization")
exit(1)

account_name = org_names[-1]
if args.account_name:
output_file = args.account_name + ".xlsx"
else:
output_file = org_names[-1] + ".xlsx"
account_name = args.account_name
output_file = account_name + ".xlsx"


if not os.path.exists(cost_estimate_file):
print(
Expand All @@ -744,14 +847,15 @@ def main():

print(f"Info: Authenticated, starting...", file=sys.stderr)

acct = Account(orgs=org_names, space_names=space_names)
acct = Account(name=account_name,orgs=org_names, space_names=space_names)
acct.report_orgs()
if len(org_names) > 1:
acct.report_summary(acct.reporter)

acct.input_workbook_file = cost_estimate_file
acct.output_workbook_file = output_file
acct.generate_cost_estimate(acct.reporter)
acct.upload_airtable_estimate(acct.airtable, acct.name)


if __name__ == "__main__":
Expand Down