Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / volume / views.py @ 18ca395d

History | View | Annotate | Download (12.7 kB)

1 dec501fa Christos Stavrakakis
# Copyright 2013 GRNET S.A. All rights reserved.
2 dec501fa Christos Stavrakakis
#
3 dec501fa Christos Stavrakakis
# Redistribution and use in source and binary forms, with or
4 dec501fa Christos Stavrakakis
# without modification, are permitted provided that the following
5 dec501fa Christos Stavrakakis
# conditions are met:
6 dec501fa Christos Stavrakakis
#
7 dec501fa Christos Stavrakakis
#   1. Redistributions of source code must retain the above
8 dec501fa Christos Stavrakakis
#      copyright notice, this list of conditions and the following
9 dec501fa Christos Stavrakakis
#      disclaimer.
10 dec501fa Christos Stavrakakis
#
11 dec501fa Christos Stavrakakis
#   2. Redistributions in binary form must reproduce the above
12 dec501fa Christos Stavrakakis
#      copyright notice, this list of conditions and the following
13 dec501fa Christos Stavrakakis
#      disclaimer in the documentation and/or other materials
14 dec501fa Christos Stavrakakis
#      provided with the distribution.
15 dec501fa Christos Stavrakakis
#
16 dec501fa Christos Stavrakakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 dec501fa Christos Stavrakakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 dec501fa Christos Stavrakakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 dec501fa Christos Stavrakakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 dec501fa Christos Stavrakakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 dec501fa Christos Stavrakakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 dec501fa Christos Stavrakakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 dec501fa Christos Stavrakakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 dec501fa Christos Stavrakakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 dec501fa Christos Stavrakakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 dec501fa Christos Stavrakakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 dec501fa Christos Stavrakakis
# POSSIBILITY OF SUCH DAMAGE.
28 dec501fa Christos Stavrakakis
#
29 dec501fa Christos Stavrakakis
# The views and conclusions contained in the software and
30 dec501fa Christos Stavrakakis
# documentation are those of the authors and should not be
31 dec501fa Christos Stavrakakis
# interpreted as representing official policies, either expressed
32 dec501fa Christos Stavrakakis
# or implied, of GRNET S.A.
33 dec501fa Christos Stavrakakis
34 dec501fa Christos Stavrakakis
from itertools import ifilter
35 dec501fa Christos Stavrakakis
from logging import getLogger
36 dec501fa Christos Stavrakakis
from django.http import HttpResponse
37 dec501fa Christos Stavrakakis
from django.utils import simplejson as json
38 dec501fa Christos Stavrakakis
39 dec501fa Christos Stavrakakis
import datetime
40 dec501fa Christos Stavrakakis
from dateutil.parser import parse as date_parse
41 dec501fa Christos Stavrakakis
42 dec501fa Christos Stavrakakis
from snf_django.lib import api
43 dec501fa Christos Stavrakakis
from snf_django.lib.api import faults, utils
44 dec501fa Christos Stavrakakis
45 dec501fa Christos Stavrakakis
from synnefo.volume import volumes, snapshots, util
46 dec501fa Christos Stavrakakis
from synnefo.db.models import Volume
47 dec501fa Christos Stavrakakis
from synnefo.plankton.utils import image_backend
48 dec501fa Christos Stavrakakis
log = getLogger('synnefo.volume')
49 dec501fa Christos Stavrakakis
50 dec501fa Christos Stavrakakis
51 dec501fa Christos Stavrakakis
def display_null_field(field):
52 dec501fa Christos Stavrakakis
    if field is None:
53 dec501fa Christos Stavrakakis
        return None
54 dec501fa Christos Stavrakakis
    else:
55 dec501fa Christos Stavrakakis
        str(field)
56 dec501fa Christos Stavrakakis
57 dec501fa Christos Stavrakakis
58 dec501fa Christos Stavrakakis
def volume_to_dict(volume, detail=True):
59 dec501fa Christos Stavrakakis
    data = {
60 dec501fa Christos Stavrakakis
        "id": str(volume.id),
61 5f90e24c Christos Stavrakakis
        "display_name": display_null_field(volume.name),
62 5f90e24c Christos Stavrakakis
        "links": util.volume_to_dict(volume.id),
63 dec501fa Christos Stavrakakis
    }
64 dec501fa Christos Stavrakakis
    if detail:
