Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / volume / views.py @ 19b2c29d

History | View | Annotate | Download (12.4 kB)

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