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) |