Revision 452d2391
b/.gitignore | ||
---|---|---|
12 | 12 |
*.installed.cfg |
13 | 13 |
*.sqlite |
14 | 14 |
.Python |
15 |
.idea |
|
16 |
.DS_Store |
b/db/charger.py | ||
---|---|---|
8 | 8 |
|
9 | 9 |
from db.models import * |
10 | 10 |
|
11 |
from datetime import datetime |
|
12 | 11 |
|
13 |
def stop_virtual_machine(vm): |
|
14 |
"""Send message to stop a virtual machine instance""" |
|
15 |
|
|
16 |
# send the message to ganeti |
|
17 |
|
|
18 |
return |
|
19 |
|
|
20 |
def charge(): |
|
12 |
def periodically_charge(): |
|
21 | 13 |
"""Scan all virtual machines and charge each user""" |
22 | 14 |
all_vms = VirtualMachine.objects.all() |
23 | 15 |
|
24 | 16 |
if len(all_vms) == 0: |
25 | 17 |
print "No virtual machines found" |
18 |
return |
|
26 | 19 |
|
27 | 20 |
for vm in all_vms: |
28 |
cost = 0 |
|
29 |
|
|
30 | 21 |
# Running and Stopped is charged, else the cost is zero |
31 |
|
|
32 |
|
|
33 |
start = vm.charged |
|
34 |
end = datetime.now() |
|
35 |
user_credits = vm.owner.charge_credits(cost, start, end) |
|
36 |
vm.charged = end |
|
37 |
|
|
38 |
# update the values in the database |
|
39 |
vm.save() |
|
40 |
vm.owner.save() |
|
41 |
|
|
42 |
if user_credits <= 0: |
|
43 |
stop_virtual_machine(vm) |
|
22 |
# FIXME: not implemented! |
|
23 |
vm.charge() |
|
44 | 24 |
|
45 | 25 |
# vim: set ts=4 sts=4 sw=4 et ai : |
b/db/fixtures/db_test_data.json | ||
---|---|---|
4 | 4 |
"pk": 30000, |
5 | 5 |
"fields": { |
6 | 6 |
"name": "test db user", |
7 |
"credit": 0, |
|
7 |
"credit": 10,
|
|
8 | 8 |
"created": "2011-02-06", |
9 | 9 |
"user" : 1 |
10 | 10 |
} |
... | ... | |
22 | 22 |
"model" : "db.Limit", |
23 | 23 |
"pk" : 30001, |
24 | 24 |
"fields" : { |
25 |
"name" : "MAX_VIOLATIONS",
|
|
25 |
"name" : "MIN_CREDITS",
|
|
26 | 26 |
"user" : 30000, |
27 |
"value" : 5
|
|
27 |
"value" : -100
|
|
28 | 28 |
} |
29 | 29 |
}, |
30 | 30 |
{ |
... | ... | |
46 | 46 |
} |
47 | 47 |
}, |
48 | 48 |
{ |
49 |
"model": "db.Flavor", |
|
50 |
"pk": 30001, |
|
51 |
"fields": { |
|
52 |
"cpu": 1, |
|
53 |
"ram": 1024, |
|
54 |
"disk": 20 |
|
55 |
} |
|
56 |
}, |
|
57 |
{ |
|
58 |
"model" : "db.FlavorCostHistory", |
|
49 |
"model" : "db.FlavorCost", |
|
59 | 50 |
"pk" : 30000, |
60 | 51 |
"fields" : { |
61 | 52 |
"cost_active" : 10, |
... | ... | |
65 | 56 |
} |
66 | 57 |
}, |
67 | 58 |
{ |
68 |
"model" : "db.FlavorCostHistory",
|
|
59 |
"model" : "db.FlavorCost", |
|
69 | 60 |
"pk" : 30001, |
70 | 61 |
"fields" : { |
71 | 62 |
"cost_active" : 2, |
... | ... | |
75 | 66 |
} |
76 | 67 |
}, |
77 | 68 |
{ |
69 |
"model" : "db.FlavorCost", |
|
70 |
"pk" : 30002, |
|
71 |
"fields" : { |
|
72 |
"cost_active" : 5, |
|
73 |
"cost_inactive" : 2, |
|
74 |
"effective_from" : "2010-08-01", |
|
75 |
"flavor" : 30000 |
|
76 |
} |
|
77 |
}, |
|
78 |
{ |
|
79 |
"model" : "db.FlavorCost", |
|
80 |
"pk" : 30003, |
|
81 |
"fields" : { |
|
82 |
"cost_active" : 4, |
|
83 |
"cost_inactive" : 10, |
|
84 |
"effective_from" : "2010-01-01", |
|
85 |
"flavor" : 30000 |
|
86 |
} |
|
87 |
}, |
|
88 |
{ |
|
78 | 89 |
"model": "db.VirtualMachine", |
79 | 90 |
"pk": 30000, |
80 | 91 |
"fields": { |
... | ... | |
91 | 102 |
} |
92 | 103 |
}, |
93 | 104 |
{ |
94 |
"model": "db.VirtualMachine", |
|
95 |
"pk": 30001, |
|
96 |
"fields": { |
|
97 |
"name": "snf-1002", |
|
98 |
"created": "2011-02-10 00:00:00", |
|
99 |
"charged": "2011-02-10 00:00:00", |
|
100 |
"sourceimage": 30000, |
|
101 |
"hostid": "HAL-9000", |
|
102 |
"description": "mail server", |
|
103 |
"ipfour": "192.168.2.2", |
|
104 |
"ipsix": "::2", |
|
105 |
"flavor": 30000, |
|
106 |
"_operstate": "BUILD" |
|
107 |
} |
|
108 |
}, |
|
109 |
{ |
|
110 |
"model": "db.VirtualMachine", |
|
111 |
"pk": 30002, |
|
112 |
"fields": { |
|
113 |
"name": "snf-1003", |
|
114 |
"created": "2009-02-10 00:00:00", |
|
115 |
"charged": "2010-01-01 00:00:00", |
|
116 |
"sourceimage": 30000, |
|
117 |
"hostid": "HAL-9000", |
|
118 |
"description": "my server", |
|
119 |
"ipfour": "192.168.2.3", |
|
120 |
"ipsix": "::3", |
|
121 |
"flavor": 30000, |
|
122 |
"_operstate": "STARTED" |
|
123 |
} |
|
124 |
}, |
|
125 |
{ |
|
126 |
"model": "db.VirtualMachine", |
|
127 |
"pk": 30003, |
|
128 |
"fields": { |
|
129 |
"name": "snf-1004", |
|
130 |
"created": "2011-02-10 00:00:00", |
|
131 |
"charged": "2011-02-10 00:00:00", |
|
132 |
"sourceimage": 30000, |
|
133 |
"hostid": "HAL-9000", |
|
134 |
"description": "my 2nd server", |
|
135 |
"ipfour": "192.168.2.4", |
|
136 |
"ipsix": "::4", |
|
137 |
"flavor": 30000, |
|
138 |
"_operstate": "STARTED" |
|
139 |
} |
|
140 |
}, |
|
141 |
{ |
|
142 |
"model" : "db.Debit", |
|
143 |
"pk" : 30000, |
|
144 |
"fields" : { |
|
145 |
"vm" : 30000, |
|
146 |
"date" : "2010-01-01", |
|
147 |
"state" : "STARTED" |
|
148 |
} |
|
149 |
}, |
|
150 |
{ |
|
151 |
"model" : "db.Debit", |
|
152 |
"pk" : 30001, |
|
153 |
"fields" : { |
|
154 |
"vm" : 30000, |
|
155 |
"date" : "2011-02-01", |
|
156 |
"state" : "STARTED" |
|
157 |
} |
|
158 |
}, |
|
159 |
{ |
|
160 |
"model" : "db.Debit", |
|
161 |
"pk" : 30002, |
|
162 |
"fields" : { |
|
163 |
"vm" : 30002, |
|
164 |
"date" : "2010-01-01", |
|
165 |
"state" : "STARTED" |
|
166 |
} |
|
167 |
}, |
|
168 |
{ |
|
169 |
"model" : "db.Debit", |
|
170 |
"pk" : 30003, |
|
171 |
"fields" : { |
|
172 |
"vm" : 30002, |
|
173 |
"date" : "2010-03-01", |
|
174 |
"state" : "STOPPED" |
|
175 |
} |
|
176 |
}, |
|
177 |
{ |
|
178 |
"model" : "db.Debit", |
|
179 |
"pk" : 30004, |
|
180 |
"fields" : { |
|
181 |
"vm" : 30002, |
|
182 |
"date" : "2011-01-01", |
|
183 |
"state" : "STARTED" |
|
184 |
} |
|
185 |
}, |
|
186 |
{ |
|
187 | 105 |
"model": "db.Image", |
188 | 106 |
"pk": 30000, |
189 | 107 |
"fields": { |
190 | 108 |
"name": "Debian Squeeze", |
191 | 109 |
"updated": "2011-02-06 00:00:00", |
192 | 110 |
"created": "2011-02-06 00:00:00", |
111 |
"size" : 2000, |
|
193 | 112 |
"state": "ACTIVE", |
194 | 113 |
"description": "Full Debian Squeeze Installation", |
195 | 114 |
"owner" : 30000 |
196 | 115 |
} |
197 | 116 |
}, |
198 | 117 |
{ |
199 |
"model": "db.Image", |
|
200 |
"pk": 30001, |
|
201 |
"fields": { |
|
202 |
"name": "Slackware 13.1", |
|
203 |
"updated": "2011-02-10 00:00:00", |
|
204 |
"created": "2011-02-10 00:00:00", |
|
205 |
"state": "ACTIVE", |
|
206 |
"description": "Full Slackware 13.1 Installation", |
|
207 |
"owner" : 30000 |
|
208 |
} |
|
209 |
}, |
|
210 |
{ |
|
211 | 118 |
"model": "db.Disk", |
212 | 119 |
"pk": 30000, |
213 | 120 |
"fields": { |
... | ... | |
217 | 124 |
"vm" : 30000, |
218 | 125 |
"owner" : 30000 |
219 | 126 |
} |
220 |
}, |
|
221 |
{ |
|
222 |
"model": "db.Disk", |
|
223 |
"pk": 30001, |
|
224 |
"fields": { |
|
225 |
"name": "My_Videos", |
|
226 |
"created": "2011-02-10 00:00:00", |
|
227 |
"size" : 300, |
|
228 |
"vm" : 30000, |
|
229 |
"owner" : 30000 |
|
230 |
} |
|
231 |
} |
|
127 |
} |
|
232 | 128 |
] |
b/db/management/commands/charge_users.py | ||
---|---|---|
12 | 12 |
help = 'Charge the users for VM usage' |
13 | 13 |
|
14 | 14 |
def handle_noargs(self, **options): |
15 |
charger.charge() |
|
15 |
charger.periodically_charge() |
b/db/models.py | ||
---|---|---|
3 | 3 |
from django.conf import settings |
4 | 4 |
from django.db import models |
5 | 5 |
from django.contrib.auth.models import User |
6 |
from django.utils.translation import gettext_lazy as _ |
|
7 | 6 |
|
8 | 7 |
import datetime |
9 | 8 |
|
... | ... | |
50 | 49 |
|
51 | 50 |
def debit_account(self, amount, vm, description): |
52 | 51 |
"""Charges the user with the specified amount of credits for a vm (resource)""" |
53 |
return |
|
52 |
date_now = datetime.datetime.now() |
|
53 |
|
|
54 |
# first reduce the user's credits and save |
|
55 |
self.credit = self.credit - amount |
|
56 |
self.save() |
|
57 |
|
|
58 |
# then write the debit entry! |
|
59 |
debit = Debit() |
|
60 |
|
|
61 |
debit.user = self |
|
62 |
debit.vm = vm |
|
63 |
debit.when = date_now |
|
64 |
debit.description = description |
|
65 |
|
|
66 |
debit.save() |
|
54 | 67 |
|
55 | 68 |
def credit_account(self, amount, creditor, description): |
56 | 69 |
"""No clue :)""" |
... | ... | |
78 | 91 |
verbose_name = u'Image' |
79 | 92 |
|
80 | 93 |
def __unicode__(self): |
81 |
return u'%s' % (self.name)
|
|
94 |
return u'%s' % ( self.name, )
|
|
82 | 95 |
|
83 | 96 |
|
84 | 97 |
class ImageMetadata(models.Model): |
... | ... | |
107 | 120 |
verbose_name = u'Enforced limit for user' |
108 | 121 |
|
109 | 122 |
def __unicode__(self): |
110 |
return u'Limit %s for user %s: %d' % (self.limit, self.user, self.value)
|
|
123 |
return u'Limit %s for user %s: %d' % (self.value, self.user, self.value)
|
|
111 | 124 |
|
112 | 125 |
|
113 | 126 |
class Flavor(models.Model): |
... | ... | |
122 | 135 |
def _get_name(self): |
123 | 136 |
"""Returns flavor name (generated)""" |
124 | 137 |
return u'C%dR%dD%d' % (self.cpu, self.ram, self.disk) |
125 |
|
|
126 |
def _current_cost_active(self): |
|
138 |
|
|
139 |
def _current_cost(self, active): |
|
140 |
"""Returns active/inactive cost value |
|
141 |
|
|
142 |
set active = True to get active cost and False for the inactive. |
|
143 |
|
|
144 |
""" |
|
127 | 145 |
fch_list = FlavorCost.objects.filter(flavor=self).order_by('-effective_from') |
128 | 146 |
if len(fch_list) > 0: |
129 |
return fch_list[0].cost_active |
|
130 |
|
|
147 |
if active: |
|
148 |
return fch_list[0].cost_active |
|
149 |
else: |
|
150 |
return fch_list[0].cost_inactive |
|
151 |
|
|
131 | 152 |
return 0 |
153 |
|
|
154 |
|
|
155 |
def _current_cost_active(self): |
|
156 |
"""Returns current active cost (property method)""" |
|
157 |
return self._current_cost(True) |
|
132 | 158 |
|
133 | 159 |
def _current_cost_inactive(self): |
134 |
fch_list = FlavorCost.objects.filter(flavor=self).order_by('-effective_from') |
|
135 |
if len(fch_list) > 0: |
|
136 |
return fch_list[0].cost_inactive |
|
137 |
|
|
138 |
return 0 |
|
160 |
"""Returns current inactive cost (property method)""" |
|
161 |
return self._current_cost(False) |
|
139 | 162 |
|
140 | 163 |
name = property(_get_name) |
141 | 164 |
current_cost_active = property(_current_cost_active) |
... | ... | |
143 | 166 |
|
144 | 167 |
def __unicode__(self): |
145 | 168 |
return self.name |
146 |
|
|
169 |
|
|
170 |
def _get_costs(self, start_datetime, end_datetime, active): |
|
171 |
"""Return a list with FlavorCost objects for the specified duration""" |
|
172 |
def between(enh_fc, a_date): |
|
173 |
"""Checks if a date is between a FlavorCost duration""" |
|
174 |
if enh_fc.effective_from <= a_date and enh_fc.effective_to is None: |
|
175 |
return True |
|
176 |
|
|
177 |
return enh_fc.effective_from <= a_date and enh_fc.effective_to >= a_date |
|
178 |
|
|
179 |
def calculate_cost(start_date, end_date, cost): |
|
180 |
"""Calculate the total cost for the specified duration""" |
|
181 |
td = end_date - start_date |
|
182 |
sec = float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / float(10**6) |
|
183 |
total_hours = float(sec) / float(60.0*60.0) |
|
184 |
total_cost = float(cost)*total_hours |
|
185 |
|
|
186 |
return round(total_cost) |
|
187 |
|
|
188 |
price_list = FlavorCost.objects.filter(flavor=self).order_by('effective_from') |
|
189 |
|
|
190 |
for idx in range(0, len(price_list)): |
|
191 |
if idx + 1 == len(price_list): |
|
192 |
price_list[idx].effective_to = None |
|
193 |
else: |
|
194 |
price_list[idx].effective_to = price_list[idx + 1].effective_from |
|
195 |
|
|
196 |
price_result = [] |
|
197 |
found_start = False |
|
198 |
|
|
199 |
for p in price_list: |
|
200 |
if between(p, start_datetime): |
|
201 |
found_start = True |
|
202 |
p.effective_from = start_datetime |
|
203 |
if between(p, end_datetime): |
|
204 |
p.effective_to = end_datetime |
|
205 |
price_result.append(p) |
|
206 |
break |
|
207 |
if found_start: |
|
208 |
price_result.append(p) |
|
209 |
|
|
210 |
results = [] |
|
211 |
|
|
212 |
for p in price_result: |
|
213 |
if active: |
|
214 |
cost = p.cost_active |
|
215 |
else: |
|
216 |
cost = p.cost_inactive |
|
217 |
|
|
218 |
results.append( ( str(p.effective_from), calculate_cost(p.effective_from, p.effective_to, cost)) ) |
|
219 |
|
|
220 |
return results |
|
221 |
|
|
147 | 222 |
def get_cost_active(self, start_datetime, end_datetime): |
148 | 223 |
"""Returns a list with the active costs for the specified duration""" |
149 |
|
|
150 |
return [] |
|
151 |
|
|
224 |
return self._get_costs(start_datetime, end_datetime, True) |
|
225 |
|
|
152 | 226 |
def get_cost_inactive(self, start_datetime, end_datetime): |
153 | 227 |
"""Returns a list with the active costs for the specified duration""" |
154 |
return []
|
|
228 |
return self._get_costs(start_datetime, end_datetime, False)
|
|
155 | 229 |
|
156 | 230 |
|
157 | 231 |
class FlavorCost(models.Model): |
158 | 232 |
cost_active = models.PositiveIntegerField('Active Cost') |
159 | 233 |
cost_inactive = models.PositiveIntegerField('Inactive Cost') |
160 |
effective_from = models.DateField() |
|
234 |
effective_from = models.DateTimeField()
|
|
161 | 235 |
flavor = models.ForeignKey(Flavor) |
162 | 236 |
|
163 | 237 |
class Meta: |
... | ... | |
377 | 451 |
Charges the VM owner from vm.charged to datetime.now() |
378 | 452 |
|
379 | 453 |
""" |
380 |
return |
|
454 |
cur_datetime = datetime.datetime.now() |
|
455 |
cost_list = [] |
|
456 |
|
|
457 |
if self._operstate == 'STARTED': |
|
458 |
cost_list = self.flavor.get_cost_active(self.charged, cur_datetime) |
|
459 |
else: |
|
460 |
cost_list = self.flavot.get_cost_inactive(self.charged, cur_datetime) |
|
461 |
|
|
462 |
total_cost = 0 |
|
463 |
|
|
464 |
for cl in cost_list: |
|
465 |
total_cost = total_cost + cl[1] |
|
466 |
|
|
467 |
# still need to set correctly the message |
|
468 |
self.owner.debit_account(total_cost, self, "Charged Credits to User") |
|
469 |
|
|
470 |
self.charged = cur_datetime |
|
471 |
self.save() |
|
381 | 472 |
|
382 | 473 |
|
383 | 474 |
class VirtualMachineGroup(models.Model): |
... | ... | |
419 | 510 |
verbose_name = u'Accounting log' |
420 | 511 |
|
421 | 512 |
def __unicode__(self): |
422 |
return u'%s - %s - %s' % (self.vm.name, str(self.date), self.state)
|
|
513 |
return u'%s - %s - %s - %s' % ( self.user.id, self.vm.name, str(self.when), self.description)
|
|
423 | 514 |
|
424 | 515 |
|
425 | 516 |
class Disk(models.Model): |
b/db/tests.py | ||
---|---|---|
6 | 6 |
# Copyright 2010 Greek Research and Technology Network |
7 | 7 |
# |
8 | 8 |
|
9 |
import unittest |
|
10 |
|
|
11 |
from datetime import datetime, date, timedelta |
|
12 |
|
|
13 | 9 |
from db.models import * |
14 |
from db import credit_allocator |
|
15 |
from db import charger |
|
16 | 10 |
|
17 |
from django.conf import settings |
|
18 |
from django.contrib.auth.models import User |
|
19 | 11 |
from django.test import TestCase |
20 | 12 |
|
21 |
class CreditAllocatorTestCase(TestCase): |
|
22 |
fixtures = [ 'db_test_data' ] |
|
23 |
|
|
24 |
def test_credit_allocator(self): |
|
25 |
"""Credit Allocator unit test method""" |
|
26 |
# test the allocator |
|
27 |
credit_allocator.allocate_credit() |
|
28 |
|
|
29 |
user = SynnefoUser.objects.get(pk=30000) |
|
30 |
self.assertEquals(user.credit, 10, 'Allocation of credits failed, credit: (%d!=10)' % ( user.credit, ) ) |
|
31 |
|
|
32 |
# get the quota from Limit model and check the answer |
|
33 |
limit_quota = user.credit_quota |
|
34 |
self.assertEquals(limit_quota, 100, 'User quota has not retrieved correctly (%d!=100)' % ( limit_quota, )) |
|
35 |
|
|
36 |
# test if the quota policy is endorced |
|
37 |
for i in range(1, 10): |
|
38 |
credit_allocator.allocate_credit() |
|
39 |
|
|
40 |
user = SynnefoUser.objects.get(pk=30000) |
|
41 |
self.assertEquals(user.credit, limit_quota, 'User exceeded quota! (cr:%d, qu:%d)' % ( user.credit, limit_quota ) ) |
|
42 |
|
|
43 | 13 |
|
44 | 14 |
class FlavorTestCase(TestCase): |
45 | 15 |
fixtures = [ 'db_test_data' ] |
46 |
|
|
16 |
|
|
47 | 17 |
def test_flavor(self): |
48 | 18 |
"""Test a flavor object, its internal cost calculation and naming methods""" |
49 | 19 |
flavor = Flavor.objects.get(pk=30000) |
50 |
|
|
51 |
flavor_name = u'C%dR%dD%d' % ( flavor.cpu, flavor.ram, flavor.disk ) |
|
52 |
|
|
53 |
self.assertEquals(flavor.cost_active, 10, 'Active cost is not calculated correctly! (%d!=10)' % ( flavor.cost_active, ) ) |
|
54 |
self.assertEquals(flavor.cost_inactive, 5, 'Inactive cost is not calculated correctly! (%d!=5)' % ( flavor.cost_inactive, ) ) |
|
55 |
self.assertEquals(flavor.name, flavor_name, 'Invalid flavor name!') |
|
56 |
|
|
57 |
def test_flavor_cost_history(self): |
|
58 |
"""Flavor unit test (find_cost method)""" |
|
59 |
flavor = Flavor.objects.get(pk=30000) |
|
60 |
fch_list = flavor.get_price_list() |
|
61 | 20 |
|
62 |
self.assertEquals(len(fch_list), 2, 'Price list should have two objects! (%d!=2)' % ( len(fch_list), )) |
|
21 |
# test current active/inactive costs |
|
22 |
c_active = flavor.current_cost_active |
|
23 |
c_inactive = flavor.current_cost_inactive |
|
63 | 24 |
|
64 |
# 2010-10-10, active should be 2, inactive 1 |
|
65 |
ex_date = date(year=2010, month=10, day=10) |
|
66 |
r = flavor.find_cost(ex_date) |
|
25 |
self.assertEqual(c_active, 10, 'flavor.cost_active should be 10! (%d)' % ( c_active, )) |
|
26 |
self.assertEqual(c_inactive, 5, 'flavor.cost_inactive should be 5! (%d)' % ( c_inactive, )) |
|
67 | 27 |
|
68 |
self.assertEquals(r.cost_active, 2, 'Active cost for 2010-10-10 should be 2 (%d!=2)' % ( r.cost_active, ))
|
|
69 |
self.assertEquals(r.cost_inactive, 1, 'Inactive cosr for 2010-10-10 should be 1 (%d!=1)' % ( r.cost_inactive, ))
|
|
28 |
# test name property, should be C1R1024D10
|
|
29 |
f_name = flavor.name
|
|
70 | 30 |
|
71 |
# 2011-11-11, active should be 10, inactive 5 |
|
72 |
ex_date = date(year=2011, month=11, day=11) |
|
73 |
r = flavor.find_cost(ex_date) |
|
74 |
self.assertEquals(r.cost_active, 10, 'Active cost for 2011-11-11 should be 10 (%d!=10)' % ( r.cost_active, )) |
|
75 |
self.assertEquals(r.cost_inactive, 5, 'Inactive cost for 2011-11-11 should be 5 (%d!=5)' % ( r.cost_inactive, )) |
|
31 |
self.assertEqual(f_name, 'C1R1024D10', 'flavor.name is not generated correctly, C1R1024D10! (%s)' % ( f_name, )) |
|
76 | 32 |
|
33 |
def test_flavor_get_costs(self): |
|
34 |
flavor = Flavor.objects.get(pk=30000) |
|
77 | 35 |
|
78 |
class DebitTestCase(TestCase): |
|
79 |
fixtures = [ 'db_test_data' ] |
|
80 |
|
|
81 |
def test_accounting_log(self): |
|
82 |
"""Test the Accounting Log unit method""" |
|
83 |
vm = VirtualMachine.objects.get(pk=30000) |
|
84 |
|
|
85 |
# get all entries, should be 2 |
|
86 |
entries = Debit.get_log_entries(vm, datetime.datetime(year=2009, month=01, day=01)) |
|
87 |
self.assertEquals(len(entries), 2, 'Log entries should be 2 (%d!=2)' % ( len(entries), )) |
|
88 | 36 |
|
89 |
# get enrties only for 2011, should be 1 |
|
90 |
entries = Debit.get_log_entries(vm, datetime.datetime(year=2011, month=01, day=01)) |
|
91 |
self.assertEquals(len(entries), 1, 'Log entries should be 1 (%d!=1)' % ( len(entries), )) |
|
92 | 37 |
|
93 | 38 |
|
94 |
class VirtualMachineTestCase(TestCase):
|
|
39 |
class SynnefoUserTestCase(TestCase):
|
|
95 | 40 |
fixtures = [ 'db_test_data' ] |
96 |
|
|
97 |
def test_virtual_machine(self): |
|
98 |
"""Virtual Machine (model) unit test method""" |
|
99 |
vm = VirtualMachine.objects.get(pk=30002) |
|
100 |
|
|
101 |
# should be three |
|
102 |
acc_logs = vm.get_accounting_logs() |
|
103 |
|
|
104 |
self.assertEquals(len(acc_logs), 3, 'Log Entries should be 3 (%d!=3)' % ( len(acc_logs), )) |
|
105 | 41 |
|
42 |
def test_synnefo_user(self): |
|
43 |
"""Test a SynnefoUser object""" |
|
44 |
s_user = SynnefoUser.objects.get(pk=30000) |
|
45 |
v_machine = VirtualMachine.objects.get(pk=30000) |
|
106 | 46 |
|
47 |
# charge the user |
|
48 |
s_user.debit_account(10, v_machine, "This should be a structured debit message!") |
|
107 | 49 |
|
108 |
class ChargerTestCase(TestCase):
|
|
109 |
fixtures = [ 'db_test_data' ]
|
|
50 |
# should have only one debit object
|
|
51 |
d_list = Debit.objects.all()
|
|
110 | 52 |
|
111 |
def test_charger(self): |
|
112 |
"""Charger unit test method""" |
|
113 |
|
|
114 |
# user with pk=1 should have 100 credits |
|
115 |
user = SynnefoUser.objects.get(pk=1) |
|
116 |
user.credit = 100 |
|
117 |
user.save() |
|
118 |
|
|
119 |
# charge when the vm is running |
|
120 |
charger.charge() |
|
121 |
|
|
122 |
user = SynnefoUser.objects.get(pk=1) |
|
123 |
|
|
124 |
self.assertEquals(user.credit, 90, 'Error in charging process (%d!=90, running)' % ( user.credit, )) |
|
125 |
|
|
126 |
# charge when the vm is stopped |
|
127 |
vm = VirtualMachine.objects.get(pk=1) |
|
128 |
vm.charged = datetime.datetime.now() - datetime.timedelta(hours=1) |
|
129 |
vm.save() |
|
130 |
|
|
131 |
charger.charge() |
|
132 |
|
|
133 |
user = SynnefoUser.objects.get(pk=1) |
|
134 |
self.assertEquals(user.credit, 85, 'Error in charging process (%d!=85, stopped)' % ( user.credit, )) |
|
135 |
|
|
136 |
# try charge until the user spends all his credits, see if the charger |
|
137 |
vm = VirtualMachine.objects.get(pk=1) |
|
138 |
vm.charged = datetime.datetime.now() - datetime.timedelta(hours=1) |
|
139 |
vm.save() |
|
140 |
|
|
141 |
# the user now has 85, charge until his credits drop to zero |
|
142 |
for i in range(1, 10): |
|
143 |
vm = VirtualMachine.objects.get(pk=1) |
|
144 |
vm.charged = datetime.datetime.now() - datetime.timedelta(hours=1) |
|
145 |
vm.save() |
|
146 |
charger.charge() |
|
147 |
|
|
148 |
user = SynnefoUser.objects.get(pk=1) |
|
149 |
self.assertEquals(user.credit, 0, 'Error in charging process (%d!=0, running)' % ( user.credit, )) |
|
150 |
|
|
53 |
self.assertEqual(len(d_list), 1, 'SynnefoUser.debit_account() writes more than one debit entries!') |
|
54 |
|
|
55 |
# retrieve the user, now he/she should have zero credits |
|
56 |
s_user = SynnefoUser.objects.get(pk=30000) |
|
57 |
|
|
58 |
self.assertEqual(0, s_user.credit, 'SynnefoUser (pk=30000) should have zero credits (%d)' % ( s_user.credit, )) |
Also available in: Unified diff