Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (15.6 kB)

1 ff5edb80 Giorgos Korfiatis
# Copyright 2012-2014 GRNET S.A. All rights reserved.
2 bfe7ba3c Christos Stavrakakis
#
3 bfe7ba3c Christos Stavrakakis
# Redistribution and use in source and binary forms, with or without
4 bfe7ba3c Christos Stavrakakis
# modification, are permitted provided that the following conditions
5 bfe7ba3c Christos Stavrakakis
# are met:
6 bfe7ba3c Christos Stavrakakis
#
7 bfe7ba3c Christos Stavrakakis
#   1. Redistributions of source code must retain the above copyright
8 bfe7ba3c Christos Stavrakakis
#      notice, this list of conditions and the following disclaimer.
9 bfe7ba3c Christos Stavrakakis
#
10 bfe7ba3c Christos Stavrakakis
#  2. Redistributions in binary form must reproduce the above copyright
11 bfe7ba3c Christos Stavrakakis
#     notice, this list of conditions and the following disclaimer in the
12 bfe7ba3c Christos Stavrakakis
#     documentation and/or other materials provided with the distribution.
13 bfe7ba3c Christos Stavrakakis
#
14 bfe7ba3c Christos Stavrakakis
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15 bfe7ba3c Christos Stavrakakis
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 bfe7ba3c Christos Stavrakakis
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 bfe7ba3c Christos Stavrakakis
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18 bfe7ba3c Christos Stavrakakis
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 bfe7ba3c Christos Stavrakakis
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 bfe7ba3c Christos Stavrakakis
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 bfe7ba3c Christos Stavrakakis
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 bfe7ba3c Christos Stavrakakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 bfe7ba3c Christos Stavrakakis
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 bfe7ba3c Christos Stavrakakis
# SUCH DAMAGE.
25 bfe7ba3c Christos Stavrakakis
#
26 bfe7ba3c Christos Stavrakakis
# The views and conclusions contained in the software and documentation are
27 bfe7ba3c Christos Stavrakakis
# those of the authors and should not be interpreted as representing official
28 bfe7ba3c Christos Stavrakakis
# policies, either expressed or implied, of GRNET S.A.
29 bfe7ba3c Christos Stavrakakis
30 656cf771 Christos Stavrakakis
from django.utils import simplejson as json
31 2509ce17 Christos Stavrakakis
from django.db import transaction
32 bfe7ba3c Christos Stavrakakis
33 bd40abfa Christos Stavrakakis
from snf_django.lib.api import faults
34 8b41683a Christos Stavrakakis
from synnefo.db.models import (QuotaHolderSerial, VirtualMachine, Network,
35 326c3ec8 Christos Stavrakakis
                               IPAddress)
36 6dbd90c0 Christos Stavrakakis
37 18c4414d Giorgos Korfiatis
from synnefo.settings import (CYCLADES_SERVICE_TOKEN as ASTAKOS_TOKEN,
38 e407f159 Ilias Tsitsimpis
                              ASTAKOS_AUTH_URL)
39 629acc65 Giorgos Korfiatis
from astakosclient import AstakosClient
40 0dc4b304 Giorgos Korfiatis
from astakosclient import errors
41 bfe7ba3c Christos Stavrakakis
42 bfe7ba3c Christos Stavrakakis
import logging
43 bfe7ba3c Christos Stavrakakis
log = logging.getLogger(__name__)
44 bfe7ba3c Christos Stavrakakis
45 e407f159 Ilias Tsitsimpis
46 ba777b02 Giorgos Korfiatis
QUOTABLE_RESOURCES = [VirtualMachine, Network, IPAddress]
47 ba777b02 Giorgos Korfiatis
48 ba777b02 Giorgos Korfiatis
49 656cf771 Christos Stavrakakis
RESOURCES = [
50 656cf771 Christos Stavrakakis
    "cyclades.vm",
51 5083fc47 Christos Stavrakakis
    "cyclades.total_cpu",
52 656cf771 Christos Stavrakakis
    "cyclades.cpu",
53 656cf771 Christos Stavrakakis
    "cyclades.disk",
54 5083fc47 Christos Stavrakakis
    "cyclades.total_ram",
55 656cf771 Christos Stavrakakis
    "cyclades.ram",
56 8b41683a Christos Stavrakakis
    "cyclades.network.private",
57 8b41683a Christos Stavrakakis
    "cyclades.floating_ip",
58 656cf771 Christos Stavrakakis
]
59 bfe7ba3c Christos Stavrakakis
60 bfe7ba3c Christos Stavrakakis
61 629acc65 Giorgos Korfiatis
class Quotaholder(object):
62 629acc65 Giorgos Korfiatis
    _object = None
