Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / quotas / __init__.py @ bfe7ba3c

History | View | Annotate | Download (6.1 kB)

1
# Copyright 2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
#
7
#   1. Redistributions of source code must retain the above copyright
8
#      notice, this list of conditions and the following disclaimer.
9
#
10
#  2. Redistributions in binary form must reproduce the above copyright
11
#     notice, this list of conditions and the following disclaimer in the
12
#     documentation and/or other materials provided with the distribution.
13
#
14
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
# SUCH DAMAGE.
25
#
26
# The views and conclusions contained in the software and documentation are
27
# those of the authors and should not be interpreted as representing official
28
# policies, either expressed or implied, of GRNET S.A.
29

    
30
from functools import wraps
31
from contextlib import contextmanager
32

    
33
from synnefo.settings import QUOTAHOLDER_URL
34
from synnefo.db.models import QuotaHolderSerial
35
from synnefo.api.faults import OverLimit
36

    
37
from commissioning.clients.quotaholder import QuotaholderHTTP
38
from commissioning.api.exception import (CommissionException, NoCapacityError,
39
                                         NoQuantityError)
40

    
41
import logging
42
log = logging.getLogger(__name__)
43

    
44

    
45
@contextmanager
46
def get_quota_holder():
47
    """Context manager for using a QuotaHolder."""
48
    quotaholder = QuotaholderHTTP(QUOTAHOLDER_URL)
49

    
50
    try:
51
        yield quotaholder
52
    finally:
53
        pass
54

    
55

    
56
def uses_commission(func):
57
    """Decorator for wrapping functions that needs commission.
58

59
    All decorated functions must take as first argument the `serials` list in
60
    order to extend them with the needed serial numbers, as return by the
61
    Quotaholder
62

63
    On successful competition of the decorated function, all serials are
64
    accepted to the quotaholder, otherwise they are rejected.
65

66
    """
67

    
68
    @wraps(func)
69
    def wrapper(*args, **kwargs):
70
        try:
71
            serials = []
72
            ret = func(serials, *args, **kwargs)
73
            if serials:
74
                accept_commission(serials)
75
            return ret
76
        except CommissionException:
77
            log.exception("Unexpected error")
78
        except:
79
            if serials:
80
                reject_commission(serials=serials)
81
            raise
82
    return wrapper
83

    
84

    
85
## FIXME: Wrap the following two functions inside transaction ?
86
def accept_commission(serials):
87
    """Accept a list of pending commissions.
88

89
    @param serials: List of QuotaHolderSerial objects
90

91
    """
92
    # Update the DB if needed
93
    log.critical("Serials are %s", serials)
94
    for s in serials:
95
        if s.pending:
96
            s.accepted = True
97
            s.save()
98

    
99
    with get_quota_holder() as qh:
100
        qh.accept_commission(context={},
101
                             clientkey='cyclades',
102
                             serials=[s.serial for s in serials])
103

    
104

    
105
def reject_commission(serials):
106
    """Reject a list of pending commissions.
107

108
    @param serials: List of QuotaHolderSerial objects
109

110
    """
111
    # Update the DB if needed
112
    for s in serials:
113
        if s.pending:
114
            s.rejected = True
115
            s.save()
116

    
117
    with get_quota_holder() as qh:
118
        qh.reject_commission(context={},
119
                             clientkey='cyclades',
120
                             serials=[s.serial for s in serials])
121

    
122

    
123
def issue_commission(**commission_info):
124
    """Issue a new commission to the quotaholder.
125

126
    Issue a new commission to the quotaholder, and create the
127
    corresponing QuotaHolderSerial object in DB.
128

129
    """
130

    
131
    with get_quota_holder() as qh:
132
        try:
133
            serial = qh.issue_commission(**commission_info)
134
        except (NoCapacityError, NoQuantityError):
135
            raise OverLimit("Limit exceeded for your account")
136

    
137
    db_serial = QuotaHolderSerial.objects.create(serial=serial)
138
    return db_serial
139

    
140

    
141
# Wrapper functions for issuing commissions for each resource type.  Each
142
# functions creates the `commission_info` dictionary as expected by the
143
# `issue_commision` function. Commissions for deleting a resource, are the same
144
# as for creating the same resource, but with negative resource sizes.
145

    
146

    
147
def issue_vm_commission(user, flavor, delete=False):
148
    resources = get_server_resources(flavor)
149
    commission_info = create_commission(user, resources, delete)
150

    
151
    return issue_commission(**commission_info)
152

    
153

    
154
def get_server_resources(flavor):
155
    return {'vm': 1,
156
            'cpu': flavor.cpu,
157
            'disk': flavor.disk,
158
            # 'public_ip': 1,
159
            #'disk_template': flavor.disk_template,
160
            'ram': flavor.ram}
161

    
162

    
163
def issue_network_commission(user, delete=False):
164
    resources = get_network_resources()
165
    commission_info = create_commission(user, resources, delete)
166

    
167
    return issue_commission(**commission_info)
168

    
169

    
170
def get_network_resources():
171
    return {"network.private": 1}
172

    
173

    
174
def invert_resources(resources_dict):
175
    return dict((r, -s) for r, s in resources_dict.items())
176

    
177

    
178
def create_commission(user, resources, delete=False):
179
    if delete:
180
        resources = invert_resources(resources)
181
    provisions = [('system', 'cyclades.' + r, s)
182
                  for r, s in resources.items()]
183
    log.debug("Provisions are %s", provisions)
184
    return  {"context":    {},
185
             "target":     user,
186
             "key":        "",
187
             "clientkey":  "cyclades",
188
             "owner":      "",
189
             "ownerkey":   "",
190
             "name":       "",
191
             "provisions": provisions}