Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (13.1 kB)

1 629acc65 Giorgos Korfiatis
# Copyright 2012, 2013 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 629acc65 Giorgos Korfiatis
DEFAULT_SOURCE = 'system'
47 656cf771 Christos Stavrakakis
RESOURCES = [
48 656cf771 Christos Stavrakakis
    "cyclades.vm",
49 5083fc47 Christos Stavrakakis
    "cyclades.total_cpu",
50 656cf771 Christos Stavrakakis
    "cyclades.cpu",
51 656cf771 Christos Stavrakakis
    "cyclades.disk",
52 5083fc47 Christos Stavrakakis
    "cyclades.total_ram",
53 656cf771 Christos Stavrakakis
    "cyclades.ram",
54 8b41683a Christos Stavrakakis
    "cyclades.network.private",
55 8b41683a Christos Stavrakakis
    "cyclades.floating_ip",
56 656cf771 Christos Stavrakakis
]
57 bfe7ba3c Christos Stavrakakis
58 bfe7ba3c Christos Stavrakakis
59 629acc65 Giorgos Korfiatis
class Quotaholder(object):
60 629acc65 Giorgos Korfiatis
    _object = None
61 629acc65 Giorgos Korfiatis
62 629acc65 Giorgos Korfiatis
    @classmethod
63 629acc65 Giorgos Korfiatis
    def get(cls):
64 629acc65 Giorgos Korfiatis
        if cls._object is None:
65 4fb643b3 Giorgos Korfiatis
            cls._object = AstakosClient(ASTAKOS_TOKEN,
66 4fb643b3 Giorgos Korfiatis
                                        ASTAKOS_AUTH_URL,
67 4fb643b3 Giorgos Korfiatis
                                        use_pool=True,
68 4fb643b3 Giorgos Korfiatis
                                        retry=3,
69 4fb643b3 Giorgos Korfiatis
                                        logger=log)
70 629acc65 Giorgos Korfiatis
        return cls._object
71 bfe7ba3c Christos Stavrakakis
72 bfe7ba3c Christos Stavrakakis
73 0dc4b304 Giorgos Korfiatis
class AstakosClientExceptionHandler(object):
74 0dc4b304 Giorgos Korfiatis
    def __init__(self, *args, **kwargs):
75 0dc4b304 Giorgos Korfiatis
        pass
76 0dc4b304 Giorgos Korfiatis
77 0dc4b304 Giorgos Korfiatis
    def __enter__(self):
78 0dc4b304 Giorgos Korfiatis
        pass
79 0dc4b304 Giorgos Korfiatis
80 0dc4b304 Giorgos Korfiatis
    def __exit__(self, exc_type, value, traceback):
81 0dc4b304 Giorgos Korfiatis
        if value is not None:  # exception
82 0dc4b304 Giorgos Korfiatis
            if not isinstance(value, errors.AstakosClientException):
83 0dc4b304 Giorgos Korfiatis
                return False  # reraise
84 0dc4b304 Giorgos Korfiatis
            if exc_type is errors.QuotaLimit:
85 0dc4b304 Giorgos Korfiatis
                msg, details = render_overlimit_exception(value)
86 0dc4b304 Giorgos Korfiatis
                raise faults.OverLimit(msg, details=details)
87 0dc4b304 Giorgos Korfiatis
88 0dc4b304 Giorgos Korfiatis
            log.exception("Unexpected error %s" % value.message)
89 499d9bfe Christos Stavrakakis
            raise faults.InternalServerError("Unexpected error")
90 499d9bfe Christos Stavrakakis
91 499d9bfe Christos Stavrakakis
92 16b959ce Giorgos Korfiatis
def issue_commission(resource, action, name="", force=False, auto_accept=False,
93 16b959ce Giorgos Korfiatis
                     action_fields=None):
94 bfe7ba3c Christos Stavrakakis
    """Issue a new commission to the quotaholder.
95 bfe7ba3c Christos Stavrakakis

96 bfe7ba3c Christos Stavrakakis
    Issue a new commission to the quotaholder, and create the
97 bfe7ba3c Christos Stavrakakis
    corresponing QuotaHolderSerial object in DB.
98 bfe7ba3c Christos Stavrakakis

99 bfe7ba3c Christos Stavrakakis
    """