63 629acc65 Giorgos Korfiatis
64 629acc65 Giorgos Korfiatis
    @classmethod
65 629acc65 Giorgos Korfiatis
    def get(cls):
66 629acc65 Giorgos Korfiatis
        if cls._object is None:
67 4fb643b3 Giorgos Korfiatis
            cls._object = AstakosClient(ASTAKOS_TOKEN,
68 4fb643b3 Giorgos Korfiatis
                                        ASTAKOS_AUTH_URL,
69 4fb643b3 Giorgos Korfiatis
                                        use_pool=True,
70 4fb643b3 Giorgos Korfiatis
                                        retry=3,
71 4fb643b3 Giorgos Korfiatis
                                        logger=log)
72 629acc65 Giorgos Korfiatis
        return cls._object
73 bfe7ba3c Christos Stavrakakis
74 bfe7ba3c Christos Stavrakakis
75 0dc4b304 Giorgos Korfiatis
class AstakosClientExceptionHandler(object):
76 0dc4b304 Giorgos Korfiatis
    def __init__(self, *args, **kwargs):
77 13f1e2ff Giorgos Korfiatis
        self.user = kwargs.get("user")
78 13f1e2ff Giorgos Korfiatis
        self.projects = kwargs.get("projects")
79 0dc4b304 Giorgos Korfiatis
80 0dc4b304 Giorgos Korfiatis
    def __enter__(self):
81 0dc4b304 Giorgos Korfiatis
        pass
82 0dc4b304 Giorgos Korfiatis
83 13f1e2ff Giorgos Korfiatis
    def check_notFound(self):
84 13f1e2ff Giorgos Korfiatis
        if not self.user or not self.projects:
85 13f1e2ff Giorgos Korfiatis
            return
86 13f1e2ff Giorgos Korfiatis
        try:
87 13f1e2ff Giorgos Korfiatis
            qh = Quotaholder.get()
88 13f1e2ff Giorgos Korfiatis
            user_quota = qh.service_get_quotas(self.user)
89 13f1e2ff Giorgos Korfiatis
        except errors.AstakosClientException as e:
90 13f1e2ff Giorgos Korfiatis
            log.exception("Unexpected error %s" % e.message)
91 13f1e2ff Giorgos Korfiatis
            raise faults.InternalServerError("Unexpected error")
92 13f1e2ff Giorgos Korfiatis
93 13f1e2ff Giorgos Korfiatis
        user_quota = user_quota[self.user]
94 13f1e2ff Giorgos Korfiatis
        for project in self.projects:
95 13f1e2ff Giorgos Korfiatis
            try:
96 13f1e2ff Giorgos Korfiatis
                user_quota[project]
97 13f1e2ff Giorgos Korfiatis
            except KeyError:
98 13f1e2ff Giorgos Korfiatis
                m = "User %s not in project %s" % (self.user, project)
99 13f1e2ff Giorgos Korfiatis
                raise faults.BadRequest(m)
100 13f1e2ff Giorgos Korfiatis
101 0dc4b304 Giorgos Korfiatis
    def __exit__(self, exc_type, value, traceback):
102 0dc4b304 Giorgos Korfiatis
        if value is not None:  # exception
103 0dc4b304 Giorgos Korfiatis
            if not isinstance(value, errors.AstakosClientException):
104 0dc4b304 Giorgos Korfiatis
                return False  # reraise
105 0dc4b304 Giorgos Korfiatis
            if exc_type is errors.QuotaLimit:
106 0dc4b304 Giorgos Korfiatis
                msg, details = render_overlimit_exception(value)
107 0dc4b304 Giorgos Korfiatis
                raise faults.OverLimit(msg, details=details)
108 13f1e2ff Giorgos Korfiatis
            if exc_type is errors.NotFound:
109 13f1e2ff Giorgos Korfiatis
                self.check_notFound()
110 0dc4b304 Giorgos Korfiatis
111 0dc4b304 Giorgos Korfiatis
            log.exception("Unexpected error %s" % value.message)