65 dec501fa Christos Stavrakakis
        details = {
66 dec501fa Christos Stavrakakis
            "status": volume.status.lower(),
67 dec501fa Christos Stavrakakis
            "size": volume.size,
68 5f90e24c Christos Stavrakakis
            "display_description": volume.description,
69 dec501fa Christos Stavrakakis
            "created_at": utils.isoformat(volume.created),
70 dec501fa Christos Stavrakakis
            "metadata": dict((m.key, m.value) for m in volume.metadata.all()),
71 dec501fa Christos Stavrakakis
            "snapshot_id": display_null_field(volume.source_snapshot_id),
72 dec501fa Christos Stavrakakis
            "source_volid": display_null_field(volume.source_volume_id),
73 dec501fa Christos Stavrakakis
            "image_id": display_null_field(volume.source_image_id),
74 dec501fa Christos Stavrakakis
            "attachments": get_volume_attachments(volume),
75 dec501fa Christos Stavrakakis
            # TODO:
76 dec501fa Christos Stavrakakis
            "volume_type": None,
77 5f90e24c Christos Stavrakakis
            "delete_on_termination": volume.delete_on_termination,
78 dec501fa Christos Stavrakakis
            #"availabilit_zone": None,
79 dec501fa Christos Stavrakakis
            #"bootable": None,
80 dec501fa Christos Stavrakakis
            #"os-vol-tenant-attr:tenant_id": None,
81 dec501fa Christos Stavrakakis
            #"os-vol-host-attr:host": None,
82 dec501fa Christos Stavrakakis
            #"os-vol-mig-status-attr:name_id": None,
83 dec501fa Christos Stavrakakis
            #"os-vol-mig-status-attr:migstat": None,
84 dec501fa Christos Stavrakakis
        }
85 dec501fa Christos Stavrakakis
        data.update(details)
86 dec501fa Christos Stavrakakis
    return data
87 dec501fa Christos Stavrakakis
88 dec501fa Christos Stavrakakis
89 dec501fa Christos Stavrakakis
def get_volume_attachments(volume):
90 dec501fa Christos Stavrakakis
    if volume.machine_id is None:
91 dec501fa Christos Stavrakakis
        return []
92 dec501fa Christos Stavrakakis
    else:
93 dec501fa Christos Stavrakakis
        return [{"server_id": volume.machine_id,
94 dec501fa Christos Stavrakakis
                 "volume_id": volume.id,
95 dec501fa Christos Stavrakakis
                 "device_index": volume.index}]
96 dec501fa Christos Stavrakakis
97 dec501fa Christos Stavrakakis
98 dec501fa Christos Stavrakakis
@api.api_method(http_method="POST", user_required=True, logger=log)
99 dec501fa Christos Stavrakakis
def create_volume(request):
100 dec501fa Christos Stavrakakis
    """Create a new Volume."""
101 dec501fa Christos Stavrakakis
102 dec501fa Christos Stavrakakis
    req = utils.get_request_dict(request)
103 dec501fa Christos Stavrakakis
    log.debug("create_volume %s", req)
104 dec501fa Christos Stavrakakis
    user_id = request.user_uniq
105 e7953d63 Christos Stavrakakis
106 e7953d63 Christos Stavrakakis
    new_volume = req.get("volume")
107 e7953d63 Christos Stavrakakis
    if new_volume is None:
108 e7953d63 Christos Stavrakakis
        raise faults.BadRequest("Missing 'volume' attribute.")
109 e7953d63 Christos Stavrakakis
110 dec501fa Christos Stavrakakis
    # Get and validate 'name' parameter
111 dec501fa Christos Stavrakakis
    # TODO: auto generate name
112 5f90e24c Christos Stavrakakis
    name = new_volume.get("display_name", None)
113 dec501fa Christos Stavrakakis
    if name is None:
114 dec501fa Christos Stavrakakis
        raise faults.BadRequest("Volume 'name' is needed.")
115 dec501fa Christos Stavrakakis
    # Get and validate 'size' parameter
116 e7953d63 Christos Stavrakakis
    size = new_volume.get("size")
117 dec501fa Christos Stavrakakis
    if size is None:
118 dec501fa Christos Stavrakakis
        raise faults.BadRequest("Volume 'size' is needed.")
119 dec501fa Christos Stavrakakis
    try:
120 dec501fa Christos Stavrakakis
        size = int(size)
121 dec501fa Christos Stavrakakis
        if size <= 0:
122 dec501fa Christos Stavrakakis
            raise ValueError
123 dec501fa Christos Stavrakakis
    except ValueError:
