Revision 5d805533 snf-cyclades-app/synnefo/volume/volumes.py
b/snf-cyclades-app/synnefo/volume/volumes.py | ||
---|---|---|
1 | 1 |
import logging |
2 | 2 |
|
3 | 3 |
from django.db import transaction |
4 |
from synnefo.db.models import Volume |
|
5 | 4 |
from snf_django.lib.api import faults |
5 |
from synnefo.db.models import Volume |
|
6 | 6 |
from synnefo.volume import util |
7 |
from synnefo.logic import backend, servers
|
|
7 |
from synnefo.logic import server_attachments
|
|
8 | 8 |
|
9 | 9 |
log = logging.getLogger(__name__) |
10 | 10 |
|
... | ... | |
14 | 14 |
source_volume_id=None, source_snapshot_id=None, |
15 | 15 |
source_image_id=None, metadata=None): |
16 | 16 |
|
17 |
# Currently we cannot create volumes without being attached to a server |
|
17 | 18 |
if server_id is None: |
18 | 19 |
raise faults.BadRequest("Volume must be attached to server") |
19 | 20 |
server = util.get_server(user_id, server_id, for_update=True, |
... | ... | |
25 | 26 |
if len(sources) > 1: |
26 | 27 |
raise faults.BadRequest("Volume can not have more than one source!") |
27 | 28 |
|
28 |
# Only ext_ disk template supports cloning from another source |
|
29 |
if source_volume_id is not None: |
|
30 |
source_type = "volume" |
|
31 |
source_uuid = source_volume_id |
|
32 |
elif source_snapshot_id is not None: |
|
33 |
source_type = "snapshot" |
|
34 |
source_uuid = source_snapshot_id |
|
35 |
elif source_image_id is not None: |
|
36 |
source_type = "image" |
|
37 |
source_uuid = source_image_id |
|
38 |
else: |
|
39 |
source_type = source_uuid = None |
|
40 |
|
|
41 |
volume = _create_volume(server, user_id, size, source_type, source_uuid, |
|
42 |
name, description, index=None) |
|
43 |
|
|
44 |
if metadata is not None: |
|
45 |
for meta_key, meta_val in metadata.items(): |
|
46 |
volume.metadata.create(key=meta_key, value=meta_val) |
|
47 |
|
|
48 |
server_attachments.attach_volume(server, volume) |
|
49 |
|
|
50 |
return volume |
|
51 |
|
|
52 |
|
|
53 |
def _create_volume(server, user_id, size, source_type, source_uuid, |
|
54 |
name=None, description=None, index=None, |
|
55 |
delete_on_termination=True): |
|
56 |
|
|
57 |
# Only ext_ disk template supports cloning from another source. Otherwise |
|
58 |
# is must be the root volume so that 'snf-image' fill the volume |
|
29 | 59 |
disk_template = server.flavor.disk_template |
30 |
if not disk_template.startswith("ext_") and sources: |
|
60 |
can_have_source = (index == 0 or disk_template.startswith("ext_")) |
|
61 |
if not can_have_source and source_type != "blank": |
|
31 | 62 |
msg = ("Volumes of '%s' disk template cannot have a source" % |
32 | 63 |
disk_template) |
33 | 64 |
raise faults.BadRequest(msg) |
34 | 65 |
|
35 |
origin = None |
|
36 |
source = None |
|
37 |
if source_volume_id is not None: |
|
38 |
source_volume = util.get_volume(user_id, source_volume_id, |
|
66 |
# TODO: Check Volume/Snapshot Status |
|
67 |
if source_type == "volume": |
|
68 |
source_volume = util.get_volume(user_id, source_uuid, |
|
39 | 69 |
for_update=True, |
40 | 70 |
exception=faults.BadRequest) |
41 |
# Check that volume is ready to be snapshotted |
|
42 |
if source_volume.status != "AVAILABLE": |
|
43 |
msg = ("Cannot take a snapshot while snapshot is in '%s' state" |
|
44 |
% source_volume.status) |
|
45 |
raise faults.BadRequest(msg) |
|
46 |
source = Volume.prefix_source(source_volume_id, source_type="volume") |
|
71 |
if source_volume.status != "IN_USE": |
|
72 |
raise faults.BadRequest("Cannot clone volume while it is in '%s'" |
|
73 |
" status" % source_volume.status) |
|
74 |
# If no size is specified, use the size of the volume |
|
75 |
if size is None: |
|
76 |
size = source_volume.size |
|
77 |
elif size < source_volume.size: |
|
78 |
raise faults.BadRequest("Volume size cannot be smaller than the" |
|
79 |
" source volume") |
|
80 |
source = Volume.prefix_source(source_uuid, source_type="volume") |
|
47 | 81 |
origin = source_volume.backend_volume_uuid |
48 |
elif source_snapshot_id is not None:
|
|
49 |
source_snapshot = util.get_snapshot(user_id, source_snapshot_id,
|
|
82 |
elif source_type == "snapshot":
|
|
83 |
source_snapshot = util.get_snapshot(user_id, source_uuid,
|
|
50 | 84 |
exception=faults.BadRequest) |
51 |
# TODO: Check the state of the snapshot!! |
|
52 |
source = Volume.prefix_source(source_snapshot_id, |
|
85 |
source = Volume.prefix_source(source_uuid, |
|
53 | 86 |
source_type="snapshot") |
87 |
if size is None: |
|
88 |
raise faults.BadRequest("Volume size is required") |
|
89 |
elif (size << 30) < int(source_snapshot["size"]): |
|
90 |
raise faults.BadRequest("Volume size '%s' is smaller than" |
|
91 |
" snapshot's size '%s'" |
|
92 |
% (size << 30, source_snapshot["size"])) |
|
54 | 93 |
origin = source_snapshot["checksum"] |
55 |
elif source_image_id is not None:
|
|
56 |
source_image = util.get_image(user_id, source_image_id,
|
|
94 |
elif source_type == "image":
|
|
95 |
source_image = util.get_image(user_id, source_uuid,
|
|
57 | 96 |
exception=faults.BadRequest) |
58 |
source = Volume.prefix_source(source_image_id, source_type="image") |
|
97 |
if size is None: |
|
98 |
raise faults.BadRequest("Volume size is required") |
|
99 |
elif (size << 30) < int(source_image["size"]): |
|
100 |
raise faults.BadRequest("Volume size '%s' is smaller than" |
|
101 |
" image's size '%s'" |
|
102 |
% (size << 30, source_image["size"])) |
|
103 |
source = Volume.prefix_source(source_uuid, source_type="image") |
|
59 | 104 |
origin = source_image["checksum"] |
105 |
elif source_type == "blank": |
|
106 |
if size is None: |
|
107 |
raise faults.BadRequest("Volume size is required") |
|
108 |
source = origin = None |
|
109 |
else: |
|
110 |
raise faults.BadRequest("Unknwon source type") |
|
60 | 111 |
|
61 | 112 |
volume = Volume.objects.create(userid=user_id, |
62 | 113 |
size=size, |
63 | 114 |
name=name, |
64 | 115 |
machine=server, |
65 | 116 |
description=description, |
117 |
delete_on_termination=delete_on_termination, |
|
66 | 118 |
source=source, |
67 | 119 |
origin=origin, |
68 | 120 |
#volume_type=volume_type, |
69 | 121 |
status="CREATING") |
70 |
|
|
71 |
if metadata is not None: |
|
72 |
for meta_key, meta_val in metadata.items(): |
|
73 |
volume.metadata.create(key=meta_key, value=meta_val) |
|
74 |
|
|
75 |
servers.attach_volume(server, volume) |
|
76 |
|
|
77 | 122 |
return volume |
78 | 123 |
|
79 | 124 |
|
... | ... | |
83 | 128 |
# A volume is deleted by detaching it from the server that is attached. |
84 | 129 |
# Deleting a detached volume is not implemented. |
85 | 130 |
if volume.machine_id is not None: |
86 |
servers.detach_volume(volume.machine, volume) |
|
131 |
server_attachments.detach_volume(volume.machine, volume)
|
|
87 | 132 |
log.info("Detach volume '%s' from server '%s', job: %s", |
88 | 133 |
volume.id, volume.machine_id, volume.backendjobid) |
89 | 134 |
else: |
Also available in: Unified diff