Revision 864bed43
b/snf-cyclades-app/synnefo/api/actions.py | ||
---|---|---|
306 | 306 |
raise BadRequest('Malformed Request.') |
307 | 307 |
vm = get_vm(server_id, request.user_uniq) |
308 | 308 |
|
309 |
#Dummy write to enforce isolation
|
|
310 |
Network.objects.filter(id=net.id).update(id=net.id)
|
|
311 |
net = Network.objects.get(id=net.id) |
|
309 |
# Get the Network object in exclusive mode in order to
|
|
310 |
# guarantee consistency of the pool
|
|
311 |
net = Network.objects.select_for_update().get(id=net.id)
|
|
312 | 312 |
# Get a free IP from the address pool. |
313 | 313 |
pool = ippool.IPPool(net) |
314 | 314 |
try: |
b/snf-cyclades-app/synnefo/db/managers.py | ||
---|---|---|
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 django.db import connections |
|
31 |
from django.db.models import Manager |
|
32 |
from django.db.models.query import QuerySet |
|
33 |
|
|
34 |
|
|
35 |
class ForUpdateManager(Manager): |
|
36 |
""" Model manager implementing SELECT .. FOR UPDATE statement |
|
37 |
|
|
38 |
This manager implements select_for_update() method in order to use |
|
39 |
row-level locking in the database and guarantee exclusive access, since |
|
40 |
this method is only implemented in Django>=1.4. |
|
41 |
|
|
42 |
Non-blocking reads are not implemented, and each query including a row |
|
43 |
that is locked by another transaction will block until the lock is |
|
44 |
released. Also care must be taken in order to avoid deadlocks or retry |
|
45 |
transactions that abort due to deadlocks. |
|
46 |
|
|
47 |
Example: |
|
48 |
networks = Network.objects.select_for_update().filter(public=True) |
|
49 |
|
|
50 |
""" |
|
51 |
|
|
52 |
def __init__(self, *args, **kwargs): |
|
53 |
super(ForUpdateManager, self).__init__(*args, **kwargs) |
|
54 |
self._select_for_update = False |
|
55 |
|
|
56 |
def filter(self, *args, **kwargs): |
|
57 |
query = self.get_query_set().filter(*args, **kwargs) |
|
58 |
if self._select_for_update: |
|
59 |
self._select_for_update = False |
|
60 |
return for_update(query) |
|
61 |
else: |
|
62 |
return query |
|
63 |
|
|
64 |
def get(self, *args, **kwargs): |
|
65 |
if not self._select_for_update: |
|
66 |
return self.get_query_set().get(*args, **kwargs) |
|
67 |
|
|
68 |
query = self.filter(*args, **kwargs) |
|
69 |
query = list(query) |
|
70 |
num = len(query) |
|
71 |
if num == 1: |
|
72 |
return query[0] |
|
73 |
if not num: |
|
74 |
raise self.model.DoesNotExist( |
|
75 |
"%s matching query does not exist. " |
|
76 |
"Lookup parameters were %s" % |
|
77 |
(self.model._meta.object_name, kwargs)) |
|
78 |
raise self.model.MultipleObjectsReturned( |
|
79 |
"get() returned more than one %s -- it returned %s! " |
|
80 |
"Lookup parameters were %s" % |
|
81 |
(self.model._meta.object_name, num, kwargs)) |
|
82 |
|
|
83 |
def select_for_update(self, *args, **kwargs): |
|
84 |
self._select_for_update = True |
|
85 |
return self |
|
86 |
|
|
87 |
|
|
88 |
def for_update(query): |
|
89 |
""" Rewrite query using SELECT .. FOR UPDATE. |
|
90 |
|
|
91 |
""" |
|
92 |
if 'sqlite' in connections[query.db].settings_dict['ENGINE'].lower(): |
|
93 |
# SQLite does not support FOR UPDATE |
|
94 |
return query |
|
95 |
sql, params = query.query.get_compiler(query.db).as_sql() |
|
96 |
return query.model._default_manager.raw(sql.rstrip() + ' FOR UPDATE', |
|
97 |
params) |
|
98 |
|
|
99 |
|
|
100 |
class ProtectedDeleteManager(Manager): |
|
101 |
""" Manager for protecting Backend deletion. |
|
102 |
|
|
103 |
Call Backend delete() method in order to prevent deletion |
|
104 |
of Backends that host non-deleted VirtualMachines. |
|
105 |
|
|
106 |
""" |
|
107 |
|
|
108 |
def get_query_set(self): |
|
109 |
return BackendQuerySet(self.model, using=self._db) |
|
110 |
|
|
111 |
|
|
112 |
class BackendQuerySet(QuerySet): |
|
113 |
def delete(self): |
|
114 |
for backend in self._clone(): |
|
115 |
backend.delete() |
b/snf-cyclades-app/synnefo/db/models.py | ||
---|---|---|
40 | 40 |
from synnefo import settings as snf_settings |
41 | 41 |
from aes_encrypt import encrypt_db_charfield, decrypt_db_charfield |
42 | 42 |
|
43 |
from synnefo.db.managers import ForUpdateManager, ProtectedDeleteManager |
|
44 |
|
|
43 | 45 |
BACKEND_CLIENTS = {} # {hash:Backend client} |
44 | 46 |
BACKEND_HASHES = {} # {Backend.id:hash} |
45 | 47 |
|
... | ... | |
113 | 115 |
return self.name |
114 | 116 |
|
115 | 117 |
|
116 |
class BackendQuerySet(models.query.QuerySet): |
|
117 |
def delete(self): |
|
118 |
for backend in self._clone(): |
|
119 |
backend.delete() |
|
120 |
|
|
121 |
|
|
122 |
class ProtectDeleteManager(models.Manager): |
|
123 |
def get_query_set(self): |
|
124 |
return BackendQuerySet(self.model, using=self._db) |
|
125 |
|
|
126 |
|
|
127 | 118 |
class Backend(models.Model): |
128 | 119 |
clustername = models.CharField('Cluster Name', max_length=128, unique=True) |
129 | 120 |
port = models.PositiveIntegerField('Port', default=5080) |
... | ... | |
147 | 138 |
ctotal = models.PositiveIntegerField('Total number of logical processors', |
148 | 139 |
default=0, null=False) |
149 | 140 |
# Custom object manager to protect from cascade delete |
150 |
objects = ProtectDeleteManager() |
|
141 |
objects = ProtectedDeleteManager()
|
|
151 | 142 |
|
152 | 143 |
class Meta: |
153 | 144 |
verbose_name = u'Backend' |
... | ... | |
408 | 399 |
return u'%s: %s' % (self.meta_key, self.meta_value) |
409 | 400 |
|
410 | 401 |
|
402 |
|
|
411 | 403 |
class Network(models.Model): |
412 | 404 |
OPER_STATES = ( |
413 | 405 |
('PENDING', 'Pending'), |
... | ... | |
462 | 454 |
|
463 | 455 |
ip_pool = None |
464 | 456 |
|
457 |
objects = ForUpdateManager() |
|
465 | 458 |
|
466 | 459 |
class InvalidBackendIdError(Exception): |
467 | 460 |
def __init__(self, value): |
... | ... | |
562 | 555 |
pool._update_network() |
563 | 556 |
self.save() |
564 | 557 |
|
558 |
|
|
565 | 559 |
# def get_free_address(self): |
566 | 560 |
# # Get yourself inside a transaction |
567 | 561 |
# network = Network.objects.get(id=self.id) |
b/snf-cyclades-app/synnefo/logic/backend.py | ||
---|---|---|
40 | 40 |
|
41 | 41 |
from synnefo.db.models import (Backend, VirtualMachine, Network, |
42 | 42 |
BackendNetwork, BACKEND_STATUSES) |
43 |
from synnefo.logic import utils |
|
43 |
from synnefo.logic import utils, ippool |
|
44 |
from synnefo.api.faults import ServiceUnavailable |
|
44 | 45 |
from synnefo.util.rapi import GanetiRapiClient |
45 | 46 |
|
46 | 47 |
log = getLogger('synnefo.logic') |
... | ... | |
125 | 126 |
if pk in networks: |
126 | 127 |
net = networks[pk] |
127 | 128 |
else: |
128 |
#Dummy write to enforce isolation
|
|
129 |
Network.objects.filter(id=pk).update(id=pk)
|
|
130 |
net = Network.objects.get(pk=pk) |
|
129 |
# Get the network object in exclusive mode in order
|
|
130 |
# to guarantee consistency of the address pool
|
|
131 |
net = Network.objects.select_for_update().get(pk=pk)
|
|
131 | 132 |
net.release_address(old_nic.ipv4) |
132 | 133 |
old_nic.delete() |
133 | 134 |
|
... | ... | |
140 | 141 |
if pk in networks: |
141 | 142 |
net = networks[pk] |
142 | 143 |
else: |
143 |
net = Network.objects.get(pk=pk) |
|
144 |
net = Network.objects.select_for_update().get(pk=pk)
|
|
144 | 145 |
|
145 | 146 |
# Get the new nic info |
146 | 147 |
mac = new_nic.get('mac', '') |
... | ... | |
272 | 273 |
|
273 | 274 |
metadata value should be a dictionary. |
274 | 275 |
""" |
275 |
nic = {'ip': 'pool', 'network': settings.GANETI_PUBLIC_NETWORK} |
|
276 |
|
|
277 |
# Get the Network object in exclusive mode in order to |
|
278 |
# safely (isolated) reserve an IP address |
|
279 |
network = Network.objects.select_for_update().get(id=1) |
|
280 |
pool = ippool.IPPool(network) |
|
281 |
try: |
|
282 |
address = pool.get_free_address() |
|
283 |
except ippool.IPPool.IPPoolExhausted: |
|
284 |
raise ServiceUnavailable('Network is full') |
|
285 |
pool.save() |
|
286 |
|
|
287 |
nic = {'ip': address, 'network': settings.GANETI_PUBLIC_NETWORK} |
|
276 | 288 |
|
277 | 289 |
if settings.IGNORE_FLAVOR_DISK_SIZES: |
278 | 290 |
if image['backend_id'].find("windows") >= 0: |
Also available in: Unified diff