124 dec501fa Christos Stavrakakis
        raise faults.BadRequest("Volume 'size' needs to be a positive integer"
125 dec501fa Christos Stavrakakis
                                " value. '%s' cannot be accepted." % size)
126 dec501fa Christos Stavrakakis
127 dec501fa Christos Stavrakakis
    # TODO: Fix volume type, validate, etc..
128 e7953d63 Christos Stavrakakis
    volume_type = new_volume.get("volume_type", None)
129 dec501fa Christos Stavrakakis
130 dec501fa Christos Stavrakakis
    # Optional parameters
131 5f90e24c Christos Stavrakakis
    description = new_volume.get("display_description", "")
132 e7953d63 Christos Stavrakakis
    metadata = new_volume.get("metadata", {})
133 dec501fa Christos Stavrakakis
    if not isinstance(metadata, dict):
134 dec501fa Christos Stavrakakis
        msg = "Volume 'metadata' needs to be a dictionary of key-value pairs."\
135 dec501fa Christos Stavrakakis
              " '%s' can not be accepted." % metadata
136 dec501fa Christos Stavrakakis
        raise faults.BadRequest(msg)
137 dec501fa Christos Stavrakakis
138 dec501fa Christos Stavrakakis
    # Id of the volume to clone from
139 e7953d63 Christos Stavrakakis
    source_volume_id = new_volume.get("source_volid")
140 dec501fa Christos Stavrakakis
    # Id of the snapshot to create the volume from
141 e7953d63 Christos Stavrakakis
    source_snapshot_id = new_volume.get("snapshot_id")
142 dec501fa Christos Stavrakakis
    # Reference to an Image stored in Glance
143 e7953d63 Christos Stavrakakis
    source_image_id = new_volume.get("imageRef")
144 dec501fa Christos Stavrakakis
    # TODO: Check that not all of them are used
145 dec501fa Christos Stavrakakis
146 e7953d63 Christos Stavrakakis
    server_id = new_volume.get("server_id")
147 dec501fa Christos Stavrakakis
    if server_id is None:
148 dec501fa Christos Stavrakakis
        raise faults.BadRequest("Attribute 'server_id' is mandatory")
149 dec501fa Christos Stavrakakis
150 dec501fa Christos Stavrakakis
    # Create the volume
151 dec501fa Christos Stavrakakis
    volume = volumes.create(user_id=user_id, size=size, name=name,
152 dec501fa Christos Stavrakakis
                            source_volume_id=source_volume_id,
153 dec501fa Christos Stavrakakis
                            source_snapshot_id=source_snapshot_id,
154 dec501fa Christos Stavrakakis
                            source_image_id=source_image_id,
155 dec501fa Christos Stavrakakis
                            volume_type=volume_type, description=description,
156 dec501fa Christos Stavrakakis
                            metadata=metadata, server_id=server_id)
157 dec501fa Christos Stavrakakis
158 dec501fa Christos Stavrakakis
    # Render response
159 dec501fa Christos Stavrakakis
    data = json.dumps(dict(volume=volume_to_dict(volume, detail=False)))
160 dec501fa Christos Stavrakakis
    return HttpResponse(data, status=200)
161 dec501fa Christos Stavrakakis
162 dec501fa Christos Stavrakakis
163 dec501fa Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
164 dec501fa Christos Stavrakakis
def list_volumes(request, detail=False):
165 dec501fa Christos Stavrakakis
    log.debug('list_volumes detail=%s', detail)
166 5f90e24c Christos Stavrakakis
    volumes = Volume.objects.filter(userid=request.user_uniq).order_by("id")
167 dec501fa Christos Stavrakakis
168 5f90e24c Christos Stavrakakis
    volumes = utils.filter_modified_since(request, objects=volumes)
169 dec501fa Christos Stavrakakis
170 5f90e24c Christos Stavrakakis
    volumes = [volume_to_dict(v, detail) for v in volumes]
171 dec501fa Christos Stavrakakis
172 dec501fa Christos Stavrakakis
    data = json.dumps({'volumes': volumes})
173 dec501fa Christos Stavrakakis
    return HttpResponse(data, content_type="application/json", status=200)
174 dec501fa Christos Stavrakakis
175 dec501fa Christos Stavrakakis
176 dec501fa Christos Stavrakakis
@api.api_method(http_method="DELETE", user_required=True, logger=log)
177 dec501fa Christos Stavrakakis
def delete_volume(request, volume_id):
178 dec501fa Christos Stavrakakis
    log.debug("delete_volume volume_id: %s", volume_id)
