Revision b3fd98ae

b/snf-cyclades-app/synnefo/api/flavors.py
38 38
from django.template.loader import render_to_string
39 39
from django.utils import simplejson as json
40 40

  
41
from snf_django.lib import api
41 42
from synnefo.api import util
42 43
from synnefo.db.models import Flavor
43 44

  
......
63 64
    return d
64 65

  
65 66

  
66
@util.api_method('GET')
67
@api.api_method(http_method='GET', user_required=True, logger=log)
67 68
def list_flavors(request, detail=False):
68 69
    # Normal Response Codes: 200, 203
69 70
    # Error Response Codes: computeFault (400, 500),
......
87 88
    return HttpResponse(data, status=200)
88 89

  
89 90

  
90
@util.api_method('GET')
91
@api.api_method(http_method='GET', user_required=True, logger=log)
91 92
def get_flavor_details(request, flavor_id):
92 93
    # Normal Response Codes: 200, 203
93 94
    # Error Response Codes: computeFault (400, 500),
b/snf-cyclades-app/synnefo/api/images.py
33 33

  
34 34
from logging import getLogger
35 35

  
36
import dateutil.parser
36
from dateutil.parser import parse as date_parse
37 37

  
38 38
from django.conf.urls.defaults import patterns
39 39
from django.http import HttpResponse
40 40
from django.template.loader import render_to_string
41 41
from django.utils import simplejson as json
42 42

  
43
from contextlib import contextmanager
44

  
45
from snf_django.lib.api import faults
43
from snf_django.lib import api
44
from snf_django.lib.api import faults, utils
46 45
from synnefo.api import util
47
from synnefo.api.common import method_not_allowed
48
from synnefo.api.util import api_method, isoformat, isoparse
49
from synnefo.plankton.backend import ImageBackend
46
from synnefo.plankton.utils import image_backend
50 47

  
51 48

  
52
log = getLogger('synnefo.api')
49
log = getLogger(__name__)
53 50

  
54 51
urlpatterns = patterns(
55 52
    'synnefo.api.images',
......
67 64
    elif request.method == 'POST':
68 65
        return create_image(request)
69 66
    else:
70
        return method_not_allowed(request)
67
        return api.method_not_allowed(request)
71 68

  
72 69

  
73 70
def image_demux(request, image_id):
......
76 73
    elif request.method == 'DELETE':
77 74
        return delete_image(request, image_id)
78 75
    else:
79
        return method_not_allowed(request)
76
        return api.method_not_allowed(request)
80 77

  
81 78

  
82 79
def metadata_demux(request, image_id):
......
85 82
    elif request.method == 'POST':
86 83
        return update_metadata(request, image_id)
87 84
    else:
88
        return method_not_allowed(request)
85
        return api.method_not_allowed(request)
89 86

  
90 87

  
91 88
def metadata_item_demux(request, image_id, key):
......
96 93
    elif request.method == 'DELETE':
97 94
        return delete_metadata_item(request, image_id, key)
98 95
    else:
99
        return method_not_allowed(request)
96
        return api.method_not_allowed(request)
100 97

  
101 98

  
102 99
def image_to_dict(image, detail=True):
103 100
    d = dict(id=image['id'], name=image['name'])
104 101
    if detail:
105
        d['updated'] = isoformat(dateutil.parser.parse(image['updated_at']))
106
        d['created'] = isoformat(dateutil.parser.parse(image['created_at']))
102
        d['updated'] = utils.isoformat(date_parse(image['updated_at']))
103
        d['created'] = utils.isoformat(date_parse(image['created_at']))
107 104
        d['status'] = 'DELETED' if image['deleted_at'] else 'ACTIVE'
108 105
        d['progress'] = 100 if image['status'] == 'available' else 0
109 106
        if image['properties']:
......
111 108
    return d
112 109

  
113 110

  
114
@contextmanager
115
def image_backend(userid):
116
    backend = ImageBackend(userid)
117
    try:
118
        yield backend
119
    finally:
120
        backend.close()
121

  
122

  
123
@api_method('GET')
111
@api.api_method("GET", user_required=True, logger=log)
124 112
def list_images(request, detail=False):
125 113
    # Normal Response Codes: 200, 203
126 114
    # Error Response Codes: computeFault (400, 500),
......
131 119

  
132 120
    log.debug('list_images detail=%s', detail)
133 121
    with image_backend(request.user_uniq) as backend:
134
        since = isoparse(request.GET.get('changes-since'))
122
        since = utils.isoparse(request.GET.get('changes-since'))
135 123
        if since:
136 124
            images = []
137 125
            for image in backend.iter():
138
                updated = dateutil.parser.parse(image['updated_at'])
126
                updated = date_parse(image['updated_at'])
139 127
                if updated >= since:
140 128
                    images.append(image)
141 129
            if not images:
......
155 143
    return HttpResponse(data, status=200)
156 144

  
157 145

  
158
@api_method('POST')
146
@api.api_method('POST', user_required=True, logger=log)
159 147
def create_image(request):
160 148
    # Normal Response Code: 202
161 149
    # Error Response Codes: computeFault (400, 500),
......
173 161
    raise faults.NotImplemented('Not supported.')
174 162

  
175 163

  
176
@api_method('GET')
164
@api.api_method('GET', user_required=True, logger=log)
177 165
def get_image_details(request, image_id):
178 166
    # Normal Response Codes: 200, 203
179 167
    # Error Response Codes: computeFault (400, 500),
......
195 183
    return HttpResponse(data, status=200)
196 184

  
197 185

  
198
@api_method('DELETE')
186
@api.api_method('DELETE', user_required=True, logger=log)
199 187
def delete_image(request, image_id):
200 188
    # Normal Response Code: 204
201 189
    # Error Response Codes: computeFault (400, 500),
......
211 199
    return HttpResponse(status=204)
212 200

  
213 201

  
214
@api_method('GET')
202
@api.api_method('GET', user_required=True, logger=log)
215 203
def list_metadata(request, image_id):
216 204
    # Normal Response Codes: 200, 203
217 205
    # Error Response Codes: computeFault (400, 500),
......
226 214
    return util.render_metadata(request, metadata, use_values=True, status=200)
227 215

  
228 216

  
229
@api_method('POST')
217
@api.api_method('POST', user_required=True, logger=log)
230 218
def update_metadata(request, image_id):
231 219
    # Normal Response Code: 201
232 220
    # Error Response Codes: computeFault (400, 500),
......
237 225
    #                       badMediaType(415),
238 226
    #                       overLimit (413)
239 227

  
240
    req = util.get_request_dict(request)
228
    req = utils.get_request_dict(request)
241 229
    log.info('update_image_metadata %s %s', image_id, req)
242 230
    image = util.get_image(image_id, request.user_uniq)
243 231
    try:
......
255 243
    return util.render_metadata(request, properties, status=201)
256 244

  
257 245

  
258
@api_method('GET')
246
@api.api_method('GET', user_required=True, logger=log)
259 247
def get_metadata_item(request, image_id, key):
260 248
    # Normal Response Codes: 200, 203
261 249
    # Error Response Codes: computeFault (400, 500),
......
273 261
    return util.render_meta(request, {key: val}, status=200)
274 262

  
275 263

  
276
@api_method('PUT')
264
@api.api_method('PUT', user_required=True, logger=log)
277 265
def create_metadata_item(request, image_id, key):
278 266
    # Normal Response Code: 201
279 267
    # Error Response Codes: computeFault (400, 500),
......
285 273
    #                       badMediaType(415),
286 274
    #                       overLimit (413)
287 275

  
288
    req = util.get_request_dict(request)
276
    req = utils.get_request_dict(request)
289 277
    log.info('create_image_metadata_item %s %s %s', image_id, key, req)
290 278
    try:
291 279
        metadict = req['meta']
......
306 294
    return util.render_meta(request, {key: val}, status=201)
307 295

  
308 296

  
309
@api_method('DELETE')
297
@api.api_method('DELETE', user_required=True, logger=log)
310 298
def delete_metadata_item(request, image_id, key):
311 299
    # Normal Response Code: 204
312 300
    # Error Response Codes: computeFault (400, 500),
b/snf-cyclades-app/synnefo/api/networks.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from logging import getLogger
35 34

  
36
from django.conf.urls.defaults import patterns
37 35
from django.conf import settings
38
from django.db.models import Q
36
from django.conf.urls.defaults import patterns
39 37
from django.db import transaction
38
from django.db.models import Q
40 39
from django.http import HttpResponse
41 40
from django.template.loader import render_to_string
42 41
from django.utils import simplejson as json
43 42

  
44
from snf_django.lib.api import faults
43
from snf_django.lib import api
44
from snf_django.lib.api import faults, utils
45 45
from synnefo.api import util
46 46
from synnefo.api.actions import network_actions
47
from synnefo.api.common import method_not_allowed
48 47
from synnefo import quotas
49 48
from synnefo.db.models import Network
50 49
from synnefo.db.pools import EmptyPool
51 50
from synnefo.logic import backend
52 51

  
53 52

  
54
log = getLogger('synnefo.api')
53
from logging import getLogger
54
log = getLogger(__name__)
55 55

  
56 56
urlpatterns = patterns(
57 57
    'synnefo.api.networks',
......
68 68
    elif request.method == 'POST':
69 69
        return create_network(request)
70 70
    else:
71
        return method_not_allowed(request)
71
        return api.method_not_allowed(request)
72 72

  
73 73

  
74 74
def network_demux(request, network_id):
......
79 79
    elif request.method == 'DELETE':
80 80
        return delete_network(request, network_id)
81 81
    else:
82
        return method_not_allowed(request)
82
        return api.method_not_allowed(request)
83 83

  
84 84

  
85 85
def network_to_dict(network, user_id, detail=True):
......
91 91
        d['gateway6'] = network.gateway6
92 92
        d['dhcp'] = network.dhcp
93 93
        d['type'] = network.flavor
94
        d['updated'] = util.isoformat(network.updated)
95
        d['created'] = util.isoformat(network.created)
94
        d['updated'] = utils.isoformat(network.updated)
95
        d['created'] = utils.isoformat(network.created)
96 96
        d['status'] = network.state
97 97
        d['public'] = network.public
98 98

  
......
112 112
    return HttpResponse(data, status=status)
113 113

  
114 114

  
115
@util.api_method('GET')
115
@api.api_method(http_method='GET', user_required=True, logger=log)
116 116
def list_networks(request, detail=False):
117 117
    # Normal Response Codes: 200, 203
118 118
    # Error Response Codes: computeFault (400, 500),
......
122 122
    #                       overLimit (413)
123 123

  
124 124
    log.debug('list_networks detail=%s', detail)
125
    since = util.isoparse(request.GET.get('changes-since'))
125
    since = utils.isoparse(request.GET.get('changes-since'))
126 126
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
127 127
                                           Q(public=True))
128 128

  
......
146 146
    return HttpResponse(data, status=200)
147 147

  
148 148

  
149
@util.api_method('POST')
149
@api.api_method(http_method='POST', user_required=True, logger=log)
150 150
@quotas.uses_commission
151 151
@transaction.commit_manually
152 152
def create_network(serials, request):
......
160 160
    #                       overLimit (413)
161 161

  
162 162
    try:
163
        req = util.get_request_dict(request)
163
        req = utils.get_request_dict(request)
164 164
        log.info('create_network %s', req)
165 165

  
166 166
        user_id = request.user_uniq
......
242 242
    return response
243 243

  
244 244

  
245
@util.api_method('GET')
245
@api.api_method(http_method='GET', user_required=True, logger=log)
246 246
def get_network_details(request, network_id):
247 247
    # Normal Response Codes: 200, 203
248 248
    # Error Response Codes: computeFault (400, 500),
......
258 258
    return render_network(request, netdict)
259 259

  
260 260

  
261
@util.api_method('PUT')
261
@api.api_method(http_method='PUT', user_required=True, logger=log)
262 262
def update_network_name(request, network_id):
263 263
    # Normal Response Code: 204
264 264
    # Error Response Codes: computeFault (400, 500),
......
270 270
    #                       itemNotFound (404),
271 271
    #                       overLimit (413)
272 272

  
273
    req = util.get_request_dict(request)
273
    req = utils.get_request_dict(request)
274 274
    log.info('update_network_name %s', network_id)
275 275

  
276 276
    try:
......
288 288
    return HttpResponse(status=204)
289 289

  
290 290

  
291
@util.api_method('DELETE')
291
@api.api_method(http_method='DELETE', user_required=True, logger=log)
292 292
@transaction.commit_on_success
293 293
def delete_network(request, network_id):
294 294
    # Normal Response Code: 204
......
317 317
    return HttpResponse(status=204)
318 318

  
319 319

  
320
@util.api_method('POST')
320
@api.api_method(http_method='POST', user_required=True, logger=log)
321 321
def network_action(request, network_id):
322
    req = util.get_request_dict(request)
322
    req = utils.get_request_dict(request)
323 323
    log.debug('network_action %s %s', network_id, req)
324 324
    if len(req) != 1:
325 325
        raise faults.BadRequest('Malformed request.')
b/snf-cyclades-app/synnefo/api/servers.py
1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
......
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from base64 import b64decode
35

  
36 34
from django import dispatch
37 35
from django.conf import settings
38 36
from django.conf.urls.defaults import patterns
......
41 39
from django.template.loader import render_to_string
42 40
from django.utils import simplejson as json
43 41

  
44
from snf_django.lib.api import faults
42
from snf_django.lib import api
43
from snf_django.lib.api import faults, utils
45 44
from synnefo.api import util
46 45
from synnefo.api.actions import server_actions
47
from synnefo.api.common import method_not_allowed
48 46
from synnefo.db.models import (VirtualMachine, VirtualMachineMetadata,
49 47
                               NetworkInterface)
50 48
from synnefo.logic.backend import create_instance, delete_instance
......
57 55
server_created = dispatch.Signal(providing_args=["created_vm_params"])
58 56

  
59 57
from logging import getLogger
60
log = getLogger('synnefo.api')
58
log = getLogger(__name__)
61 59

  
62 60
urlpatterns = patterns(
63 61
    'synnefo.api.servers',
......
80 78
    elif request.method == 'POST':
81 79
        return create_server(request)
82 80
    else:
83
        return method_not_allowed(request)
81
        return api.method_not_allowed(request)
84 82

  
85 83

  
86 84
def server_demux(request, server_id):
......
91 89
    elif request.method == 'DELETE':
92 90
        return delete_server(request, server_id)
93 91
    else:
94
        return method_not_allowed(request)
92
        return api.method_not_allowed(request)
95 93

  
96 94

  
97 95
def metadata_demux(request, server_id):
......
100 98
    elif request.method == 'POST':
101 99
        return update_metadata(request, server_id)
102 100
    else:
103
        return method_not_allowed(request)
101
        return api.method_not_allowed(request)
104 102

  
105 103

  
106 104
def metadata_item_demux(request, server_id, key):
......
111 109
    elif request.method == 'DELETE':
112 110
        return delete_metadata_item(request, server_id, key)
113 111
    else:
114
        return method_not_allowed(request)
112
        return api.method_not_allowed(request)
115 113

  
116 114

  
117 115
def nic_to_dict(nic):
......
133 131
        d['progress'] = 100 if get_rsapi_state(vm) == 'ACTIVE' \
134 132
            else vm.buildpercentage
135 133
        d['hostId'] = vm.hostid
136
        d['updated'] = util.isoformat(vm.updated)
137
        d['created'] = util.isoformat(vm.created)
134
        d['updated'] = utils.isoformat(vm.updated)
135
        d['created'] = utils.isoformat(vm.created)
138 136
        d['flavorRef'] = vm.flavor.id
139 137
        d['imageRef'] = vm.imageid
140 138
        d['suspended'] = vm.suspended
......
166 164
        # format source date if set
167 165
        formatted_source_date = None
168 166
        if diagnostic.source_date:
169
            formatted_source_date = util.isoformat(diagnostic.source_date)
167
            formatted_source_date = utils.isoformat(diagnostic.source_date)
170 168

  
171 169
        entry = {
172 170
            'source': diagnostic.source,
173
            'created': util.isoformat(diagnostic.created),
171
            'created': utils.isoformat(diagnostic.created),
174 172
            'message': diagnostic.message,
175 173
            'details': diagnostic.details,
176 174
            'level': diagnostic.level,
......
201 199
    return HttpResponse(json.dumps(diagnostics_dict), status=status)
202 200

  
203 201

  
204
@util.api_method('GET')
202
@api.api_method(http_method='GET', user_required=True, logger=log)
205 203
def get_server_diagnostics(request, server_id):
206 204
    """
207 205
    Virtual machine diagnostics api view.
......
212 210
    return render_diagnostics(request, diagnostics)
213 211

  
214 212

  
215
@util.api_method('GET')
213
@api.api_method(http_method='GET', user_required=True, logger=log)
216 214
def list_servers(request, detail=False):
217 215
    # Normal Response Codes: 200, 203
218 216
    # Error Response Codes: computeFault (400, 500),
......
224 222
    log.debug('list_servers detail=%s', detail)
225 223
    user_vms = VirtualMachine.objects.filter(userid=request.user_uniq)
226 224

  
227
    since = util.isoparse(request.GET.get('changes-since'))
225
    since = utils.isoparse(request.GET.get('changes-since'))
228 226

  
229 227
    if since:
230 228
        user_vms = user_vms.filter(updated__gte=since)
......
246 244
    return HttpResponse(data, status=200)
247 245

  
248 246

  
249
@util.api_method('POST')
247
@api.api_method(http_method='POST', user_required=True, logger=log)
250 248
# Use manual transactions. Backend and IP pool allocations need exclusive
251 249
# access (SELECT..FOR UPDATE). Running create_server with commit_on_success
252 250
# would result in backends and public networks to be locked until the job is
......
264 262
    #                       serverCapacityUnavailable (503),
265 263
    #                       overLimit (413)
266 264
    try:
267
        req = util.get_request_dict(request)
265
        req = utils.get_request_dict(request)
268 266
        log.info('create_server %s', req)
269 267
        user_id = request.user_uniq
270 268

  
......
398 396
    return respsone
399 397

  
400 398

  
401
@util.api_method('GET')
399
@api.api_method(http_method='GET', user_required=True, logger=log)
402 400
def get_server_details(request, server_id):
403 401
    # Normal Response Codes: 200, 203
404 402
    # Error Response Codes: computeFault (400, 500),
......
414 412
    return render_server(request, server)
415 413

  
416 414

  
417
@util.api_method('PUT')
415
@api.api_method(http_method='PUT', user_required=True, logger=log)
418 416
def update_server_name(request, server_id):
419 417
    # Normal Response Code: 204
420 418
    # Error Response Codes: computeFault (400, 500),
......
426 424
    #                       buildInProgress (409),
427 425
    #                       overLimit (413)
428 426

  
429
    req = util.get_request_dict(request)
427
    req = utils.get_request_dict(request)
430 428
    log.info('update_server_name %s %s', server_id, req)
431 429

  
432 430
    try:
......
442 440
    return HttpResponse(status=204)
443 441

  
444 442

  
445
@util.api_method('DELETE')
443
@api.api_method(http_method='DELETE', user_required=True, logger=log)
446 444
@transaction.commit_on_success
447 445
def delete_server(request, server_id):
448 446
    # Normal Response Codes: 204
......
466 464
ARBITRARY_ACTIONS = ['console', 'firewallProfile']
467 465

  
468 466

  
469
@util.api_method('POST')
467
@api.api_method(http_method='POST', user_required=True, logger=log)
470 468
def server_action(request, server_id):
471
    req = util.get_request_dict(request)
469
    req = utils.get_request_dict(request)
472 470
    log.debug('server_action %s %s', server_id, req)
473 471

  
474 472
    if len(req) != 1:
......
528 526
    vm.save()
529 527

  
530 528

  
531
@util.api_method('GET')
529
@api.api_method(http_method='GET', user_required=True, logger=log)
532 530
def list_addresses(request, server_id):
533 531
    # Normal Response Codes: 200, 203
534 532
    # Error Response Codes: computeFault (400, 500),
......
549 547
    return HttpResponse(data, status=200)
550 548

  
551 549

  
552
@util.api_method('GET')
550
@api.api_method(http_method='GET', user_required=True, logger=log)
553 551
def list_addresses_by_network(request, server_id, network_id):
554 552
    # Normal Response Codes: 200, 203
555 553
    # Error Response Codes: computeFault (400, 500),
......
573 571
    return HttpResponse(data, status=200)
574 572

  
575 573

  
576
@util.api_method('GET')
574
@api.api_method(http_method='GET', user_required=True, logger=log)
577 575
def list_metadata(request, server_id):
578 576
    # Normal Response Codes: 200, 203
579 577
    # Error Response Codes: computeFault (400, 500),
......
588 586
    return util.render_metadata(request, metadata, use_values=True, status=200)
589 587

  
590 588

  
591
@util.api_method('POST')
589
@api.api_method(http_method='POST', user_required=True, logger=log)
592 590
def update_metadata(request, server_id):
593 591
    # Normal Response Code: 201
594 592
    # Error Response Codes: computeFault (400, 500),
......
599 597
    #                       badMediaType(415),
600 598
    #                       overLimit (413)
601 599

  
602
    req = util.get_request_dict(request)
600
    req = utils.get_request_dict(request)
603 601
    log.info('update_server_metadata %s %s', server_id, req)
604 602
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
605 603
    try:
......
618 616
    return util.render_metadata(request, vm_meta, status=201)
619 617

  
620 618

  
621
@util.api_method('GET')
619
@api.api_method(http_method='GET', user_required=True, logger=log)
622 620
def get_metadata_item(request, server_id, key):
623 621
    # Normal Response Codes: 200, 203
624 622
    # Error Response Codes: computeFault (400, 500),
......
635 633
    return util.render_meta(request, d, status=200)
636 634

  
637 635

  
638
@util.api_method('PUT')
636
@api.api_method(http_method='PUT', user_required=True, logger=log)
639 637
@transaction.commit_on_success
640 638
def create_metadata_item(request, server_id, key):
641 639
    # Normal Response Code: 201
......
648 646
    #                       badMediaType(415),
649 647
    #                       overLimit (413)
650 648

  
651
    req = util.get_request_dict(request)
649
    req = utils.get_request_dict(request)
652 650
    log.info('create_server_metadata_item %s %s %s', server_id, key, req)
653 651
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
654 652
    try:
......
670 668
    return util.render_meta(request, d, status=201)
671 669

  
672 670

  
673
@util.api_method('DELETE')
671
@api.api_method(http_method='DELETE', user_required=True, logger=log)
674 672
@transaction.commit_on_success
675 673
def delete_metadata_item(request, server_id, key):
676 674
    # Normal Response Code: 204
......
691 689
    return HttpResponse(status=204)
692 690

  
693 691

  
694
@util.api_method('GET')
692
@api.api_method(http_method='GET', user_required=True, logger=log)
695 693
def server_stats(request, server_id):
696 694
    # Normal Response Codes: 200
697 695
    # Error Response Codes: computeFault (400, 500),
b/snf-cyclades-app/synnefo/api/test/images.py
46 46
    def wrapper(self, backend):
47 47
        result = func(self, backend)
48 48
        if backend.called is True:
49
            backend.return_value.close.assert_called_once_with()
49
            num = len(backend.mock_calls) / 2
50
            assert(len(backend.return_value.close.mock_calls), num)
50 51
        return result
51 52
    return wrapper
52 53

  
53 54

  
54
@patch('synnefo.api.images.ImageBackend')
55
@patch('synnefo.plankton.utils.ImageBackend')
55 56
class ImageAPITest(BaseAPITest):
56 57
    @assert_backend_closed
57 58
    def test_create_image(self, mimage):
......
191 192
        mimage.return_value._delete.assert_not_called('42')
192 193

  
193 194

  
194
@patch('synnefo.api.util.ImageBackend')
195
@patch('synnefo.plankton.utils.ImageBackend')
195 196
class ImageMetadataAPITest(BaseAPITest):
196 197
    def setUp(self):
197 198
        self.image = {'id': 42,
......
232 233
        response = self.get('/api/v1.1/images/42/meta/not_found', 'user')
233 234
        self.assertItemNotFound(response)
234 235

  
235
    @assert_backend_closed
236 236
    def test_delete_metadata_item(self, backend):
237 237
        backend.return_value.get_image.return_value = self.image
238
        with patch("synnefo.api.images.ImageBackend") as m:
239
            response = self.delete('/api/v1.1/images/42/meta/foo', 'user')
240
            self.assertEqual(response.status_code, 204)
241
            m.return_value.update.assert_called_once_with('42',
242
                                        {'properties': {'foo2': 'bar2'}})
238
        response = self.delete('/api/v1.1/images/42/meta/foo', 'user')
239
        self.assertEqual(response.status_code, 204)
240
        backend.return_value.update.assert_called_once_with('42', {'properties': {'foo2':
241
                                                    'bar2'}})
243 242

  
244 243
    @assert_backend_closed
245 244
    def test_create_metadata_item(self, backend):
246 245
        backend.return_value.get_image.return_value = self.image
247
        with patch("synnefo.api.images.ImageBackend") as m:
248
                request = {'meta': {'foo3': 'bar3'}}
249
                response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
250
                                    json.dumps(request), 'json')
251
                self.assertEqual(response.status_code, 201)
252
                m.return_value.update.assert_called_once_with('42',
253
                        {'properties':
254
                            {'foo': 'bar', 'foo2': 'bar2', 'foo3': 'bar3'}})
246
        request = {'meta': {'foo3': 'bar3'}}
247
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
248
                            json.dumps(request), 'json')
249
        self.assertEqual(response.status_code, 201)
250
        backend.return_value.update.assert_called_once_with('42',
251
                {'properties':
252
                    {'foo': 'bar', 'foo2': 'bar2', 'foo3': 'bar3'}})
255 253

  
256 254
    @assert_backend_closed
257 255
    def test_create_metadata_malformed_1(self, backend):
258 256
        backend.return_value.get_image.return_value = self.image
259
        with patch("synnefo.api.images.ImageBackend"):
260
                request = {'met': {'foo3': 'bar3'}}
261
                response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
262
                                    json.dumps(request), 'json')
263
                self.assertBadRequest(response)
257
        request = {'met': {'foo3': 'bar3'}}
258
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
259
                            json.dumps(request), 'json')
260
        self.assertBadRequest(response)
264 261

  
265 262
    @assert_backend_closed
266 263
    def test_create_metadata_malformed_2(self, backend):
267 264
        backend.return_value.get_image.return_value = self.image
268
        with patch("synnefo.api.images.ImageBackend"):
269
                request = {'meta': [('foo3', 'bar3')]}
270
                response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
271
                                    json.dumps(request), 'json')
272
                self.assertBadRequest(response)
265
        request = {'meta': [('foo3', 'bar3')]}
266
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
267
                            json.dumps(request), 'json')
268
        self.assertBadRequest(response)
273 269

  
274 270
    @assert_backend_closed
275 271
    def test_create_metadata_malformed_3(self, backend):
276 272
        backend.return_value.get_image.return_value = self.image
277
        with patch("synnefo.api.images.ImageBackend"):
278
            request = {'met': {'foo3': 'bar3', 'foo4': 'bar4'}}
279
            response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
280
                                    json.dumps(request), 'json')
281
            self.assertBadRequest(response)
273
        request = {'met': {'foo3': 'bar3', 'foo4': 'bar4'}}
274
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
275
                                json.dumps(request), 'json')
276
        self.assertBadRequest(response)
282 277

  
283 278
    @assert_backend_closed
284 279
    def test_create_metadata_malformed_4(self, backend):
285 280
        backend.return_value.get_image.return_value = self.image
286
        with patch("synnefo.api.images.ImageBackend"):
287
            request = {'met': {'foo3': 'bar3'}}
288
            response = self.put('/api/v1.1/images/42/meta/foo4', 'user',
289
                                    json.dumps(request), 'json')
290
            self.assertBadRequest(response)
281
        request = {'met': {'foo3': 'bar3'}}
282
        response = self.put('/api/v1.1/images/42/meta/foo4', 'user',
283
                                json.dumps(request), 'json')
284
        self.assertBadRequest(response)
291 285

  
292 286
    @assert_backend_closed
293 287
    def test_update_metadata_item(self, backend):
294 288
        backend.return_value.get_image.return_value = self.image
295
        with patch("synnefo.api.images.ImageBackend") as m:
296
                request = {'metadata': {'foo': 'bar_new', 'foo4': 'bar4'}}
297
                response = self.post('/api/v1.1/images/42/meta', 'user',
298
                                    json.dumps(request), 'json')
299
                self.assertEqual(response.status_code, 201)
300
                m.return_value.update.assert_called_once_with('42',
301
                        {'properties':
302
                            {'foo': 'bar_new', 'foo2': 'bar2', 'foo4': 'bar4'}
303
                        })
289
        request = {'metadata': {'foo': 'bar_new', 'foo4': 'bar4'}}
290
        response = self.post('/api/v1.1/images/42/meta', 'user',
291
                             json.dumps(request), 'json')
292
        self.assertEqual(response.status_code, 201)
293
        backend.return_value.update.assert_called_once_with('42',
294
                {'properties':
295
                    {'foo': 'bar_new', 'foo2': 'bar2', 'foo4': 'bar4'}
296
                })
304 297

  
305 298
    @assert_backend_closed
306 299
    def test_update_metadata_malformed(self, backend):
307 300
        backend.return_value.get_image.return_value = self.image
308
        with patch("synnefo.api.images.ImageBackend"):
309
                request = {'meta': {'foo': 'bar_new', 'foo4': 'bar4'}}
310
                response = self.post('/api/v1.1/images/42/meta', 'user',
311
                                    json.dumps(request), 'json')
312
                self.assertBadRequest(response)
301
        request = {'meta': {'foo': 'bar_new', 'foo4': 'bar4'}}
302
        response = self.post('/api/v1.1/images/42/meta', 'user',
303
                            json.dumps(request), 'json')
304
        self.assertBadRequest(response)
b/snf-cyclades-app/synnefo/api/tests.py
50 50
        .... make api calls ....
51 51

  
52 52
    """
53
    def dummy_get_user(request, *args, **kwargs):
54
        request.user = {'username': user, 'groups': []}
55
        request.user_uniq = user
56

  
57
    with patch('synnefo.api.util.get_user') as m:
58
        m.side_effect = dummy_get_user
59
        yield
53
    with patch("snf_django.lib.api.get_token") as get_token:
54
        get_token.return_value = "DummyToken"
55
        with patch('astakosclient.AstakosClient.get_user_info') as m:
56
            m.return_value = {"uuid": user}
57
            yield
60 58

  
61 59

  
62 60
class BaseAPITest(TestCase):
b/snf-cyclades-app/synnefo/api/urls.py
33 33

  
34 34
from django.conf.urls.defaults import include, patterns
35 35

  
36
from snf_django.lib.api import not_found
36 37
from synnefo.api import servers, flavors, images, networks
37
from synnefo.api.common import not_found
38 38
from synnefo.api.versions import versions_list, version_details
39 39

  
40 40

  
b/snf-cyclades-app/synnefo/api/util.py
63 63
from synnefo.db.pools import EmptyPool
64 64

  
65 65
from synnefo.lib.astakos import get_user
66
from synnefo.plankton.backend import ImageBackend, NotAllowedError
66
from synnefo.plankton.utils import image_backend
67 67
from synnefo.settings import MAX_CIDR_BLOCK
68 68

  
69 69

  
......
189 189
def get_image(image_id, user_id):
190 190
    """Return an Image instance or raise ItemNotFound."""
191 191

  
192
    backend = ImageBackend(user_id)
193
    try:
192
    with image_backend(user_id) as backend:
194 193
        image = backend.get_image(image_id)
195 194
        if not image:
196 195
            raise faults.ItemNotFound('Image not found.')
197 196
        return image
198
    finally:
199
        backend.close()
200 197

  
201 198

  
202 199
def get_image_dict(image_id, user_id):
b/snf-cyclades-app/synnefo/api/versions.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from datetime import datetime
35 34
from logging import getLogger
36 35

  
37 36
from django.conf import settings
......
39 38
from django.template.loader import render_to_string
40 39
from django.utils import simplejson as json
41 40

  
42
from synnefo.api.util import api_method, isoformat
41
from snf_django.lib import api
43 42

  
44 43

  
45 44
log = getLogger('synnefo.api')
......
60 59
VERSIONS = [VERSION_1_1]
61 60

  
62 61
MEDIA_TYPES = [
63
    {'base': 'application/xml', 'type': 'application/vnd.openstack.compute-v1.1+xml'},
64
    {'base': 'application/json', 'type': 'application/vnd.openstack.compute-v1.1+json'}
62
    {'base': 'application/xml',
63
     'type': 'application/vnd.openstack.compute-v1.1+xml'},
64
    {'base': 'application/json',
65
     'type': 'application/vnd.openstack.compute-v1.1+json'}
65 66
]
66 67

  
67 68
DESCRIBED_BY = [
68
    {
69
        'rel' : 'describedby',
70
        'type' : 'application/pdf',
71
        'href' : 'http://docs.rackspacecloud.com/servers/api/v1.1/cs-devguide-20110125.pdf'
72
    },
73
    {
74
        'rel' : 'describedby',
75
        'type' : 'application/vnd.sun.wadl+xml',
76
        'href' : 'http://docs.rackspacecloud.com/servers/api/v1.1/application.wadl'
77
    }
69
    {'rel': 'describedby',
70
     'type': 'application/pdf',
71
     'href': "http://docs.rackspacecloud.com/servers/api/"
72
             "v1.1/cs-devguide-20110125.pdf"},
73
    {'rel': 'describedby',
74
     'type': 'application/vnd.sun.wadl+xml',
75
     'href': "http://docs.rackspacecloud.com/servers/api/v1.1/"
76
             "application.wadl"}
78 77
]
79 78

  
80
@api_method('GET', atom_allowed=True)
79

  
80
@api.api_method(http_method='GET', user_required=True, logger=log)
81 81
def versions_list(request):
82 82
    # Normal Response Codes: 200, 203
83 83
    # Error Response Codes: 400, 413, 500, 503
84 84

  
85 85
    if request.serialization == 'xml':
86 86
        data = render_to_string('versions_list.xml', {'versions': VERSIONS})
87
    elif request.serialization == 'atom':
88
        now = isoformat(datetime.now())
89
        data = render_to_string('versions_list.atom', {'now': now,'versions': VERSIONS})
90 87
    else:
91 88
        data = json.dumps({'versions': {'values': VERSIONS}})
92 89

  
93 90
    return HttpResponse(data)
94 91

  
95
@api_method('GET', atom_allowed=True)
92

  
93
@api.api_method('GET', user_required=True, logger=log)
96 94
def version_details(request, api_version):
97 95
    # Normal Response Codes: 200, 203
98 96
    # Error Response Codes: computeFault (400, 500),
......
109 107
    if request.serialization == 'xml':
110 108
        version['media_types'] = MEDIA_TYPES
111 109
        data = render_to_string('version_details.xml', {'version': version})
112
    elif request.serialization == 'atom':
113
        version['media_types'] = MEDIA_TYPES
114
        now = isoformat(datetime.now())
115
        data = render_to_string('version_details.atom', {'now': now,'version': version})
116 110
    else:
117 111
        version['media-types'] = MEDIA_TYPES
118 112
        data = json.dumps({'version': version})

Also available in: Unified diff