112 499d9bfe Christos Stavrakakis
            raise faults.InternalServerError("Unexpected error")
113 499d9bfe Christos Stavrakakis
114 499d9bfe Christos Stavrakakis
115 16b959ce Giorgos Korfiatis
def issue_commission(resource, action, name="", force=False, auto_accept=False,
116 16b959ce Giorgos Korfiatis
                     action_fields=None):
117 bfe7ba3c Christos Stavrakakis
    """Issue a new commission to the quotaholder.
118 bfe7ba3c Christos Stavrakakis

119 bfe7ba3c Christos Stavrakakis
    Issue a new commission to the quotaholder, and create the
120 bfe7ba3c Christos Stavrakakis
    corresponing QuotaHolderSerial object in DB.
121 bfe7ba3c Christos Stavrakakis

122 bfe7ba3c Christos Stavrakakis
    """
123 bfe7ba3c Christos Stavrakakis
124 16b959ce Giorgos Korfiatis
    provisions = get_commission_info(resource=resource, action=action,
125 16b959ce Giorgos Korfiatis
                                     action_fields=action_fields)
126 16b959ce Giorgos Korfiatis
127 16b959ce Giorgos Korfiatis
    if provisions is None:
128 16b959ce Giorgos Korfiatis
        return None
129 16b959ce Giorgos Korfiatis
130 16b959ce Giorgos Korfiatis
    user = resource.userid
131 47c27955 Giorgos Korfiatis
    source = resource.project
132 16b959ce Giorgos Korfiatis
133 629acc65 Giorgos Korfiatis
    qh = Quotaholder.get()
134 99667854 Giorgos Korfiatis
    if action == "REASSIGN":
135 99667854 Giorgos Korfiatis
        try:
136 99667854 Giorgos Korfiatis
            from_project = action_fields["from_project"]
137 99667854 Giorgos Korfiatis
            to_project = action_fields["to_project"]
138 99667854 Giorgos Korfiatis
        except KeyError:
139 99667854 Giorgos Korfiatis
            raise Exception("Missing project attribute.")
140 99667854 Giorgos Korfiatis
141 99667854 Giorgos Korfiatis
        projects = [from_project, to_project]
142 99667854 Giorgos Korfiatis
        with AstakosClientExceptionHandler(user=user, projects=projects):
143 99667854 Giorgos Korfiatis
            serial = qh.issue_resource_reassignment(user,
144 99667854 Giorgos Korfiatis
                                                    from_project, to_project,
145 99667854 Giorgos Korfiatis
                                                    provisions, name=name,
146 99667854 Giorgos Korfiatis
                                                    force=force,
147 99667854 Giorgos Korfiatis
                                                    auto_accept=auto_accept)
148 99667854 Giorgos Korfiatis
    else:
149 13f1e2ff Giorgos Korfiatis
        with AstakosClientExceptionHandler(user=user, projects=[source]):
150 16b959ce Giorgos Korfiatis
            serial = qh.issue_one_commission(user, source,
151 16b959ce Giorgos Korfiatis
                                             provisions, name=name,
152 16b959ce Giorgos Korfiatis
                                             force=force,
153 16b959ce Giorgos Korfiatis
                                             auto_accept=auto_accept)
154 bfe7ba3c Christos Stavrakakis
155 ba777b02 Giorgos Korfiatis
    if not serial:
156 b14f55b5 Christos Stavrakakis
        raise Exception("No serial")
157 bfe7ba3c Christos Stavrakakis
158 ba777b02 Giorgos Korfiatis
    serial_info = {"serial": serial}
159 ba777b02 Giorgos Korfiatis
    if auto_accept:
160 ba777b02 Giorgos Korfiatis
        serial_info["pending"] = False
161 ba777b02 Giorgos Korfiatis
        serial_info["accept"] = True
162 ba777b02 Giorgos Korfiatis
        serial_info["resolved"] = True
163 ba777b02 Giorgos Korfiatis
164 ba777b02 Giorgos Korfiatis
    serial = QuotaHolderSerial.objects.create(**serial_info)
165 ba777b02 Giorgos Korfiatis
166 ba777b02 Giorgos Korfiatis
    # Correlate the serial with the resource. Resolved serials are not
167 ba777b02 Giorgos Korfiatis
    # attached to resources
168 ba777b02 Giorgos Korfiatis
    if not auto_accept:
169 ba777b02 Giorgos Korfiatis
        resource.serial = serial
