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