100 bfe7ba3c Christos Stavrakakis
101 16b959ce Giorgos Korfiatis
    provisions = get_commission_info(resource=resource, action=action,
102 16b959ce Giorgos Korfiatis
                                     action_fields=action_fields)
103 16b959ce Giorgos Korfiatis
104 16b959ce Giorgos Korfiatis
    if provisions is None:
105 16b959ce Giorgos Korfiatis
        return None
106 16b959ce Giorgos Korfiatis
107 16b959ce Giorgos Korfiatis
    user = resource.userid
108 16b959ce Giorgos Korfiatis
    source = DEFAULT_SOURCE
109 16b959ce Giorgos Korfiatis
110 629acc65 Giorgos Korfiatis
    qh = Quotaholder.get()
111 0dc4b304 Giorgos Korfiatis
    if True:  # placeholder
112 0dc4b304 Giorgos Korfiatis
        with AstakosClientExceptionHandler():
113 16b959ce Giorgos Korfiatis
            serial = qh.issue_one_commission(user, source,
114 16b959ce Giorgos Korfiatis
                                             provisions, name=name,
115 16b959ce Giorgos Korfiatis
                                             force=force,
116 16b959ce Giorgos Korfiatis
                                             auto_accept=auto_accept)
117 bfe7ba3c Christos Stavrakakis
118 b14f55b5 Christos Stavrakakis
    if serial:
119 218de705 Christos Stavrakakis
        serial_info = {"serial": serial}
120 218de705 Christos Stavrakakis
        if auto_accept:
121 fb540e3a Giorgos Korfiatis
            serial_info["pending"] = False
122 218de705 Christos Stavrakakis
            serial_info["accept"] = True
123 218de705 Christos Stavrakakis
            serial_info["resolved"] = True
124 218de705 Christos Stavrakakis
        return QuotaHolderSerial.objects.create(**serial_info)
125 b14f55b5 Christos Stavrakakis
    else:
126 b14f55b5 Christos Stavrakakis
        raise Exception("No serial")
127 bfe7ba3c Christos Stavrakakis
128 bfe7ba3c Christos Stavrakakis
129 41a7fae7 Christos Stavrakakis
def accept_serial(serial, strict=True):
130 fb540e3a Giorgos Korfiatis
    assert serial.pending or serial.accept
131 41a7fae7 Christos Stavrakakis
    response = resolve_commissions(accept=[serial.serial], strict=strict)
132 fb540e3a Giorgos Korfiatis
    serial.pending = False
133 41a7fae7 Christos Stavrakakis
    serial.accept = True
134 41a7fae7 Christos Stavrakakis
    serial.resolved = True
135 41a7fae7 Christos Stavrakakis
    serial.save()
136 41a7fae7 Christos Stavrakakis
    return response
137 41a7fae7 Christos Stavrakakis
138 41a7fae7 Christos Stavrakakis
139 41a7fae7 Christos Stavrakakis
def reject_serial(serial, strict=True):
140 fb540e3a Giorgos Korfiatis
    assert serial.pending or not serial.accept
141 41a7fae7 Christos Stavrakakis
    response = resolve_commissions(reject=[serial.serial], strict=strict)
142 fb540e3a Giorgos Korfiatis
    serial.pending = False
143 fb540e3a Giorgos Korfiatis
    serial.accept = False
144 41a7fae7 Christos Stavrakakis
    serial.resolved = True
145 41a7fae7 Christos Stavrakakis
    serial.save()
146 41a7fae7 Christos Stavrakakis
    return response
147 41a7fae7 Christos Stavrakakis
148 41a7fae7 Christos Stavrakakis
149 d195e752 Christos Stavrakakis
def resolve_commissions(accept=None, reject=None, strict=True):
150 629acc65 Giorgos Korfiatis
    if accept is None:
151 629acc65 Giorgos Korfiatis
        accept = []
152 629acc65 Giorgos Korfiatis
    if reject is None:
153 629acc65 Giorgos Korfiatis
        reject = []
154 11a54cb9 Christos Stavrakakis
155 629acc65 Giorgos Korfiatis
    qh = Quotaholder.get()
156 0dc4b304 Giorgos Korfiatis
    with AstakosClientExceptionHandler():
157 0dc4b304 Giorgos Korfiatis
        response = qh.resolve_commissions(accept, reject)
158 5db3a1e6 Christos Stavrakakis
159 3af1fb4b Christos Stavrakakis
    # Update correspodning entries in DB