170 ba777b02 Giorgos Korfiatis
        resource.save()
171 bfe7ba3c Christos Stavrakakis
172 ba777b02 Giorgos Korfiatis
    return serial
173 ba777b02 Giorgos Korfiatis
174 ba777b02 Giorgos Korfiatis
175 ba777b02 Giorgos Korfiatis
def accept_resource_serial(resource, strict=True):
176 ba777b02 Giorgos Korfiatis
    serial = resource.serial
177 f4d624c6 Giorgos Korfiatis
    assert serial.pending or serial.accept, "%s can't be accepted" % serial
178 ba777b02 Giorgos Korfiatis
    log.debug("Accepting serial %s of resource %s", serial, resource)
179 ba777b02 Giorgos Korfiatis
    _resolve_commissions(accept=[serial.serial], strict=strict)
180 ba777b02 Giorgos Korfiatis
    resource.serial = None
181 ba777b02 Giorgos Korfiatis
    resource.save()
182 ba777b02 Giorgos Korfiatis
    return resource
183 41a7fae7 Christos Stavrakakis
184 41a7fae7 Christos Stavrakakis
185 ba777b02 Giorgos Korfiatis
def reject_resource_serial(resource, strict=True):
186 ba777b02 Giorgos Korfiatis
    serial = resource.serial
187 f4d624c6 Giorgos Korfiatis
    assert serial.pending or not serial.accept, "%s can't be rejected" % serial
188 ba777b02 Giorgos Korfiatis
    log.debug("Rejecting serial %s of resource %s", serial, resource)
189 ba777b02 Giorgos Korfiatis
    _resolve_commissions(reject=[serial.serial], strict=strict)
190 ba777b02 Giorgos Korfiatis
    resource.serial = None
191 ba777b02 Giorgos Korfiatis
    resource.save()
192 ba777b02 Giorgos Korfiatis
    return resource
193 41a7fae7 Christos Stavrakakis
194 41a7fae7 Christos Stavrakakis
195 ba777b02 Giorgos Korfiatis
def _resolve_commissions(accept=None, reject=None, strict=True):
196 629acc65 Giorgos Korfiatis
    if accept is None:
197 629acc65 Giorgos Korfiatis
        accept = []
198 629acc65 Giorgos Korfiatis
    if reject is None:
199 629acc65 Giorgos Korfiatis
        reject = []
200 11a54cb9 Christos Stavrakakis
201 629acc65 Giorgos Korfiatis
    qh = Quotaholder.get()
202 0dc4b304 Giorgos Korfiatis
    with AstakosClientExceptionHandler():
203 0dc4b304 Giorgos Korfiatis
        response = qh.resolve_commissions(accept, reject)
204 5db3a1e6 Christos Stavrakakis
205 f4d624c6 Giorgos Korfiatis
    accepted = response.get("accepted", [])
206 f4d624c6 Giorgos Korfiatis
    rejected = response.get("rejected", [])
207 f4d624c6 Giorgos Korfiatis
208 f4d624c6 Giorgos Korfiatis
    if accepted:
209 f4d624c6 Giorgos Korfiatis
        QuotaHolderSerial.objects.filter(serial__in=accepted).update(
210 f4d624c6 Giorgos Korfiatis
            accept=True, pending=False, resolved=True)
211 f4d624c6 Giorgos Korfiatis
    if rejected:
212 f4d624c6 Giorgos Korfiatis
        QuotaHolderSerial.objects.filter(serial__in=rejected).update(
213 f4d624c6 Giorgos Korfiatis
            accept=False, pending=False, resolved=True)
214 3af1fb4b Christos Stavrakakis
215 5db3a1e6 Christos Stavrakakis
    if strict:
216 5db3a1e6 Christos Stavrakakis
        failed = response["failed"]
217 5db3a1e6 Christos Stavrakakis
        if failed:
218 5db3a1e6 Christos Stavrakakis
            log.error("Unexpected error while resolving commissions: %s",
219 5db3a1e6 Christos Stavrakakis
                      failed)
220 5db3a1e6 Christos Stavrakakis
221 5db3a1e6 Christos Stavrakakis
    return response