179 dec501fa Christos Stavrakakis
180 dec501fa Christos Stavrakakis
    volume = util.get.volume(request.user_uniq, volume_id, for_update=True)
181 dec501fa Christos Stavrakakis
    volumes.delete(volume)
182 dec501fa Christos Stavrakakis
183 dec501fa Christos Stavrakakis
    return HttpResponse(status=202)
184 dec501fa Christos Stavrakakis
185 dec501fa Christos Stavrakakis
186 dec501fa Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
187 dec501fa Christos Stavrakakis
def get_volume(request, volume_id):
188 dec501fa Christos Stavrakakis
    log.debug('get_volume volume_id: %s', volume_id)
189 dec501fa Christos Stavrakakis
190 dec501fa Christos Stavrakakis
    volume = util.get.volume(request.user_uniq, volume_id)
191 dec501fa Christos Stavrakakis
192 dec501fa Christos Stavrakakis
    data = json.dumps({'volume': volume_to_dict(volume, detail=True)})
193 dec501fa Christos Stavrakakis
    return HttpResponse(data, content_type="application/json", status=200)
194 dec501fa Christos Stavrakakis
195 dec501fa Christos Stavrakakis
196 dec501fa Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
197 dec501fa Christos Stavrakakis
def update_volume(request, volume_id):
198 dec501fa Christos Stavrakakis
    req = utils.get_request_dict(request)
199 dec501fa Christos Stavrakakis
    log.debug('update_volume volume_id: %s, request: %s', volume_id, req)
200 dec501fa Christos Stavrakakis
201 dec501fa Christos Stavrakakis
    volume = util.get.volume(request.user_uniq, volume_id, for_update=True)
202 dec501fa Christos Stavrakakis
203 5f90e24c Christos Stavrakakis
    new_name = req.get("display_name")
204 5f90e24c Christos Stavrakakis
    description = req.get("display_description")
205 dec501fa Christos Stavrakakis
206 dec501fa Christos Stavrakakis
    if new_name is None and description is None:
207 dec501fa Christos Stavrakakis
        raise faults.BadRequest("Nothing to update.")
208 dec501fa Christos Stavrakakis
209 dec501fa Christos Stavrakakis
    if new_name is not None:
210 dec501fa Christos Stavrakakis
        volume = volumes.rename(volume, new_name)
211 dec501fa Christos Stavrakakis
    if description is not None:
212 dec501fa Christos Stavrakakis
        volume = volumes.update_description(volume, description)
213 dec501fa Christos Stavrakakis
    data = json.dumps({'volume': volume_to_dict(volume, detail=True)})
214 dec501fa Christos Stavrakakis
    return HttpResponse(data, content_type="application/json", status=200)
215 dec501fa Christos Stavrakakis
216 dec501fa Christos Stavrakakis
217 dec501fa Christos Stavrakakis
def snapshot_to_dict(snapshot, detail=True):
218 e7953d63 Christos Stavrakakis
    owner = snapshot['owner']
219 e7953d63 Christos Stavrakakis
    status = snapshot['status']
220 e7953d63 Christos Stavrakakis
    progress = "%s%%" % 100 if status == "ACTIVE" else 0
221 e7953d63 Christos Stavrakakis
222 dec501fa Christos Stavrakakis
    data = {
223 dec501fa Christos Stavrakakis
        "id": snapshot["uuid"],
224 dec501fa Christos Stavrakakis
        "size": int(snapshot["size"]) >> 30,  # gigabytes
225 18ca395d Christos Stavrakakis
        "display_name": snapshot["name"],
226 18ca395d Christos Stavrakakis
        "display_description": snapshot["description"],
227 dec501fa Christos Stavrakakis
        "status": status,
228 dec501fa Christos Stavrakakis
        "user_id": owner,
229 dec501fa Christos Stavrakakis
        "tenant_id": owner,
230 dec501fa Christos Stavrakakis
        "os-extended-snapshot-attribute:progress": progress,
231 dec501fa Christos Stavrakakis
        #"os-extended-snapshot-attribute:project_id": project,
232 dec501fa Christos Stavrakakis
        "created_at": utils.isoformat(date_parse(snapshot["created_at"])),
233 dec501fa Christos Stavrakakis
        "metadata": snapshot.get("metadata", {}),
234 dec501fa Christos Stavrakakis
        "volume_id": snapshot.get("volume_id"),
235 18ca395d Christos Stavrakakis
        "links": util.snapshot_to_links(snapshot["uuid"])
236 dec501fa Christos Stavrakakis
    }
