Revision adc46059
b/snf-cyclades-app/synnefo/api/servers.py | ||
---|---|---|
186 | 186 |
|
187 | 187 |
|
188 | 188 |
@util.api_method('POST') |
189 |
@transaction.commit_on_success |
|
189 |
# Use manual transactions. Backend and IP pool allocations need exclusive |
|
190 |
# access (SELECT..FOR UPDATE). Running create_server with commit_on_success |
|
191 |
# would result in backends and public networks to be locked until the job is |
|
192 |
# sent to the Ganeti backend. |
|
193 |
@transaction.commit_manually |
|
190 | 194 |
def create_server(request): |
191 | 195 |
# Normal Response Code: 202 |
192 | 196 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
255 | 259 |
|
256 | 260 |
backend_allocator = BackendAllocator() |
257 | 261 |
backend = backend_allocator.allocate(flavor) |
262 |
|
|
258 | 263 |
if backend is None: |
264 |
transaction.rollback() |
|
259 | 265 |
raise Exception |
266 |
transaction.commit() |
|
267 |
|
|
268 |
if settings.PUBLIC_ROUTED_USE_POOL: |
|
269 |
(network, address) = util.allocate_public_address(backend) |
|
270 |
if address is None: |
|
271 |
transaction.rollback() |
|
272 |
raise faults.OverLimit("Can not allocate IP for new machine." |
|
273 |
" Public networks are full.") |
|
274 |
transaction.commit() |
|
275 |
nic = {'ip': address, 'network': network.backend_id} |
|
276 |
else: |
|
277 |
nic = {'ip': 'pool', 'network': network.backend_id} |
|
260 | 278 |
|
261 | 279 |
# We must save the VM instance now, so that it gets a valid |
262 | 280 |
# vm.backend_vm_id. |
... | ... | |
268 | 286 |
flavor=flavor) |
269 | 287 |
|
270 | 288 |
try: |
271 |
jobID = create_instance(vm, flavor, image, password, personality) |
|
289 |
jobID = create_instance(vm, nic, flavor, image, password, personality)
|
|
272 | 290 |
except GanetiApiError: |
273 | 291 |
vm.delete() |
274 | 292 |
raise |
... | ... | |
288 | 306 |
server = vm_to_dict(vm, detail=True) |
289 | 307 |
server['status'] = 'BUILD' |
290 | 308 |
server['adminPass'] = password |
291 |
return render_server(request, server, status=202) |
|
309 |
|
|
310 |
respsone = render_server(request, server, status=202) |
|
311 |
transaction.commit() |
|
312 |
|
|
313 |
return respsone |
|
292 | 314 |
|
293 | 315 |
|
294 | 316 |
@util.api_method('GET') |
b/snf-cyclades-app/synnefo/api/util.py | ||
---|---|---|
60 | 60 |
from synnefo.db.models import (Flavor, VirtualMachine, VirtualMachineMetadata, |
61 | 61 |
Network, BackendNetwork, NetworkInterface, |
62 | 62 |
BridgePoolTable, MacPrefixPoolTable) |
63 |
from synnefo.db.pools import EmptyPool |
|
63 | 64 |
|
64 | 65 |
from synnefo.lib.astakos import get_user |
65 | 66 |
from synnefo.plankton.backend import ImageBackend |
... | ... | |
217 | 218 |
return cidr_block <= 29 and cidr_block > MAX_CIDR_BLOCK |
218 | 219 |
|
219 | 220 |
|
221 |
def allocate_public_address(backend): |
|
222 |
"""Allocate a public IP for a vm.""" |
|
223 |
for network in backend_public_networks(backend): |
|
224 |
try: |
|
225 |
address = get_network_free_address(network) |
|
226 |
return (network, address) |
|
227 |
except EmptyPool: |
|
228 |
pass |
|
229 |
return (None, None) |
|
230 |
|
|
231 |
|
|
220 | 232 |
def backend_public_networks(backend): |
221 | 233 |
"""Return available public networks of the backend. |
222 | 234 |
|
b/snf-cyclades-app/synnefo/db/managers.py | ||
---|---|---|
97 | 97 |
params) |
98 | 98 |
|
99 | 99 |
|
100 |
class ProtectedDeleteManager(Manager): |
|
100 |
class ProtectedDeleteManager(ForUpdateManager):
|
|
101 | 101 |
""" Manager for protecting Backend deletion. |
102 | 102 |
|
103 | 103 |
Call Backend delete() method in order to prevent deletion |
b/snf-cyclades-app/synnefo/logic/backend.py | ||
---|---|---|
41 | 41 |
from synnefo.db.models import (Backend, VirtualMachine, Network, |
42 | 42 |
BackendNetwork, BACKEND_STATUSES) |
43 | 43 |
from synnefo.logic import utils |
44 |
from synnefo.db.pools import EmptyPool |
|
45 |
from synnefo.api.faults import OverLimit |
|
46 |
from synnefo.api.util import backend_public_networks, get_network_free_address |
|
47 | 44 |
from synnefo.util.rapi import GanetiRapiClient |
48 | 45 |
|
49 | 46 |
log = getLogger('synnefo.logic') |
... | ... | |
258 | 255 |
vm.save() |
259 | 256 |
|
260 | 257 |
|
261 |
@transaction.commit_on_success |
|
262 |
def create_instance(vm, flavor, image, password, personality): |
|
258 |
def create_instance(vm, public_nic, flavor, image, password, personality): |
|
263 | 259 |
"""`image` is a dictionary which should contain the keys: |
264 | 260 |
'backend_id', 'format' and 'metadata' |
265 | 261 |
|
266 | 262 |
metadata value should be a dictionary. |
267 | 263 |
""" |
268 | 264 |
|
269 |
if settings.PUBLIC_ROUTED_USE_POOL: |
|
270 |
(network, address) = allocate_public_address(vm) |
|
271 |
if address is None: |
|
272 |
raise OverLimit("Can not allocate IP for new machine." |
|
273 |
" Public networks are full.") |
|
274 |
nic = {'ip': address, 'network': network.backend_id} |
|
275 |
else: |
|
276 |
nic = {'ip': 'pool', 'network': network.backend_id} |
|
277 |
|
|
278 | 265 |
if settings.IGNORE_FLAVOR_DISK_SIZES: |
279 | 266 |
if image['backend_id'].find("windows") >= 0: |
280 | 267 |
sz = 14000 |
... | ... | |
308 | 295 |
if provider: |
309 | 296 |
kw['disks'][0]['provider'] = provider |
310 | 297 |
|
311 |
kw['nics'] = [nic] |
|
298 |
kw['nics'] = [public_nic]
|
|
312 | 299 |
if settings.GANETI_USE_HOTPLUG: |
313 | 300 |
kw['hotplug'] = True |
314 | 301 |
# Defined in settings.GANETI_CREATEINSTANCE_KWARGS |
... | ... | |
341 | 328 |
return vm.client.CreateInstance(**kw) |
342 | 329 |
|
343 | 330 |
|
344 |
def allocate_public_address(vm): |
|
345 |
"""Allocate a public IP for a vm.""" |
|
346 |
for network in backend_public_networks(vm.backend): |
|
347 |
try: |
|
348 |
address = get_network_free_address(network) |
|
349 |
return (network, address) |
|
350 |
except EmptyPool: |
|
351 |
pass |
|
352 |
return (None, None) |
|
353 |
|
|
354 |
|
|
355 | 331 |
def delete_instance(vm): |
356 | 332 |
start_action(vm, 'DESTROY') |
357 | 333 |
vm.client.DeleteInstance(vm.backend_vm_id, dry_run=settings.TEST) |
b/snf-cyclades-app/synnefo/logic/backend_allocator.py | ||
---|---|---|
47 | 47 |
importlib.import_module(settings.BACKEND_ALLOCATOR_MODULE) |
48 | 48 |
|
49 | 49 |
def allocate(self, flavor): |
50 |
"""Allocate a vm of the specified flavor to a backend. |
|
51 |
|
|
52 |
Warning!!: An explicit commit is required after calling this function, |
|
53 |
in order to release the locks acquired by the get_available_backends |
|
54 |
function. |
|
55 |
|
|
56 |
""" |
|
50 | 57 |
# Get the size of the vm |
51 | 58 |
disk = flavor_disk(flavor) |
52 | 59 |
ram = flavor.ram |
... | ... | |
81 | 88 |
"""Get available backends from db. |
82 | 89 |
|
83 | 90 |
""" |
84 |
return Backend.objects.filter(drained=False, offline=False) |
|
91 |
return list(Backend.objects.select_for_update().filter(drained=False, |
|
92 |
offline=False)) |
|
85 | 93 |
|
86 | 94 |
|
87 | 95 |
def flavor_disk(flavor): |
Also available in: Unified diff