222 11a54cb9 Christos Stavrakakis
223 11a54cb9 Christos Stavrakakis
224 ba777b02 Giorgos Korfiatis
def reconcile_resolve_commissions(accept=None, reject=None, strict=True):
225 ba777b02 Giorgos Korfiatis
    response = _resolve_commissions(accept=accept,
226 ba777b02 Giorgos Korfiatis
                                    reject=reject,
227 ba777b02 Giorgos Korfiatis
                                    strict=strict)
228 ba777b02 Giorgos Korfiatis
    affected = response.get("accepted", []) + response.get("rejected", [])
229 ba777b02 Giorgos Korfiatis
    for resource in QUOTABLE_RESOURCES:
230 ba777b02 Giorgos Korfiatis
        resource.objects.filter(serial__in=affected).update(serial=None)
231 ba777b02 Giorgos Korfiatis
232 ba777b02 Giorgos Korfiatis
233 11a54cb9 Christos Stavrakakis
def resolve_pending_commissions():
234 11a54cb9 Christos Stavrakakis
    """Resolve quotaholder pending commissions.
235 11a54cb9 Christos Stavrakakis

236 11a54cb9 Christos Stavrakakis
    Get pending commissions from the quotaholder and resolve them
237 11a54cb9 Christos Stavrakakis
    to accepted and rejected, according to the state of the
238 11a54cb9 Christos Stavrakakis
    QuotaHolderSerial DB table. A pending commission in the quotaholder
239 11a54cb9 Christos Stavrakakis
    can exist in the QuotaHolderSerial table and be either accepted or
240 8d5795b4 Christos Stavrakakis
    rejected, or cannot exist in this table, so it is rejected.
241 11a54cb9 Christos Stavrakakis

242 11a54cb9 Christos Stavrakakis
    """
243 11a54cb9 Christos Stavrakakis
244 11a54cb9 Christos Stavrakakis
    qh_pending = get_quotaholder_pending()
245 11a54cb9 Christos Stavrakakis
    if not qh_pending:
246 11a54cb9 Christos Stavrakakis
        return ([], [])
247 11a54cb9 Christos Stavrakakis
248 11a54cb9 Christos Stavrakakis
    qh_pending.sort()
249 11a54cb9 Christos Stavrakakis
    min_ = qh_pending[0]
250 11a54cb9 Christos Stavrakakis
251 11a54cb9 Christos Stavrakakis
    serials = QuotaHolderSerial.objects.filter(serial__gte=min_, pending=False)
252 2509ce17 Christos Stavrakakis
    accepted = serials.filter(accept=True).values_list('serial', flat=True)
253 11a54cb9 Christos Stavrakakis
    accepted = filter(lambda x: x in qh_pending, accepted)
254 11a54cb9 Christos Stavrakakis
255 11a54cb9 Christos Stavrakakis
    rejected = list(set(qh_pending) - set(accepted))
256 11a54cb9 Christos Stavrakakis
257 11a54cb9 Christos Stavrakakis
    return (accepted, rejected)
258 11a54cb9 Christos Stavrakakis
259 11a54cb9 Christos Stavrakakis
260 11a54cb9 Christos Stavrakakis
def get_quotaholder_pending():
261 629acc65 Giorgos Korfiatis
    qh = Quotaholder.get()
262 e407f159 Ilias Tsitsimpis
    pending_serials = qh.get_pending_commissions()
263 11a54cb9 Christos Stavrakakis
    return pending_serials
264 5a70b1a3 Christos Stavrakakis
265 5a70b1a3 Christos Stavrakakis
266 629acc65 Giorgos Korfiatis
def render_overlimit_exception(e):
267 5a70b1a3 Christos Stavrakakis
    resource_name = {"vm": "Virtual Machine",
268 5a70b1a3 Christos Stavrakakis
                     "cpu": "CPU",
269 5a70b1a3 Christos Stavrakakis
                     "ram": "RAM",
270 8b41683a Christos Stavrakakis
                     "network.private": "Private Network",
271 8b41683a Christos Stavrakakis
                     "floating_ip": "Floating IP address"}
272 656cf771 Christos Stavrakakis
    details = json.loads(e.details)
273 629acc65 Giorgos Korfiatis
    data = details['overLimit']['data']
274 656cf771 Christos Stavrakakis
    usage = data["usage"]
275 656cf771 Christos Stavrakakis
    limit = data["limit"]
276 656cf771 Christos Stavrakakis
    available = limit - usage
277 629acc65 Giorgos Korfiatis
    provision = data['provision']
278 629acc65 Giorgos Korfiatis
    requested = provision['quantity']