160 3af1fb4b Christos Stavrakakis
    QuotaHolderSerial.objects.filter(serial__in=accept).update(accept=True,
161 3af1fb4b Christos Stavrakakis
                                                               pending=False,
162 3af1fb4b Christos Stavrakakis
                                                               resolved=True)
163 3af1fb4b Christos Stavrakakis
    QuotaHolderSerial.objects.filter(serial__in=reject).update(accept=False,
164 3af1fb4b Christos Stavrakakis
                                                               pending=False,
165 3af1fb4b Christos Stavrakakis
                                                               resolved=True)
166 3af1fb4b Christos Stavrakakis
167 5db3a1e6 Christos Stavrakakis
    if strict:
168 5db3a1e6 Christos Stavrakakis
        failed = response["failed"]
169 5db3a1e6 Christos Stavrakakis
        if failed:
170 5db3a1e6 Christos Stavrakakis
            log.error("Unexpected error while resolving commissions: %s",
171 5db3a1e6 Christos Stavrakakis
                      failed)
172 5db3a1e6 Christos Stavrakakis
173 5db3a1e6 Christos Stavrakakis
    return response
174 11a54cb9 Christos Stavrakakis
175 11a54cb9 Christos Stavrakakis
176 11a54cb9 Christos Stavrakakis
def resolve_pending_commissions():
177 11a54cb9 Christos Stavrakakis
    """Resolve quotaholder pending commissions.
178 11a54cb9 Christos Stavrakakis

179 11a54cb9 Christos Stavrakakis
    Get pending commissions from the quotaholder and resolve them
180 11a54cb9 Christos Stavrakakis
    to accepted and rejected, according to the state of the
181 11a54cb9 Christos Stavrakakis
    QuotaHolderSerial DB table. A pending commission in the quotaholder
182 11a54cb9 Christos Stavrakakis
    can exist in the QuotaHolderSerial table and be either accepted or
183 8d5795b4 Christos Stavrakakis
    rejected, or cannot exist in this table, so it is rejected.
184 11a54cb9 Christos Stavrakakis

185 11a54cb9 Christos Stavrakakis
    """
186 11a54cb9 Christos Stavrakakis
187 11a54cb9 Christos Stavrakakis
    qh_pending = get_quotaholder_pending()
188 11a54cb9 Christos Stavrakakis
    if not qh_pending:
189 11a54cb9 Christos Stavrakakis
        return ([], [])
190 11a54cb9 Christos Stavrakakis
191 11a54cb9 Christos Stavrakakis
    qh_pending.sort()
192 11a54cb9 Christos Stavrakakis
    min_ = qh_pending[0]
193 11a54cb9 Christos Stavrakakis
194 11a54cb9 Christos Stavrakakis
    serials = QuotaHolderSerial.objects.filter(serial__gte=min_, pending=False)
195 2509ce17 Christos Stavrakakis
    accepted = serials.filter(accept=True).values_list('serial', flat=True)
196 11a54cb9 Christos Stavrakakis
    accepted = filter(lambda x: x in qh_pending, accepted)
197 11a54cb9 Christos Stavrakakis
198 11a54cb9 Christos Stavrakakis
    rejected = list(set(qh_pending) - set(accepted))
199 11a54cb9 Christos Stavrakakis
200 11a54cb9 Christos Stavrakakis
    return (accepted, rejected)
201 11a54cb9 Christos Stavrakakis
202 11a54cb9 Christos Stavrakakis
203 11a54cb9 Christos Stavrakakis
def get_quotaholder_pending():
204 629acc65 Giorgos Korfiatis
    qh = Quotaholder.get()
205 e407f159 Ilias Tsitsimpis
    pending_serials = qh.get_pending_commissions()
206 11a54cb9 Christos Stavrakakis
    return pending_serials
207 5a70b1a3 Christos Stavrakakis
208 5a70b1a3 Christos Stavrakakis
209 629acc65 Giorgos Korfiatis
def render_overlimit_exception(e):
210 5a70b1a3 Christos Stavrakakis
    resource_name = {"vm": "Virtual Machine",
211 5a70b1a3 Christos Stavrakakis
                     "cpu": "CPU",
212 5a70b1a3 Christos Stavrakakis
                     "ram": "RAM",
213 8b41683a Christos Stavrakakis
                     "network.private": "Private Network",
214 8b41683a Christos Stavrakakis
                     "floating_ip": "Floating IP address"}
