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