279 629acc65 Giorgos Korfiatis
    resource = provision['resource']
280 629acc65 Giorgos Korfiatis
    res = resource.replace("cyclades.", "", 1)
281 5a70b1a3 Christos Stavrakakis
    try:
282 5a70b1a3 Christos Stavrakakis
        resource = resource_name[res]
283 5a70b1a3 Christos Stavrakakis
    except KeyError:
284 5a70b1a3 Christos Stavrakakis
        resource = res
285 5a70b1a3 Christos Stavrakakis
286 5a70b1a3 Christos Stavrakakis
    msg = "Resource Limit Exceeded for your account."
287 5a70b1a3 Christos Stavrakakis
    details = "Limit for resource '%s' exceeded for your account."\
288 629acc65 Giorgos Korfiatis
              " Available: %s, Requested: %s"\
289 629acc65 Giorgos Korfiatis
              % (resource, available, requested)
290 5a70b1a3 Christos Stavrakakis
    return msg, details
291 2509ce17 Christos Stavrakakis
292 2509ce17 Christos Stavrakakis
293 21eb7404 Christos Stavrakakis
@transaction.commit_on_success
294 368d879e Giorgos Korfiatis
def issue_and_accept_commission(resource, action="BUILD", action_fields=None):
295 2509ce17 Christos Stavrakakis
    """Issue and accept a commission to Quotaholder.
296 2509ce17 Christos Stavrakakis

297 2509ce17 Christos Stavrakakis
    This function implements the Commission workflow, and must be called
298 2509ce17 Christos Stavrakakis
    exactly after and in the same transaction that created/updated the
299 2509ce17 Christos Stavrakakis
    resource. The workflow that implements is the following:
300 4ed25171 Christos Stavrakakis
    0) Resolve previous unresolved commission if exists
301 21eb7404 Christos Stavrakakis
    1) Issue commission, get a serial and correlate it with the resource
302 21eb7404 Christos Stavrakakis
    2) Store the serial in DB as a serial to accept
303 21eb7404 Christos Stavrakakis
    3) COMMIT!
304 21eb7404 Christos Stavrakakis
    4) Accept commission to QH
305 2509ce17 Christos Stavrakakis

306 2509ce17 Christos Stavrakakis
    """
307 368d879e Giorgos Korfiatis
    commission_reason = ("client: api, resource: %s, action: %s"
308 368d879e Giorgos Korfiatis
                         % (resource, action))
309 21eb7404 Christos Stavrakakis
    serial = handle_resource_commission(resource=resource, action=action,
310 368d879e Giorgos Korfiatis
                                        action_fields=action_fields,
311 21eb7404 Christos Stavrakakis
                                        commission_name=commission_reason)
312 4ed25171 Christos Stavrakakis
313 ba777b02 Giorgos Korfiatis
    if serial is None:
314 ba777b02 Giorgos Korfiatis
        return
315 ba777b02 Giorgos Korfiatis
316 21eb7404 Christos Stavrakakis
    # Mark the serial as one to accept and associate it with the resource
317 21eb7404 Christos Stavrakakis
    serial.pending = False
318 21eb7404 Christos Stavrakakis
    serial.accept = True
319 21eb7404 Christos Stavrakakis
    serial.save()
320 21eb7404 Christos Stavrakakis
    transaction.commit()
321 2509ce17 Christos Stavrakakis
322 2509ce17 Christos Stavrakakis
    try:
323 41a7fae7 Christos Stavrakakis
        # Accept the commission to quotaholder
324 ba777b02 Giorgos Korfiatis
        accept_resource_serial(resource)
325 2509ce17 Christos Stavrakakis
    except:
326 21eb7404 Christos Stavrakakis
        # Do not crash if we can not accept commission to Quotaholder. Quotas
327 21eb7404 Christos Stavrakakis
        # have already been reserved and the resource already exists in DB.
328 21eb7404 Christos Stavrakakis
        # Just log the error
329 ba777b02 Giorgos Korfiatis
        log.exception("Failed to accept commission: %s", resource.serial)
330 2509ce17 Christos Stavrakakis
331 2509ce17 Christos Stavrakakis
332 41a7fae7 Christos Stavrakakis
def get_commission_info(resource, action, action_fields=None):
333 41a7fae7 Christos Stavrakakis
    if isinstance(resource, VirtualMachine):