215 656cf771 Christos Stavrakakis
    details = json.loads(e.details)
216 629acc65 Giorgos Korfiatis
    data = details['overLimit']['data']
217 656cf771 Christos Stavrakakis
    usage = data["usage"]
218 656cf771 Christos Stavrakakis
    limit = data["limit"]
219 656cf771 Christos Stavrakakis
    available = limit - usage
220 629acc65 Giorgos Korfiatis
    provision = data['provision']
221 629acc65 Giorgos Korfiatis
    requested = provision['quantity']
222 629acc65 Giorgos Korfiatis
    resource = provision['resource']
223 629acc65 Giorgos Korfiatis
    res = resource.replace("cyclades.", "", 1)
224 5a70b1a3 Christos Stavrakakis
    try:
225 5a70b1a3 Christos Stavrakakis
        resource = resource_name[res]
226 5a70b1a3 Christos Stavrakakis
    except KeyError:
227 5a70b1a3 Christos Stavrakakis
        resource = res
228 5a70b1a3 Christos Stavrakakis
229 5a70b1a3 Christos Stavrakakis
    msg = "Resource Limit Exceeded for your account."
230 5a70b1a3 Christos Stavrakakis
    details = "Limit for resource '%s' exceeded for your account."\
231 629acc65 Giorgos Korfiatis
              " Available: %s, Requested: %s"\
232 629acc65 Giorgos Korfiatis
              % (resource, available, requested)
233 5a70b1a3 Christos Stavrakakis
    return msg, details
234 2509ce17 Christos Stavrakakis
235 2509ce17 Christos Stavrakakis
236 21eb7404 Christos Stavrakakis
@transaction.commit_on_success
237 368d879e Giorgos Korfiatis
def issue_and_accept_commission(resource, action="BUILD", action_fields=None):
238 2509ce17 Christos Stavrakakis
    """Issue and accept a commission to Quotaholder.
239 2509ce17 Christos Stavrakakis

240 2509ce17 Christos Stavrakakis
    This function implements the Commission workflow, and must be called
241 2509ce17 Christos Stavrakakis
    exactly after and in the same transaction that created/updated the
242 2509ce17 Christos Stavrakakis
    resource. The workflow that implements is the following:
243 4ed25171 Christos Stavrakakis
    0) Resolve previous unresolved commission if exists
244 21eb7404 Christos Stavrakakis
    1) Issue commission, get a serial and correlate it with the resource
245 21eb7404 Christos Stavrakakis
    2) Store the serial in DB as a serial to accept
246 21eb7404 Christos Stavrakakis
    3) COMMIT!
247 21eb7404 Christos Stavrakakis
    4) Accept commission to QH
248 2509ce17 Christos Stavrakakis

249 2509ce17 Christos Stavrakakis
    """
250 368d879e Giorgos Korfiatis
    commission_reason = ("client: api, resource: %s, action: %s"
251 368d879e Giorgos Korfiatis
                         % (resource, action))
252 21eb7404 Christos Stavrakakis
    serial = handle_resource_commission(resource=resource, action=action,
253 368d879e Giorgos Korfiatis
                                        action_fields=action_fields,
254 21eb7404 Christos Stavrakakis
                                        commission_name=commission_reason)
255 4ed25171 Christos Stavrakakis
256 21eb7404 Christos Stavrakakis
    # Mark the serial as one to accept and associate it with the resource
257 21eb7404 Christos Stavrakakis
    serial.pending = False
258 21eb7404 Christos Stavrakakis
    serial.accept = True
259 21eb7404 Christos Stavrakakis
    serial.save()
260 21eb7404 Christos Stavrakakis
    transaction.commit()
261 2509ce17 Christos Stavrakakis
262 2509ce17 Christos Stavrakakis
    try:
263 41a7fae7 Christos Stavrakakis
        # Accept the commission to quotaholder
264 41a7fae7 Christos Stavrakakis
        accept_serial(serial)
265 2509ce17 Christos Stavrakakis
    except:
266 21eb7404 Christos Stavrakakis
        # Do not crash if we can not accept commission to Quotaholder. Quotas
267 21eb7404 Christos Stavrakakis
        # have already been reserved and the resource already exists in DB.
268 21eb7404 Christos Stavrakakis
        # Just log the error
