Merge branch 'master' of ssh://git.planet-lab.org/git/plstackapi
[plstackapi.git] / planetstack / tests / generate_billing_sample.py
1 """
2     Generates billing sample data
3 """
4
5 import datetime
6 import os
7 import operator
8 import pytz
9 import json
10 import random
11 import sys
12 import time
13
14 # The granularity at which the charge collection system collects charges. Once
15 # per hour makes for a very slow UI, so I upped it to once per 8 hours.
16 CHARGE_HOURS = 8
17
18 MINUTE_SECONDS = 60
19 HOUR_SECONDS = MINUTE_SECONDS * 60
20 DAY_SECONDS = HOUR_SECONDS * 24
21 MONTH_SECONDS = DAY_SECONDS * 30
22
23
24 sys.path.append("/opt/planetstack")
25 #sys.path.append("/home/smbaker/projects/vicci/plstackapi/planetstack")
26
27 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "planetstack.settings")
28 #from openstack.manager import OpenStackManager
29 from core.models import Slice, Sliver, ServiceClass, Reservation, Tag, Network, User, Node, Image, Deployment, Site, NetworkTemplate, NetworkSlice
30 from core.models import Invoice, Charge, Account, UsableObject, Payment
31
32 def delete_all(model):
33    for item in model.objects.all():
34        item.delete()
35
36 def get_usable_object(name):
37    objs = UsableObject.objects.filter(name=name)
38    if objs:
39        return objs[0]
40    obj = UsableObject(name=name)
41    obj.save()
42    return obj
43
44 def generate_invoice(account, batch):
45    invoice = Invoice(date=batch[-1].date, account=account)
46    invoice.save()
47    for charge in batch:
48        charge.invoice = invoice
49        charge.state = "invoiced"
50        charge.save()
51
52 def generate_invoices(account):
53    invoices = sorted(Invoice.objects.filter(account=account), key=operator.attrgetter('date'))
54    charges = sorted(Charge.objects.filter(account=account, state="pending"), key=operator.attrgetter('date'))
55
56    if invoices:
57        latest_invoice_date = invoices[-1].date()
58    else:
59        latest_invoice_date = None
60
61    batch = []
62    last_week = 0
63    for charge in charges:
64        # check to see if we crossed a week boundary. If we did, then generate
65        # an invoice for the last week's batch of charges
66        week = charge.date.isocalendar()[1]
67        if (week != last_week) and (batch):
68            generate_invoice(account, batch)
69            batch = []
70            last_week = week
71        batch.append(charge)
72
73    # we might still have last week's data batched up, and no data for this week
74    # if so, invoice the batch
75    this_week = datetime.datetime.now().isocalendar()[1]
76    if (this_week != last_week) and (batch):
77        generate_invoice(account, batch)
78
79 def generate_payments(account):
80     invoices = Invoice.objects.filter(account=account)
81     for invoice in invoices:
82         # let's be optomistic and assume everyone pays exactly two weeks after
83         # receiving an invoice
84         payment_time = int(invoice.date.strftime("%s")) + 14 * DAY_SECONDS
85         if payment_time < time.time():
86              payment_time = datetime.datetime.utcfromtimestamp(payment_time).replace(tzinfo=pytz.utc)
87              payment = Payment(account=account, amount=invoice.amount, date=payment_time)
88              payment.save()
89
90 print "deleting old stuff"
91
92 delete_all(Invoice)
93 delete_all(Charge)
94 delete_all(Payment)
95 delete_all(Account)
96 delete_all(UsableObject)
97
98 print "creating accounts"
99
100 for site in Site.objects.all():
101     # only create accounts for sites where some slices exist
102     if len(site.slices.all()) > 0:
103         account = Account(site=site)
104         account.save()
105
106 print "generating charges"
107
108 for slice in Slice.objects.all():
109     site = slice.site
110     account = site.accounts.all()[0]
111     serviceClass =slice.serviceClass
112
113     if not (slice.name in ["DnsRedir", "DnsDemux", "HyperCache", "Hadoop", "Owl", "Stork", "Syndicate", "test-slice-1", "test-slice-2", "test", "test2"]):
114         continue
115
116     print "   generating charges for", slice.name
117
118     now = int(time.time())/HOUR_SECONDS*HOUR_SECONDS
119
120     charge_kind=None
121     for resource in slice.serviceClass.resources.all():
122         if resource.name == "numberCores":
123             charge_kind = "reservation"
124             cost = resource.cost
125         elif (charge_kind==None) and (resource.name == "cycles") or (resource.name == "Cycles"):
126             charge_kind = "besteffort"
127             cost = resource.cost
128
129     if not charge_kind:
130         print "failed to find resource for", slice.serviceClass
131         continue
132
133     for sliver in slice.slivers.all()[:4]:    # only do up to 4 slivers; it's way too much data otherwise
134         hostname = sliver.node.name
135         for i in range(now-MONTH_SECONDS, now, CHARGE_HOURS*HOUR_SECONDS):
136             if charge_kind == "besteffort":
137                 core_hours = random.randint(20,60)/100.0
138             else:
139                 core_hours = 1
140
141             core_hours = core_hours * CHARGE_HOURS
142
143             amount = float(core_hours * cost) / 100.0
144
145             object = get_usable_object(hostname)
146
147             date = datetime.datetime.utcfromtimestamp(i).replace(tzinfo=pytz.utc)
148
149             charge = Charge(account=account, slice=slice, kind=charge_kind, state="pending", date=date, object=object, coreHours=core_hours, amount=amount)
150             charge.save()
151
152 print "doing invoices and payments"
153
154 for account in Account.objects.all():
155     generate_invoices(account)
156     generate_payments(account)
157
158
159