334 41a7fae7 Christos Stavrakakis
        flavor = resource.flavor
335 41a7fae7 Christos Stavrakakis
        resources = {"cyclades.vm": 1,
336 5083fc47 Christos Stavrakakis
                     "cyclades.total_cpu": flavor.cpu,
337 41a7fae7 Christos Stavrakakis
                     "cyclades.disk": 1073741824 * flavor.disk,
338 5083fc47 Christos Stavrakakis
                     "cyclades.total_ram": 1048576 * flavor.ram}
339 5083fc47 Christos Stavrakakis
        online_resources = {"cyclades.cpu": flavor.cpu,
340 5083fc47 Christos Stavrakakis
                            "cyclades.ram": 1048576 * flavor.ram}
341 bb649089 Christos Stavrakakis
        if action == "BUILD":
342 bb649089 Christos Stavrakakis
            resources.update(online_resources)
343 bb649089 Christos Stavrakakis
            return resources
344 41a7fae7 Christos Stavrakakis
        if action == "START":
345 41a7fae7 Christos Stavrakakis
            if resource.operstate == "STOPPED":
346 41a7fae7 Christos Stavrakakis
                return online_resources
347 41a7fae7 Christos Stavrakakis
            else:
348 41a7fae7 Christos Stavrakakis
                return None
349 41a7fae7 Christos Stavrakakis
        elif action == "STOP":
350 41a7fae7 Christos Stavrakakis
            if resource.operstate in ["STARTED", "BUILD", "ERROR"]:
351 41a7fae7 Christos Stavrakakis
                return reverse_quantities(online_resources)
352 41a7fae7 Christos Stavrakakis
            else:
353 41a7fae7 Christos Stavrakakis
                return None
354 41a7fae7 Christos Stavrakakis
        elif action == "REBOOT":
355 41a7fae7 Christos Stavrakakis
            if resource.operstate == "STOPPED":
356 41a7fae7 Christos Stavrakakis
                return online_resources
357 41a7fae7 Christos Stavrakakis
            else:
358 41a7fae7 Christos Stavrakakis
                return None
359 41a7fae7 Christos Stavrakakis
        elif action == "DESTROY":
360 41a7fae7 Christos Stavrakakis
            if resource.operstate in ["STARTED", "BUILD", "ERROR"]:
361 41a7fae7 Christos Stavrakakis
                resources.update(online_resources)
362 41a7fae7 Christos Stavrakakis
            return reverse_quantities(resources)
363 41a7fae7 Christos Stavrakakis
        elif action == "RESIZE" and action_fields:
364 41a7fae7 Christos Stavrakakis
            beparams = action_fields.get("beparams")
365 41a7fae7 Christos Stavrakakis
            cpu = beparams.get("vcpus", flavor.cpu)
366 41a7fae7 Christos Stavrakakis
            ram = beparams.get("maxmem", flavor.ram)
367 5083fc47 Christos Stavrakakis
            return {"cyclades.total_cpu": cpu - flavor.cpu,
368 5083fc47 Christos Stavrakakis
                    "cyclades.total_ram": 1048576 * (ram - flavor.ram)}
369 99667854 Giorgos Korfiatis
        elif action == "REASSIGN":
370 99667854 Giorgos Korfiatis
            if resource.operstate in ["STARTED", "BUILD", "ERROR"]:
371 99667854 Giorgos Korfiatis
                resources.update(online_resources)
372 99667854 Giorgos Korfiatis
            return resources
373 41a7fae7 Christos Stavrakakis
        else:
374 41a7fae7 Christos Stavrakakis
            #["CONNECT", "DISCONNECT", "SET_FIREWALL_PROFILE"]:
375 41a7fae7 Christos Stavrakakis
            return None
376 16b959ce Giorgos Korfiatis
    elif isinstance(resource, Network):
377 16b959ce Giorgos Korfiatis
        resources = {"cyclades.network.private": 1}
378 16b959ce Giorgos Korfiatis
        if action == "BUILD":
379 16b959ce Giorgos Korfiatis
            return resources
380 16b959ce Giorgos Korfiatis
        elif action == "DESTROY":
381 16b959ce Giorgos Korfiatis
            return reverse_quantities(resources)
382 99667854 Giorgos Korfiatis
        elif action == "REASSIGN":
383 99667854 Giorgos Korfiatis
            return resources
384 44183264 Christos Stavrakakis
    elif isinstance(resource, IPAddress):