269 21eb7404 Christos Stavrakakis
        log.exception("Failed to accept commission: %s", serial)
270 21eb7404 Christos Stavrakakis
271 21eb7404 Christos Stavrakakis
    return serial
272 2509ce17 Christos Stavrakakis
273 2509ce17 Christos Stavrakakis
274 41a7fae7 Christos Stavrakakis
def get_commission_info(resource, action, action_fields=None):
275 41a7fae7 Christos Stavrakakis
    if isinstance(resource, VirtualMachine):
276 41a7fae7 Christos Stavrakakis
        flavor = resource.flavor
277 41a7fae7 Christos Stavrakakis
        resources = {"cyclades.vm": 1,
278 5083fc47 Christos Stavrakakis
                     "cyclades.total_cpu": flavor.cpu,
279 41a7fae7 Christos Stavrakakis
                     "cyclades.disk": 1073741824 * flavor.disk,
280 5083fc47 Christos Stavrakakis
                     "cyclades.total_ram": 1048576 * flavor.ram}
281 5083fc47 Christos Stavrakakis
        online_resources = {"cyclades.cpu": flavor.cpu,
282 5083fc47 Christos Stavrakakis
                            "cyclades.ram": 1048576 * flavor.ram}
283 bb649089 Christos Stavrakakis
        if action == "BUILD":
284 bb649089 Christos Stavrakakis
            resources.update(online_resources)
285 bb649089 Christos Stavrakakis
            return resources
286 41a7fae7 Christos Stavrakakis
        if action == "START":
287 41a7fae7 Christos Stavrakakis
            if resource.operstate == "STOPPED":
288 41a7fae7 Christos Stavrakakis
                return online_resources
289 41a7fae7 Christos Stavrakakis
            else:
290 41a7fae7 Christos Stavrakakis
                return None
291 41a7fae7 Christos Stavrakakis
        elif action == "STOP":
292 41a7fae7 Christos Stavrakakis
            if resource.operstate in ["STARTED", "BUILD", "ERROR"]:
293 41a7fae7 Christos Stavrakakis
                return reverse_quantities(online_resources)
294 41a7fae7 Christos Stavrakakis
            else:
295 41a7fae7 Christos Stavrakakis
                return None
296 41a7fae7 Christos Stavrakakis
        elif action == "REBOOT":
297 41a7fae7 Christos Stavrakakis
            if resource.operstate == "STOPPED":
298 41a7fae7 Christos Stavrakakis
                return online_resources
299 41a7fae7 Christos Stavrakakis
            else:
300 41a7fae7 Christos Stavrakakis
                return None
301 41a7fae7 Christos Stavrakakis
        elif action == "DESTROY":
302 41a7fae7 Christos Stavrakakis
            if resource.operstate in ["STARTED", "BUILD", "ERROR"]:
303 41a7fae7 Christos Stavrakakis
                resources.update(online_resources)
304 41a7fae7 Christos Stavrakakis
            return reverse_quantities(resources)
305 41a7fae7 Christos Stavrakakis
        elif action == "RESIZE" and action_fields:
306 41a7fae7 Christos Stavrakakis
            beparams = action_fields.get("beparams")
307 41a7fae7 Christos Stavrakakis
            cpu = beparams.get("vcpus", flavor.cpu)
308 41a7fae7 Christos Stavrakakis
            ram = beparams.get("maxmem", flavor.ram)
309 5083fc47 Christos Stavrakakis
            return {"cyclades.total_cpu": cpu - flavor.cpu,
310 5083fc47 Christos Stavrakakis
                    "cyclades.total_ram": 1048576 * (ram - flavor.ram)}
311 41a7fae7 Christos Stavrakakis
        else:
312 41a7fae7 Christos Stavrakakis
            #["CONNECT", "DISCONNECT", "SET_FIREWALL_PROFILE"]:
313 41a7fae7 Christos Stavrakakis
            return None
314 16b959ce Giorgos Korfiatis
    elif isinstance(resource, Network):
315 16b959ce Giorgos Korfiatis
        resources = {"cyclades.network.private": 1}
316 16b959ce Giorgos Korfiatis
        if action == "BUILD":
317 16b959ce Giorgos Korfiatis
            return resources
318 16b959ce Giorgos Korfiatis
        elif action == "DESTROY":
319 16b959ce Giorgos Korfiatis
            return reverse_quantities(resources)