237 dec501fa Christos Stavrakakis
    return data
238 dec501fa Christos Stavrakakis
239 dec501fa Christos Stavrakakis
240 dec501fa Christos Stavrakakis
@api.api_method(http_method="POST", user_required=True, logger=log)
241 dec501fa Christos Stavrakakis
def create_snapshot(request):
242 dec501fa Christos Stavrakakis
    """Create a new Snapshot."""
243 dec501fa Christos Stavrakakis
244 dec501fa Christos Stavrakakis
    req = utils.get_request_dict(request)
245 dec501fa Christos Stavrakakis
    log.debug("create_snapshot %s", req)
246 dec501fa Christos Stavrakakis
    user_id = request.user_uniq
247 e7953d63 Christos Stavrakakis
248 e7953d63 Christos Stavrakakis
    new_snapshot = req.get("snapshot")
249 e7953d63 Christos Stavrakakis
    if new_snapshot is None:
250 e7953d63 Christos Stavrakakis
        raise faults.BadRequest("Missing 'snapshot' attribute.")
251 e7953d63 Christos Stavrakakis
252 dec501fa Christos Stavrakakis
    # Get and validate 'name' parameter
253 dec501fa Christos Stavrakakis
    # TODO: auto generate name
254 e7953d63 Christos Stavrakakis
    metadata = new_snapshot.get("metadata", {})
255 dec501fa Christos Stavrakakis
    if not isinstance(metadata, dict):
256 dec501fa Christos Stavrakakis
        msg = "Snapshot 'metadata' needs to be a dictionary of key-value"\
257 dec501fa Christos Stavrakakis
              " pairs. '%s' can not be accepted." % metadata
258 dec501fa Christos Stavrakakis
        raise faults.BadRequest(msg)
259 dec501fa Christos Stavrakakis
260 e7953d63 Christos Stavrakakis
    volume_id = new_snapshot.get("volume_id", None)
261 dec501fa Christos Stavrakakis
    if volume_id is None:
262 dec501fa Christos Stavrakakis
        raise faults.BadRequest("'volume_id' attribute is missing.")
263 dec501fa Christos Stavrakakis
    volume = util.get_volume(user_id, volume_id, for_update=True,
264 dec501fa Christos Stavrakakis
                             exception=faults.BadRequest)
265 dec501fa Christos Stavrakakis
266 18ca395d Christos Stavrakakis
    name = new_snapshot.get("display_name", None)
267 dec501fa Christos Stavrakakis
    if name is None:
268 dec501fa Christos Stavrakakis
        name = "snapshot_volume_%s_%s" %\
269 dec501fa Christos Stavrakakis
            (volume.id, str(datetime.datetime.now()))
270 18ca395d Christos Stavrakakis
    description = new_snapshot.get("display_description", "")
271 dec501fa Christos Stavrakakis
272 dec501fa Christos Stavrakakis
    # TODO: What to do with force ?
273 e7953d63 Christos Stavrakakis
    force = new_snapshot.get("force", False)
274 dec501fa Christos Stavrakakis
    if not isinstance(force, bool):
275 dec501fa Christos Stavrakakis
        raise faults.BadRequest("Invalid value for 'force' attribute.")
276 dec501fa Christos Stavrakakis
277 dec501fa Christos Stavrakakis
    snapshot = snapshots.create(user_id=user_id, volume=volume, name=name,
278 dec501fa Christos Stavrakakis
                                description=description, metadata=metadata,
279 dec501fa Christos Stavrakakis
                                force=force)
280 dec501fa Christos Stavrakakis
281 dec501fa Christos Stavrakakis
    # Render response
282 dec501fa Christos Stavrakakis
    data = json.dumps(dict(snapshot=snapshot_to_dict(snapshot, detail=False)))
283 e7953d63 Christos Stavrakakis
    return HttpResponse(data, status=202)
284 dec501fa Christos Stavrakakis
285 dec501fa Christos Stavrakakis
286 dec501fa Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
287 dec501fa Christos Stavrakakis
def list_snapshots(request, detail=False):
288 dec501fa Christos Stavrakakis
    log.debug('list_snapshots detail=%s', detail)
289 dec501fa Christos Stavrakakis
    since = utils.isoparse(request.GET.get('changes-since'))