385 44183264 Christos Stavrakakis
        if resource.floating_ip:
386 44183264 Christos Stavrakakis
            resources = {"cyclades.floating_ip": 1}
387 44183264 Christos Stavrakakis
            if action == "BUILD":
388 44183264 Christos Stavrakakis
                return resources
389 44183264 Christos Stavrakakis
            elif action == "DESTROY":
390 44183264 Christos Stavrakakis
                return reverse_quantities(resources)
391 99667854 Giorgos Korfiatis
            elif action == "REASSIGN":
392 99667854 Giorgos Korfiatis
                return resources
393 44183264 Christos Stavrakakis
        else:
394 44183264 Christos Stavrakakis
            return None
395 41a7fae7 Christos Stavrakakis
396 41a7fae7 Christos Stavrakakis
397 2509ce17 Christos Stavrakakis
def reverse_quantities(resources):
398 2509ce17 Christos Stavrakakis
    return dict((r, -s) for r, s in resources.items())
399 41a7fae7 Christos Stavrakakis
400 41a7fae7 Christos Stavrakakis
401 5c8076b6 Christos Stavrakakis
def handle_resource_commission(resource, action, commission_name,
402 16b959ce Giorgos Korfiatis
                               force=False, auto_accept=False,
403 16b959ce Giorgos Korfiatis
                               action_fields=None):
404 5c8076b6 Christos Stavrakakis
    """Handle a issuing of a commission for a resource.
405 5c8076b6 Christos Stavrakakis

406 5c8076b6 Christos Stavrakakis
    Create a new commission for a resource based on the action that
407 5c8076b6 Christos Stavrakakis
    is performed. If the resource has a previous pending commission,
408 5c8076b6 Christos Stavrakakis
    resolved it before issuing the new one.
409 5c8076b6 Christos Stavrakakis

410 5c8076b6 Christos Stavrakakis
    """
411 1fdd8d69 Christos Stavrakakis
    # Try to resolve previous serial:
412 1fdd8d69 Christos Stavrakakis
    # If action is DESTROY, we must always reject the previous commission,
413 1fdd8d69 Christos Stavrakakis
    # since multiple DESTROY actions are allowed in the same resource (e.g. VM)
414 1fdd8d69 Christos Stavrakakis
    # The one who succeeds will be finally accepted, and all other will be
415 1fdd8d69 Christos Stavrakakis
    # rejected
416 1fdd8d69 Christos Stavrakakis
    force = force or (action == "DESTROY")
417 ba777b02 Giorgos Korfiatis
    resolve_resource_commission(resource, force=force)
418 5c8076b6 Christos Stavrakakis
419 16b959ce Giorgos Korfiatis
    serial = issue_commission(resource, action, name=commission_name,
420 16b959ce Giorgos Korfiatis
                              force=force, auto_accept=auto_accept,
421 16b959ce Giorgos Korfiatis
                              action_fields=action_fields)
422 21eb7404 Christos Stavrakakis
    return serial
423 5c8076b6 Christos Stavrakakis
424 5c8076b6 Christos Stavrakakis
425 fb540e3a Giorgos Korfiatis
class ResolveError(Exception):
426 fb540e3a Giorgos Korfiatis
    pass
427 fb540e3a Giorgos Korfiatis
428 fb540e3a Giorgos Korfiatis
429 ba777b02 Giorgos Korfiatis
def resolve_resource_commission(resource, force=False):
430 ba777b02 Giorgos Korfiatis
    serial = resource.serial
431 5c8076b6 Christos Stavrakakis
    if serial is None or serial.resolved:
432 5c8076b6 Christos Stavrakakis
        return
433 fb540e3a Giorgos Korfiatis
    if serial.pending and not force:
434 fb540e3a Giorgos Korfiatis
        m = "Could not resolve commission: serial %s is undecided" % serial
435 fb540e3a Giorgos Korfiatis
        raise ResolveError(m)
436 41a7fae7 Christos Stavrakakis
    log.warning("Resolving pending commission: %s", serial)
437 41a7fae7 Christos Stavrakakis
    if not serial.pending and serial.accept:
438 ba777b02 Giorgos Korfiatis
        accept_resource_serial(resource)
439 41a7fae7 Christos Stavrakakis
    else:
440 ba777b02 Giorgos Korfiatis
        reject_resource_serial(resource)