320 44183264 Christos Stavrakakis
    elif isinstance(resource, IPAddress):
321 44183264 Christos Stavrakakis
        if resource.floating_ip:
322 44183264 Christos Stavrakakis
            resources = {"cyclades.floating_ip": 1}
323 44183264 Christos Stavrakakis
            if action == "BUILD":
324 44183264 Christos Stavrakakis
                return resources
325 44183264 Christos Stavrakakis
            elif action == "DESTROY":
326 44183264 Christos Stavrakakis
                return reverse_quantities(resources)
327 44183264 Christos Stavrakakis
        else:
328 44183264 Christos Stavrakakis
            return None
329 41a7fae7 Christos Stavrakakis
330 41a7fae7 Christos Stavrakakis
331 2509ce17 Christos Stavrakakis
def reverse_quantities(resources):
332 2509ce17 Christos Stavrakakis
    return dict((r, -s) for r, s in resources.items())
333 41a7fae7 Christos Stavrakakis
334 41a7fae7 Christos Stavrakakis
335 5c8076b6 Christos Stavrakakis
def handle_resource_commission(resource, action, commission_name,
336 16b959ce Giorgos Korfiatis
                               force=False, auto_accept=False,
337 16b959ce Giorgos Korfiatis
                               action_fields=None):
338 5c8076b6 Christos Stavrakakis
    """Handle a issuing of a commission for a resource.
339 5c8076b6 Christos Stavrakakis

340 5c8076b6 Christos Stavrakakis
    Create a new commission for a resource based on the action that
341 5c8076b6 Christos Stavrakakis
    is performed. If the resource has a previous pending commission,
342 5c8076b6 Christos Stavrakakis
    resolved it before issuing the new one.
343 5c8076b6 Christos Stavrakakis

344 5c8076b6 Christos Stavrakakis
    """
345 1fdd8d69 Christos Stavrakakis
    # Try to resolve previous serial:
346 1fdd8d69 Christos Stavrakakis
    # If action is DESTROY, we must always reject the previous commission,
347 1fdd8d69 Christos Stavrakakis
    # since multiple DESTROY actions are allowed in the same resource (e.g. VM)
348 1fdd8d69 Christos Stavrakakis
    # The one who succeeds will be finally accepted, and all other will be
349 1fdd8d69 Christos Stavrakakis
    # rejected
350 1fdd8d69 Christos Stavrakakis
    force = force or (action == "DESTROY")
351 fb540e3a Giorgos Korfiatis
    resolve_commission(resource.serial, force=force)
352 5c8076b6 Christos Stavrakakis
353 16b959ce Giorgos Korfiatis
    serial = issue_commission(resource, action, name=commission_name,
354 16b959ce Giorgos Korfiatis
                              force=force, auto_accept=auto_accept,
355 16b959ce Giorgos Korfiatis
                              action_fields=action_fields)
356 5c8076b6 Christos Stavrakakis
    resource.serial = serial
357 16b959ce Giorgos Korfiatis
    resource.save()
358 21eb7404 Christos Stavrakakis
    return serial
359 5c8076b6 Christos Stavrakakis
360 5c8076b6 Christos Stavrakakis
361 fb540e3a Giorgos Korfiatis
class ResolveError(Exception):
362 fb540e3a Giorgos Korfiatis
    pass
363 fb540e3a Giorgos Korfiatis
364 fb540e3a Giorgos Korfiatis
365 fb540e3a Giorgos Korfiatis
def resolve_commission(serial, force=False):
366 5c8076b6 Christos Stavrakakis
    if serial is None or serial.resolved:
367 5c8076b6 Christos Stavrakakis
        return
368 fb540e3a Giorgos Korfiatis
    if serial.pending and not force:
369 fb540e3a Giorgos Korfiatis
        m = "Could not resolve commission: serial %s is undecided" % serial
370 fb540e3a Giorgos Korfiatis
        raise ResolveError(m)
371 41a7fae7 Christos Stavrakakis
    log.warning("Resolving pending commission: %s", serial)
372 41a7fae7 Christos Stavrakakis
    if not serial.pending and serial.accept:
373 41a7fae7 Christos Stavrakakis
        accept_serial(serial)
374 41a7fae7 Christos Stavrakakis
    else:
375 41a7fae7 Christos Stavrakakis
        reject_serial(serial)