Statistics
| Branch: | Tag: | Revision:

root / logic / credits.py @ 7d52c0b4

History | View | Annotate | Download (4.6 kB)

1
#
2
# Business Logic for all Credit related activity
3
#
4
# Copyright 2010 Greek Research and Technology Network
5
#
6

    
7
from datetime import datetime
8

    
9
from db.models import Debit, FlavorCost, VirtualMachine
10

    
11
from django.db import transaction
12

    
13
import synnefo.settings as settings
14

    
15
@transaction.commit_on_success
16
def debit_account(user , amount, vm, description):
17
    """Charges the user with the specified amount of credits for a vm (resource)"""
18
    date_now = datetime.now()
19
    user.credit = user.credit - amount
20
    user.save()
21

    
22
    # then write the debit entry
23
    debit = Debit()
24
    debit.user = user
25
    debit.vm = vm
26
    debit.when = date_now
27
    debit.description = description
28
    debit.save()
29

    
30

    
31
@transaction.commit_on_success
32
def credit_account(self, amount, creditor, description):
33
    """No clue :)"""
34
    return
35

    
36

    
37
@transaction.commit_on_success
38
def charge(vm):
39
    """Charges the owner of this VM.
40

41
    Charges the owner of a VM for the period
42
    from vm.charged to datetime.now(), based on the
43
    current operating state.
44

45
    """
46
    charged_states = ('STARTED', 'STOPPED')
47

    
48
    start_datetime = vm.charged
49
    vm.charged = datetime.now()
50

    
51
    # Only charge for a specific set of states
52
    if vm._operstate in charged_states:
53
        cost_list = []
54

    
55
        # remember, we charge only for Started and Stopped
56
        if vm._operstate == 'STARTED':
57
            cost_list = vm.flavor.get_cost_active(start_datetime, vm.charged)
58
        elif vm._operstate == 'STOPPED':
59
            cost_list = vm.flavor.get_cost_inactive(start_datetime, vm.charged)
60

    
61
        # find the total vost
62
        total_cost = sum([x[1] for x in cost_list])
63

    
64
        # add the debit entry
65
        description = "Server = %s, charge = %d for state: %s" % (vm.name, total_cost, vm._operstate)
66
        debit_account(vm.owner, total_cost, vm, description)
67

    
68
    vm.save()
69

    
70
def get_costs(vm, start_datetime, end_datetime, active):
71
    """Return a list with FlavorCost objects for the specified duration"""
72
    def between(enh_fc, a_date):
73
        """Checks if a date is between a FlavorCost duration"""
74
        if enh_fc.effective_from <= a_date and enh_fc.effective_to is None:
75
            return True
76

    
77
        return enh_fc.effective_from <= a_date and enh_fc.effective_to >= a_date
78

    
79
    # Get the related FlavorCost objects, sorted.
80
    price_list = FlavorCost.objects.filter(flavor=vm).order_by('effective_from')
81

    
82
    # add the extra field FlavorCost.effective_to
83
    for idx in range(0, len(price_list)):
84
        if idx + 1 == len(price_list):
85
            price_list[idx].effective_to = None
86
        else:
87
            price_list[idx].effective_to = price_list[idx + 1].effective_from
88

    
89
    price_result = []
90
    found_start = False
91

    
92
    # Find the affected FlavorCost, according to the
93
    # dates, and put them in price_result
94
    for p in price_list:
95
        if between(p, start_datetime):
96
            found_start = True
97
            p.effective_from = start_datetime
98
        if between(p, end_datetime):
99
            p.effective_to = end_datetime
100
            price_result.append(p)
101
            break
102
        if found_start:
103
            price_result.append(p)
104

    
105
    results = []
106

    
107
    # Create the list and the result tuples
108
    for p in price_result:
109
        if active:
110
            cost = p.cost_active
111
        else:
112
            cost = p.cost_inactive
113

    
114
        results.append( ( p.effective_from, calculate_cost(p.effective_from, p.effective_to, cost)) )
115

    
116
    return results
117

    
118
def id_from_instance_name(name):
119
    """Returns VirtualMachine's Django id, given a ganeti machine name.
120

121
    Strips the ganeti prefix atm. Needs a better name!
122

123
    """
124
    if not str(name).startswith(settings.BACKEND_PREFIX_ID):
125
        raise VirtualMachine.InvalidBackendIdError(str(name))
126
    ns = str(name).lstrip(settings.BACKEND_PREFIX_ID)
127
    if not ns.isdigit():
128
        raise VirtualMachine.InvalidBackendIdError(str(name))
129

    
130
    return int(ns)
131

    
132

    
133
def get_rsapi_state(vm):
134
    """Returns the RSAPI state for a virtual machine"""
135
    try:
136
        r = VirtualMachine.RSAPI_STATE_FROM_OPER_STATE[vm._operstate]
137
    except KeyError:
138
        return "UNKNOWN"
139
    # A machine is in REBOOT if an OP_INSTANCE_REBOOT request is in progress
140
    if r == 'ACTIVE' and vm._backendopcode == 'OP_INSTANCE_REBOOT' and \
141
        vm._backendjobstatus in ('queued', 'waiting', 'running'):
142
        return "REBOOT"
143
    return r
144

    
145

    
146
def calculate_cost(start_date, end_date, cost):
147
    """Calculate the total cost for the specified duration"""
148
    td = end_date - start_date
149
    sec = float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / float(10**6)
150
    total_hours = float(sec) / float(60.0*60.0)
151
    total_cost = float(cost)*total_hours
152

    
153
    return round(total_cost)