290 dec501fa Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
291 dec501fa Christos Stavrakakis
        snapshots = backend.list_snapshots()
292 dec501fa Christos Stavrakakis
        if since:
293 dec501fa Christos Stavrakakis
            updated_since = lambda snap:\
294 dec501fa Christos Stavrakakis
                date_parse(snap["updated_at"]) >= since
295 dec501fa Christos Stavrakakis
            snapshots = ifilter(updated_since, snapshots)
296 dec501fa Christos Stavrakakis
            if not snapshots:
297 dec501fa Christos Stavrakakis
                return HttpResponse(status=304)
298 dec501fa Christos Stavrakakis
299 dec501fa Christos Stavrakakis
    snapshots = sorted(snapshots, key=lambda x: x['id'])
300 dec501fa Christos Stavrakakis
    snapshots_dict = [snapshot_to_dict(snapshot, detail)
301 dec501fa Christos Stavrakakis
                      for snapshot in snapshots]
302 dec501fa Christos Stavrakakis
303 dec501fa Christos Stavrakakis
    data = json.dumps(dict(snapshots=snapshots_dict))
304 dec501fa Christos Stavrakakis
305 dec501fa Christos Stavrakakis
    return HttpResponse(data, status=200)
306 dec501fa Christos Stavrakakis
307 dec501fa Christos Stavrakakis
308 dec501fa Christos Stavrakakis
@api.api_method(http_method="DELETE", user_required=True, logger=log)
309 dec501fa Christos Stavrakakis
def delete_snapshot(request, snapshot_id):
310 dec501fa Christos Stavrakakis
    log.debug("delete_snapshot snapshot_id: %s", snapshot_id)
311 dec501fa Christos Stavrakakis
312 dec501fa Christos Stavrakakis
    snapshot = util.get_snapshot(request.user_uniq, snapshot_id)
313 dec501fa Christos Stavrakakis
    snapshots.delete(snapshot)
314 dec501fa Christos Stavrakakis
315 dec501fa Christos Stavrakakis
    return HttpResponse(status=202)
316 dec501fa Christos Stavrakakis
317 dec501fa Christos Stavrakakis
318 dec501fa Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
319 dec501fa Christos Stavrakakis
def get_snapshot(request, snapshot_id):
320 dec501fa Christos Stavrakakis
    log.debug('get_snapshot snapshot_id: %s', snapshot_id)
321 dec501fa Christos Stavrakakis
322 dec501fa Christos Stavrakakis
    snapshot = util.get_snapshot(request.user_uniq, snapshot_id)
323 dec501fa Christos Stavrakakis
    data = json.dumps({'snapshot': snapshot_to_dict(snapshot, detail=True)})
324 dec501fa Christos Stavrakakis
    return HttpResponse(data, content_type="application/json", status=200)
325 dec501fa Christos Stavrakakis
326 dec501fa Christos Stavrakakis
327 dec501fa Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
328 dec501fa Christos Stavrakakis
def update_snapshot(request, snapshot_id):
329 dec501fa Christos Stavrakakis
    req = utils.get_request_dict(request)
330 dec501fa Christos Stavrakakis
    log.debug('update_snapshot snapshot_id: %s, request: %s', snapshot_id, req)
331 dec501fa Christos Stavrakakis
    snapshot = util.get_snapshot(request.user_uniq, snapshot_id)
332 18ca395d Christos Stavrakakis
333 18ca395d Christos Stavrakakis
    new_name = req.get("display_name")
334 18ca395d Christos Stavrakakis
    new_description = req.get("display_description")
335 18ca395d Christos Stavrakakis
    if new_name is None and new_description is None:
336 18ca395d Christos Stavrakakis
        raise faults.BadRequest("Nothing to update.")
337 18ca395d Christos Stavrakakis
338 18ca395d Christos Stavrakakis
    if new_name is not None:
339 18ca395d Christos Stavrakakis
        snapshot = snapshots.rename(snapshot, new_name)
340 18ca395d Christos Stavrakakis
    if new_description is not None:
341 18ca395d Christos Stavrakakis
        snapshot = snapshots.update_description(snapshot, new_description)
342 18ca395d Christos Stavrakakis
343 dec501fa Christos Stavrakakis
    data = json.dumps({'snapshot': snapshot_to_dict(snapshot, detail=True)})
344 dec501fa Christos Stavrakakis
    return HttpResponse(data, content_type="application/json", status=200)