Revision f533f224

b/api/actions.py
12 12
from synnefo.api.faults import BadRequest, ServiceUnavailable
13 13
from synnefo.api.util import random_password, get_vm
14 14
from synnefo.util.vapclient import request_forwarding as request_vnc_forwarding
15
from synnefo.logic.backend import (reboot_instance, startup_instance, shutdown_instance,
16
                                    get_instance_console)
15
from synnefo.logic import backend
17 16
from synnefo.logic.utils import get_rsapi_state
18 17

  
19 18

  
......
76 75
    reboot_type = args.get('type', '')
77 76
    if reboot_type not in ('SOFT', 'HARD'):
78 77
        raise BadRequest('Malformed Request.')
79
    reboot_instance(vm, reboot_type.lower())
78
    backend.reboot_instance(vm, reboot_type.lower())
80 79
    return HttpResponse(status=202)
81 80

  
82 81
@server_action('start')
......
87 86

  
88 87
    if args:
89 88
        raise BadRequest('Malformed Request.')
90
    startup_instance(vm)
89
    backend.startup_instance(vm)
91 90
    return HttpResponse(status=202)
92 91

  
93 92
@server_action('shutdown')
......
98 97

  
99 98
    if args:
100 99
        raise BadRequest('Malformed Request.')
101
    shutdown_instance(vm)
100
    backend.shutdown_instance(vm)
102 101
    return HttpResponse(status=202)
103 102

  
104 103
@server_action('rebuild')
......
196 195
    if settings.TEST:
197 196
        console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
198 197
    else:
199
        console_data = get_instance_console(vm)
198
        console_data = backend.get_instance_console(vm)
200 199

  
201 200
    if console_data['kind'] != 'vnc':
202
        raise ServiceUnavailable('Could not create a console of requested type.')
201
        message = 'Could not create a console of requested type.'
202
        raise ServiceUnavailable(message)
203 203

  
204 204
    # Let vncauthproxy decide on the source port.
205 205
    # The alternative: static allocation, e.g.
......
251 251
    if not server_id:
252 252
        raise BadRequest('Malformed Request.')
253 253
    vm = get_vm(server_id, request.user)
254
    net.machines.add(vm)
254
    backend.connect_to_network(vm, net)
255
    vm.save()
255 256
    net.save()
256 257
    return HttpResponse(status=202)
257 258

  
......
270 271
    if not server_id:
271 272
        raise BadRequest('Malformed Request.')
272 273
    vm = get_vm(server_id, request.user)
273
    net.machines.remove(vm)
274
    backend.disconnect_from_network(vm, net)
275
    vm.save()
274 276
    net.save()
275 277
    return HttpResponse(status=202)
b/api/faults.py
28 28
class BuildInProgress(Fault):
29 29
    code = 409
30 30

  
31
class OverLimit(Fault):
32
    code = 413
33

  
31 34
class ServiceUnavailable(Fault):
32 35
    code = 503
b/api/fixtures/api_test_data.json
264 264
            "charged": "2011-02-06 00:00:00",
265 265
            "sourceimage": 1,
266 266
            "hostid": "HAL-9000",
267
            "ipfour": "192.168.2.1",
268
            "ipsix": "::1",
269 267
            "flavor": 1,
270 268
            "operstate": "STOPPED"
271 269
        }
......
281 279
            "charged": "2011-02-10 00:00:00",
282 280
            "sourceimage": 1,
283 281
            "hostid": "HAL-9000",
284
            "ipfour": "192.168.2.2",
285
            "ipsix": "::2",
286 282
            "flavor": 1,
287 283
            "operstate": "BUILD"
288 284
        }
......
298 294
            "charged": "2011-02-10 00:00:00",
299 295
            "sourceimage": 1,
300 296
            "hostid": "HAL-9000",
301
            "ipfour": "192.168.2.3",
302
            "ipsix": "::3",
303 297
            "flavor": 1,
304 298
            "operstate": "STARTED"
305 299
        }
......
315 309
            "charged": "2011-02-10 00:00:00",
316 310
            "sourceimage": 1,
317 311
            "hostid": "HAL-9000",
318
            "ipfour": "192.168.2.4",
319
            "ipsix": "::4",
320 312
            "flavor": 1,
321 313
            "operstate": "STARTED"
322 314
        }
b/api/flavors.py
7 7
from django.template.loader import render_to_string
8 8
from django.utils import simplejson as json
9 9

  
10
from synnefo.api.util import get_flavor, api_method
10
from synnefo.api import util
11 11
from synnefo.db.models import Flavor
12 12

  
13 13

  
......
27 27
    return d
28 28

  
29 29

  
30
@api_method('GET')
30
@util.api_method('GET')
31 31
def list_flavors(request, detail=False):
32 32
    # Normal Response Codes: 200, 203
33 33
    # Error Response Codes: computeFault (400, 500),
......
40 40
    flavors = [flavor_to_dict(flavor, detail) for flavor in all_flavors]
41 41

  
42 42
    if request.serialization == 'xml':
43
        data = render_to_string('list_flavors.xml', {'flavors': flavors, 'detail': detail})
43
        data = render_to_string('list_flavors.xml', {
44
            'flavors': flavors,
45
            'detail': detail})
44 46
    else:
45 47
        data = json.dumps({'flavors': {'values': flavors}})
46 48

  
47 49
    return HttpResponse(data, status=200)
48 50

  
49
@api_method('GET')
51
@util.api_method('GET')
50 52
def get_flavor_details(request, flavor_id):
51 53
    # Normal Response Codes: 200, 203
52 54
    # Error Response Codes: computeFault (400, 500),
......
56 58
    #                       itemNotFound (404),
57 59
    #                       overLimit (413)
58 60

  
59
    flavor = get_flavor(flavor_id)
61
    flavor = util.get_flavor(flavor_id)
60 62
    flavordict = flavor_to_dict(flavor, detail=True)
61 63

  
62 64
    if request.serialization == 'xml':
b/api/images.py
2 2
# Copyright (c) 2010 Greek Research and Technology Network
3 3
#
4 4

  
5
from synnefo.api.common import method_not_allowed
6
from synnefo.api.faults import BadRequest
7
from synnefo.api.util import (isoformat, isoparse, get_vm, get_image, get_image_meta,
8
                                get_request_dict, render_metadata, render_meta, api_method)
9
from synnefo.db.models import Image, ImageMetadata
10

  
11 5
from django.conf.urls.defaults import patterns
12 6
from django.http import HttpResponse
13 7
from django.template.loader import render_to_string
14 8
from django.utils import simplejson as json
15 9

  
10
from synnefo.api import util
11
from synnefo.api.common import method_not_allowed
12
from synnefo.api.faults import BadRequest
13
from synnefo.db.models import Image, ImageMetadata
14

  
16 15

  
17 16
urlpatterns = patterns('synnefo.api.images',
18 17
    (r'^(?:/|.json|.xml)?$', 'demux'),
......
60 59
def image_to_dict(image, detail=True):
61 60
    d = {'id': image.id, 'name': image.name}
62 61
    if detail:
63
        d['updated'] = isoformat(image.updated)
64
        d['created'] = isoformat(image.created)
62
        d['updated'] = util.isoformat(image.updated)
63
        d['created'] = util.isoformat(image.created)
65 64
        d['status'] = image.state
66 65
        d['progress'] = 100 if image.state == 'ACTIVE' else 0
67 66
        if image.sourcevm:
......
81 80
    return dict((meta.meta_key, meta.meta_value) for meta in image_meta)
82 81

  
83 82

  
84
@api_method('GET')
83
@util.api_method('GET')
85 84
def list_images(request, detail=False):
86 85
    # Normal Response Codes: 200, 203
87 86
    # Error Response Codes: computeFault (400, 500),
......
90 89
    #                       badRequest (400),
91 90
    #                       overLimit (413)
92 91

  
93
    since = isoparse(request.GET.get('changes-since'))
92
    since = util.isoparse(request.GET.get('changes-since'))
94 93

  
95 94
    if since:
96
        avail_images = Image.objects.filter(owner=request.user, updated__gte=since)
95
        avail_images = Image.objects.filter(owner=request.user,
96
                                            updated__gte=since)
97 97
        if not avail_images:
98 98
            return HttpResponse(status=304)
99 99
    else:
......
102 102
    images = [image_to_dict(image, detail) for image in avail_images]
103 103

  
104 104
    if request.serialization == 'xml':
105
        data = render_to_string('list_images.xml', {'images': images, 'detail': detail})
105
        data = render_to_string('list_images.xml', {
106
            'images': images,
107
            'detail': detail})
106 108
    else:
107 109
        data = json.dumps({'images': {'values': images}})
108 110

  
109 111
    return HttpResponse(data, status=200)
110 112

  
111
@api_method('POST')
113
@util.api_method('POST')
112 114
def create_image(request):
113 115
    # Normal Response Code: 202
114 116
    # Error Response Codes: computeFault (400, 500),
......
123 125
    #                       backupOrResizeInProgress (409),
124 126
    #                       overLimit (413)
125 127

  
126
    req = get_request_dict(request)
128
    req = util.get_request_dict(request)
127 129

  
128 130
    try:
129 131
        d = req['image']
......
133 135
        raise BadRequest('Malformed request.')
134 136

  
135 137
    owner = request.user
136
    vm = get_vm(server_id, owner)
138
    vm = util.get_vm(server_id, owner)
137 139
    image = Image.objects.create(name=name, owner=owner, sourcevm=vm)
138 140

  
139 141
    imagedict = image_to_dict(image)
......
144 146

  
145 147
    return HttpResponse(data, status=202)
146 148

  
147
@api_method('GET')
149
@util.api_method('GET')
148 150
def get_image_details(request, image_id):
149 151
    # Normal Response Codes: 200, 203
150 152
    # Error Response Codes: computeFault (400, 500),
......
154 156
    #                       itemNotFound (404),
155 157
    #                       overLimit (413)
156 158

  
157
    image = get_image(image_id, request.user)
159
    image = util.get_image(image_id, request.user)
158 160
    imagedict = image_to_dict(image)
159 161

  
160 162
    if request.serialization == 'xml':
......
164 166

  
165 167
    return HttpResponse(data, status=200)
166 168

  
167
@api_method('DELETE')
169
@util.api_method('DELETE')
168 170
def delete_image(request, image_id):
169 171
    # Normal Response Code: 204
170 172
    # Error Response Codes: computeFault (400, 500),
......
173 175
    #                       itemNotFound (404),
174 176
    #                       overLimit (413)
175 177

  
176
    image = get_image(image_id, request.user)
178
    image = util.get_image(image_id, request.user)
177 179
    image.delete()
178 180
    return HttpResponse(status=204)
179 181

  
180
@api_method('GET')
182
@util.api_method('GET')
181 183
def list_metadata(request, image_id):
182 184
    # Normal Response Codes: 200, 203
183 185
    # Error Response Codes: computeFault (400, 500),
......
186 188
    #                       badRequest (400),
187 189
    #                       overLimit (413)
188 190

  
189
    image = get_image(image_id, request.user)
191
    image = util.get_image(image_id, request.user)
190 192
    metadata = metadata_to_dict(image)
191
    return render_metadata(request, metadata, use_values=True, status=200)
193
    return util.render_metadata(request, metadata, use_values=True, status=200)
192 194

  
193
@api_method('POST')
195
@util.api_method('POST')
194 196
def update_metadata(request, image_id):
195 197
    # Normal Response Code: 201
196 198
    # Error Response Codes: computeFault (400, 500),
......
201 203
    #                       badMediaType(415),
202 204
    #                       overLimit (413)
203 205

  
204
    image = get_image(image_id, request.user)
205
    req = get_request_dict(request)
206
    image = util.get_image(image_id, request.user)
207
    req = util.get_request_dict(request)
206 208
    try:
207 209
        metadata = req['metadata']
208 210
        assert isinstance(metadata, dict)
......
219 221
            updated[key] = val
220 222
        except ImageMetadata.DoesNotExist:
221 223
            pass    # Ignore non-existent metadata
224
    
225
    if updated:
226
        image.save()
227
    
228
    return util.render_metadata(request, updated, status=201)
222 229

  
223
    return render_metadata(request, updated, status=201)
224

  
225
@api_method('GET')
230
@util.api_method('GET')
226 231
def get_metadata_item(request, image_id, key):
227 232
    # Normal Response Codes: 200, 203
228 233
    # Error Response Codes: computeFault (400, 500),
......
232 237
    #                       badRequest (400),
233 238
    #                       overLimit (413)
234 239

  
235
    image = get_image(image_id, request.user)
236
    meta = get_image_meta(image, key)
237
    return render_meta(request, meta, status=200)
240
    image = util.get_image(image_id, request.user)
241
    meta = util.get_image_meta(image, key)
242
    return util.render_meta(request, meta, status=200)
238 243

  
239
@api_method('PUT')
244
@util.api_method('PUT')
240 245
def create_metadata_item(request, image_id, key):
241 246
    # Normal Response Code: 201
242 247
    # Error Response Codes: computeFault (400, 500),
......
248 253
    #                       badMediaType(415),
249 254
    #                       overLimit (413)
250 255

  
251
    image = get_image(image_id, request.user)
252
    req = get_request_dict(request)
256
    image = util.get_image(image_id, request.user)
257
    req = util.get_request_dict(request)
253 258
    try:
254 259
        metadict = req['meta']
255 260
        assert isinstance(metadict, dict)
......
257 262
        assert key in metadict
258 263
    except (KeyError, AssertionError):
259 264
        raise BadRequest('Malformed request.')
260

  
261
    meta, created = ImageMetadata.objects.get_or_create(meta_key=key, image=image)
265
    
266
    meta, created = ImageMetadata.objects.get_or_create(
267
        meta_key=key,
268
        image=image)
269
    
262 270
    meta.meta_value = metadict[key]
263 271
    meta.save()
264
    return render_meta(request, meta, status=201)
272
    image.save()
273
    return util.render_meta(request, meta, status=201)
265 274

  
266
@api_method('DELETE')
275
@util.api_method('DELETE')
267 276
def delete_metadata_item(request, image_id, key):
268 277
    # Normal Response Code: 204
269 278
    # Error Response Codes: computeFault (400, 500),
......
275 284
    #                       badMediaType(415),
276 285
    #                       overLimit (413),
277 286

  
278
    image = get_image(image_id, request.user)
279
    meta = get_image_meta(image, key)
287
    image = util.get_image(image_id, request.user)
288
    meta = util.get_image_meta(image, key)
280 289
    meta.delete()
290
    image.save()
281 291
    return HttpResponse(status=204)
b/api/networks.py
1
from synnefo.api.actions import network_actions
2
from synnefo.api.common import method_not_allowed
3
from synnefo.api.faults import BadRequest, Unauthorized
4
from synnefo.api.util import (isoformat, isoparse, get_network,
5
                                get_request_dict, api_method)
6
from synnefo.db.models import Network
7

  
8 1
from django.conf.urls.defaults import patterns
2
from django.db.models import Q
9 3
from django.http import HttpResponse
4
from django.template.loader import render_to_string
10 5
from django.utils import simplejson as json
11 6

  
7
from synnefo.api import util
8
from synnefo.api.actions import network_actions
9
from synnefo.api.common import method_not_allowed
10
from synnefo.api.faults import BadRequest, OverLimit, Unauthorized
11
from synnefo.db.models import Network
12
from synnefo.logic import backend
13

  
12 14

  
13 15
urlpatterns = patterns('synnefo.api.networks',
14 16
    (r'^(?:/|.json|.xml)?$', 'demux'),
......
38 40

  
39 41

  
40 42
def network_to_dict(network, detail=True):
41
    d = {'id': network.id, 'name': network.name}
43
    network_id = str(network.id) if not network.public else 'public'
44
    d = {'id': network_id, 'name': network.name}
42 45
    if detail:
46
        d['updated'] = util.isoformat(network.updated)
47
        d['created'] = util.isoformat(network.created)
43 48
        d['servers'] = {'values': [vm.id for vm in network.machines.all()]}
49
        d['status'] = network.state
44 50
    return d
45 51

  
46 52
def render_network(request, networkdict, status=200):
......
51 57
    return HttpResponse(data, status=status)
52 58

  
53 59

  
54
@api_method('GET')
60
@util.api_method('GET')
55 61
def list_networks(request, detail=False):
56 62
    # Normal Response Codes: 200, 203
57 63
    # Error Response Codes: computeFault (400, 500),
......
59 65
    #                       unauthorized (401),
60 66
    #                       badRequest (400),
61 67
    #                       overLimit (413)
62

  
63
    since = isoparse(request.GET.get('changes-since'))
64

  
68
    
69
    owner = request.user
70
    since = util.isoparse(request.GET.get('changes-since'))
71
    user_networks = Network.objects.filter(Q(owner=owner) | Q(public=True))
72
    
65 73
    if since:
66
        user_networks = Network.objects.filter(owner=request.user, updated__gte=since)
74
        user_networks = user_networks.filter(updated__gte=since)
67 75
        if not user_networks:
68 76
            return HttpResponse(status=304)
69 77
    else:
70
        user_networks = Network.objects.filter(owner=request.user)
71

  
78
        user_networks = user_networks.filter(state='ACTIVE')
79
    
72 80
    networks = [network_to_dict(network, detail) for network in user_networks]
73 81

  
74 82
    if request.serialization == 'xml':
75
        data = render_to_string('list_networks.xml', {'networks': networks, 'detail': detail})
83
        data = render_to_string('list_networks.xml', {
84
            'networks': networks,
85
            'detail': detail})
76 86
    else:
77 87
        data = json.dumps({'networks': {'values': networks}})
78 88

  
79 89
    return HttpResponse(data, status=200)
80 90

  
81
@api_method('POST')
91
@util.api_method('POST')
82 92
def create_network(request):
83 93
    # Normal Response Code: 202
84 94
    # Error Response Codes: computeFault (400, 500),
......
88 98
    #                       badRequest (400),
89 99
    #                       overLimit (413)
90 100

  
91
    req = get_request_dict(request)
101
    req = util.get_request_dict(request)
92 102

  
93 103
    try:
94 104
        d = req['network']
95 105
        name = d['name']
96 106
    except (KeyError, ValueError):
97 107
        raise BadRequest('Malformed request.')
98

  
99
    network = Network.objects.create(name=name, owner=request.user)
108
    
109
    network = backend.create_network(name, request.user)
110
    if not network:
111
        raise OverLimit('Maximum number of networks reached.')
112
    
100 113
    networkdict = network_to_dict(network)
101 114
    return render_network(request, networkdict, status=202)
102 115

  
103
@api_method('GET')
116
@util.api_method('GET')
104 117
def get_network_details(request, network_id):
105 118
    # Normal Response Codes: 200, 203
106 119
    # Error Response Codes: computeFault (400, 500),
......
110 123
    #                       itemNotFound (404),
111 124
    #                       overLimit (413)
112 125

  
113
    net = get_network(network_id, request.user)
126
    net = util.get_network(network_id, request.user)
114 127
    netdict = network_to_dict(net)
115 128
    return render_network(request, netdict)
116 129

  
117
@api_method('PUT')
130
@util.api_method('PUT')
118 131
def update_network_name(request, network_id):
119 132
    # Normal Response Code: 204
120 133
    # Error Response Codes: computeFault (400, 500),
......
125 138
    #                       itemNotFound (404),
126 139
    #                       overLimit (413)
127 140

  
128
    req = get_request_dict(request)
141
    req = util.get_request_dict(request)
129 142

  
130 143
    try:
131 144
        name = req['network']['name']
132 145
    except (TypeError, KeyError):
133 146
        raise BadRequest('Malformed request.')
134 147

  
135
    net = get_network(network_id, request.user)
148
    net = util.get_network(network_id, request.user)
149
    if net.public:
150
        raise Unauthorized('Can not rename the public network.')
136 151
    net.name = name
137 152
    net.save()
138 153
    return HttpResponse(status=204)
139 154

  
140
@api_method('DELETE')
155
@util.api_method('DELETE')
141 156
def delete_network(request, network_id):
142 157
    # Normal Response Code: 204
143 158
    # Error Response Codes: computeFault (400, 500),
......
146 161
    #                       itemNotFound (404),
147 162
    #                       unauthorized (401),
148 163
    #                       overLimit (413)
149

  
150
    net = get_network(network_id, request.user)
151
    net.delete()
164
    
165
    net = util.get_network(network_id, request.user)
166
    if net.public:
167
        raise Unauthorized('Can not delete the public network.')
168
    backend.delete_network(net)
152 169
    return HttpResponse(status=204)
153 170

  
154
@api_method('POST')
171
@util.api_method('POST')
155 172
def network_action(request, network_id):
156
    net = get_network(network_id, request.user)
157
    req = get_request_dict(request)
173
    net = util.get_network(network_id, request.user)
174
    if net.public:
175
        raise Unauthorized('Can not modify the public network.')
176
    
177
    req = util.get_request_dict(request)
158 178
    if len(req) != 1:
159 179
        raise BadRequest('Malformed request.')
160 180

  
b/api/servers.py
9 9
from django.template.loader import render_to_string
10 10
from django.utils import simplejson as json
11 11

  
12
from synnefo.api import util
12 13
from synnefo.api.actions import server_actions
13 14
from synnefo.api.common import method_not_allowed
14 15
from synnefo.api.faults import BadRequest, ItemNotFound, ServiceUnavailable
15
from synnefo.api.util import (isoformat, isoparse, random_password,
16
                                get_vm, get_vm_meta, get_image, get_flavor,
17
                                get_request_dict, render_metadata, render_meta, api_method)
18 16
from synnefo.db.models import VirtualMachine, VirtualMachineMetadata
19 17
from synnefo.logic.backend import create_instance, delete_instance
20 18
from synnefo.logic.utils import get_rsapi_state
......
70 68
        return method_not_allowed(request)
71 69

  
72 70

  
73
def address_to_dict(ipfour, ipsix):
74
    return {'id': 'public',
75
            'values': [{'version': 4, 'addr': ipfour}, {'version': 6, 'addr': ipsix}]}
71
def nic_to_dict(nic):
72
    network = nic.network
73
    network_id = str(network.id) if not network.public else 'public'
74
    d = {'id': network_id, 'name': network.name, 'mac': nic.mac}
75
    if nic.firewall_profile:
76
        d['firewallProfile'] = nic.firewall_profile
77
    if nic.ipv4 or nic.ipv6:
78
        d['values'] = []
79
        if nic.ipv4:
80
            d['values'].append({'version': 4, 'addr': nic.ipv4})
81
        if nic.ipv6:
82
            d['values'].append({'version': 6, 'addr': nic.ipv6})
83
    return d
76 84

  
77 85
def metadata_to_dict(vm):
78 86
    vm_meta = vm.virtualmachinemetadata_set.all()
......
84 92
        d['status'] = get_rsapi_state(vm)
85 93
        d['progress'] = 100 if get_rsapi_state(vm) == 'ACTIVE' else 0
86 94
        d['hostId'] = vm.hostid
87
        d['updated'] = isoformat(vm.updated)
88
        d['created'] = isoformat(vm.created)
95
        d['updated'] = util.isoformat(vm.updated)
96
        d['created'] = util.isoformat(vm.created)
89 97
        d['flavorRef'] = vm.flavor.id
90 98
        d['imageRef'] = vm.sourceimage.id
91 99

  
......
93 101
        if metadata:
94 102
            d['metadata'] = {'values': metadata}
95 103

  
96
        addresses = [address_to_dict(vm.ipfour, vm.ipsix)]
97
        addresses.extend({'id': str(network.id), 'values': []} for network in vm.network_set.all())
98
        d['addresses'] = {'values': addresses}
104
        addresses = [nic_to_dict(nic) for nic in vm.nics.all()]
105
        if addresses:
106
            d['addresses'] = {'values': addresses}
99 107
    return d
100 108

  
101 109

  
102 110
def render_server(request, server, status=200):
103 111
    if request.serialization == 'xml':
104
        data = render_to_string('server.xml', {'server': server, 'is_root': True})
112
        data = render_to_string('server.xml', {
113
            'server': server,
114
            'is_root': True})
105 115
    else:
106 116
        data = json.dumps({'server': server})
107 117
    return HttpResponse(data, status=status)
108 118

  
109 119

  
110
@api_method('GET')
120
@util.api_method('GET')
111 121
def list_servers(request, detail=False):
112 122
    # Normal Response Codes: 200, 203
113 123
    # Error Response Codes: computeFault (400, 500),
......
116 126
    #                       badRequest (400),
117 127
    #                       overLimit (413)
118 128

  
119
    since = isoparse(request.GET.get('changes-since'))
129
    since = util.isoparse(request.GET.get('changes-since'))
120 130

  
121 131
    if since:
122
        user_vms = VirtualMachine.objects.filter(owner=request.user, updated__gte=since)
132
        user_vms = VirtualMachine.objects.filter(owner=request.user,
133
                                                updated__gte=since)
123 134
        if not user_vms:
124 135
            return HttpResponse(status=304)
125 136
    else:
126
        user_vms = VirtualMachine.objects.filter(owner=request.user, deleted=False)
137
        user_vms = VirtualMachine.objects.filter(owner=request.user,
138
                                                deleted=False)
139
    
127 140
    servers = [vm_to_dict(server, detail) for server in user_vms]
128 141

  
129 142
    if request.serialization == 'xml':
130
        data = render_to_string('list_servers.xml', {'servers': servers, 'detail': detail})
143
        data = render_to_string('list_servers.xml', {
144
            'servers': servers,
145
            'detail': detail})
131 146
    else:
132 147
        data = json.dumps({'servers': {'values': servers}})
133 148

  
134 149
    return HttpResponse(data, status=200)
135 150

  
136
@api_method('POST')
151
@util.api_method('POST')
137 152
def create_server(request):
138 153
    # Normal Response Code: 202
139 154
    # Error Response Codes: computeFault (400, 500),
......
145 160
    #                       serverCapacityUnavailable (503),
146 161
    #                       overLimit (413)
147 162

  
148
    req = get_request_dict(request)
149

  
163
    req = util.get_request_dict(request)
164
    owner = request.user
165
    
150 166
    try:
151 167
        server = req['server']
152 168
        name = server['name']
......
156 172
        flavor_id = server['flavorRef']
157 173
    except (KeyError, AssertionError):
158 174
        raise BadRequest('Malformed request.')
159

  
160
    image = get_image(image_id, request.user)
161
    flavor = get_flavor(flavor_id)
162

  
175
    
176
    image = util.get_image(image_id, owner)
177
    flavor = util.get_flavor(flavor_id)
178
    password = util.random_password()
179
    
163 180
    # We must save the VM instance now, so that it gets a valid vm.backend_id.
164 181
    vm = VirtualMachine.objects.create(
165 182
        name=name,
166
        owner=request.user,
183
        owner=owner,
167 184
        sourceimage=image,
168
        ipfour='0.0.0.0',
169
        ipsix='::1',
170 185
        flavor=flavor)
171

  
172
    password = random_password()
173

  
186
    
174 187
    try:
175 188
        create_instance(vm, flavor, image, password)
176 189
    except GanetiApiError:
......
178 191
        raise ServiceUnavailable('Could not create server.')
179 192

  
180 193
    for key, val in metadata.items():
181
        VirtualMachineMetadata.objects.create(meta_key=key, meta_value=val, vm=vm)
182

  
194
        VirtualMachineMetadata.objects.create(
195
            meta_key=key,
196
            meta_value=val,
197
            vm=vm)
198
    
183 199
    logging.info('created vm with %s cpus, %s ram and %s storage',
184 200
                    flavor.cpu, flavor.ram, flavor.disk)
185 201

  
......
188 204
    server['adminPass'] = password
189 205
    return render_server(request, server, status=202)
190 206

  
191
@api_method('GET')
207
@util.api_method('GET')
192 208
def get_server_details(request, server_id):
193 209
    # Normal Response Codes: 200, 203
194 210
    # Error Response Codes: computeFault (400, 500),
......
198 214
    #                       itemNotFound (404),
199 215
    #                       overLimit (413)
200 216

  
201
    vm = get_vm(server_id, request.user)
217
    vm = util.get_vm(server_id, request.user)
202 218
    server = vm_to_dict(vm, detail=True)
203 219
    return render_server(request, server)
204 220

  
205
@api_method('PUT')
221
@util.api_method('PUT')
206 222
def update_server_name(request, server_id):
207 223
    # Normal Response Code: 204
208 224
    # Error Response Codes: computeFault (400, 500),
......
214 230
    #                       buildInProgress (409),
215 231
    #                       overLimit (413)
216 232

  
217
    req = get_request_dict(request)
233
    req = util.get_request_dict(request)
218 234

  
219 235
    try:
220 236
        name = req['server']['name']
221 237
    except (TypeError, KeyError):
222 238
        raise BadRequest('Malformed request.')
223 239

  
224
    vm = get_vm(server_id, request.user)
240
    vm = util.get_vm(server_id, request.user)
225 241
    vm.name = name
226 242
    vm.save()
227 243

  
228 244
    return HttpResponse(status=204)
229 245

  
230
@api_method('DELETE')
246
@util.api_method('DELETE')
231 247
def delete_server(request, server_id):
232 248
    # Normal Response Codes: 204
233 249
    # Error Response Codes: computeFault (400, 500),
......
238 254
    #                       buildInProgress (409),
239 255
    #                       overLimit (413)
240 256

  
241
    vm = get_vm(server_id, request.user)
257
    vm = util.get_vm(server_id, request.user)
242 258
    delete_instance(vm)
243 259
    return HttpResponse(status=204)
244 260

  
245
@api_method('POST')
261
@util.api_method('POST')
246 262
def server_action(request, server_id):
247
    vm = get_vm(server_id, request.user)
248
    req = get_request_dict(request)
263
    vm = util.get_vm(server_id, request.user)
264
    req = util.get_request_dict(request)
249 265
    if len(req) != 1:
250 266
        raise BadRequest('Malformed request.')
251 267

  
......
260 276
    except AssertionError:
261 277
        raise BadRequest('Invalid argument.')
262 278

  
263
@api_method('GET')
279
@util.api_method('GET')
264 280
def list_addresses(request, server_id):
265 281
    # Normal Response Codes: 200, 203
266 282
    # Error Response Codes: computeFault (400, 500),
......
269 285
    #                       badRequest (400),
270 286
    #                       overLimit (413)
271 287

  
272
    vm = get_vm(server_id, request.user)
273
    addresses = [address_to_dict(vm.ipfour, vm.ipsix)]
274

  
288
    vm = util.get_vm(server_id, request.user)
289
    addresses = [nic_to_dict(nic) for nic in vm.nics.all()]
290
    
275 291
    if request.serialization == 'xml':
276 292
        data = render_to_string('list_addresses.xml', {'addresses': addresses})
277 293
    else:
......
279 295

  
280 296
    return HttpResponse(data, status=200)
281 297

  
282
@api_method('GET')
298
@util.api_method('GET')
283 299
def list_addresses_by_network(request, server_id, network_id):
284 300
    # Normal Response Codes: 200, 203
285 301
    # Error Response Codes: computeFault (400, 500),
......
288 304
    #                       badRequest (400),
289 305
    #                       itemNotFound (404),
290 306
    #                       overLimit (413)
291

  
292
    vm = get_vm(server_id, request.user)
293
    if network_id != 'public':
294
        raise ItemNotFound('Unknown network.')
295

  
296
    address = address_to_dict(vm.ipfour, vm.ipsix)
297

  
307
    
308
    owner = request.user
309
    machine = util.get_vm(server_id, owner)
310
    network = util.get_network(network_id, owner)
311
    nic = util.get_nic(machine, network)
312
    address = nic_to_dict(nic)
313
    
298 314
    if request.serialization == 'xml':
299 315
        data = render_to_string('address.xml', {'address': address})
300 316
    else:
......
302 318

  
303 319
    return HttpResponse(data, status=200)
304 320

  
305
@api_method('GET')
321
@util.api_method('GET')
306 322
def list_metadata(request, server_id):
307 323
    # Normal Response Codes: 200, 203
308 324
    # Error Response Codes: computeFault (400, 500),
......
311 327
    #                       badRequest (400),
312 328
    #                       overLimit (413)
313 329

  
314
    vm = get_vm(server_id, request.user)
330
    vm = util.get_vm(server_id, request.user)
315 331
    metadata = metadata_to_dict(vm)
316
    return render_metadata(request, metadata, use_values=True, status=200)
332
    return util.render_metadata(request, metadata, use_values=True, status=200)
317 333

  
318
@api_method('POST')
334
@util.api_method('POST')
319 335
def update_metadata(request, server_id):
320 336
    # Normal Response Code: 201
321 337
    # Error Response Codes: computeFault (400, 500),
......
326 342
    #                       badMediaType(415),
327 343
    #                       overLimit (413)
328 344

  
329
    vm = get_vm(server_id, request.user)
330
    req = get_request_dict(request)
345
    vm = util.get_vm(server_id, request.user)
346
    req = util.get_request_dict(request)
331 347
    try:
332 348
        metadata = req['metadata']
333 349
        assert isinstance(metadata, dict)
......
344 360
            updated[key] = val
345 361
        except VirtualMachineMetadata.DoesNotExist:
346 362
            pass    # Ignore non-existent metadata
363
    
364
    if updated:
365
        vm.save()
366
    
367
    return util.render_metadata(request, updated, status=201)
347 368

  
348
    return render_metadata(request, updated, status=201)
349

  
350
@api_method('GET')
369
@util.api_method('GET')
351 370
def get_metadata_item(request, server_id, key):
352 371
    # Normal Response Codes: 200, 203
353 372
    # Error Response Codes: computeFault (400, 500),
......
357 376
    #                       badRequest (400),
358 377
    #                       overLimit (413)
359 378

  
360
    vm = get_vm(server_id, request.user)
361
    meta = get_vm_meta(vm, key)
362
    return render_meta(request, meta, status=200)
379
    vm = util.get_vm(server_id, request.user)
380
    meta = util.get_vm_meta(vm, key)
381
    return util.render_meta(request, meta, status=200)
363 382

  
364
@api_method('PUT')
383
@util.api_method('PUT')
365 384
def create_metadata_item(request, server_id, key):
366 385
    # Normal Response Code: 201
367 386
    # Error Response Codes: computeFault (400, 500),
......
373 392
    #                       badMediaType(415),
374 393
    #                       overLimit (413)
375 394

  
376
    vm = get_vm(server_id, request.user)
377
    req = get_request_dict(request)
395
    vm = util.get_vm(server_id, request.user)
396
    req = util.get_request_dict(request)
378 397
    try:
379 398
        metadict = req['meta']
380 399
        assert isinstance(metadict, dict)
......
382 401
        assert key in metadict
383 402
    except (KeyError, AssertionError):
384 403
        raise BadRequest('Malformed request.')
385

  
386
    meta, created = VirtualMachineMetadata.objects.get_or_create(meta_key=key, vm=vm)
404
    
405
    meta, created = VirtualMachineMetadata.objects.get_or_create(
406
        meta_key=key,
407
        vm=vm)
408
    
387 409
    meta.meta_value = metadict[key]
388 410
    meta.save()
389
    return render_meta(request, meta, status=201)
411
    vm.save()
412
    return util.render_meta(request, meta, status=201)
390 413

  
391
@api_method('DELETE')
414
@util.api_method('DELETE')
392 415
def delete_metadata_item(request, server_id, key):
393 416
    # Normal Response Code: 204
394 417
    # Error Response Codes: computeFault (400, 500),
......
400 423
    #                       badMediaType(415),
401 424
    #                       overLimit (413),
402 425

  
403
    vm = get_vm(server_id, request.user)
404
    meta = get_vm_meta(vm, key)
426
    vm = util.get_vm(server_id, request.user)
427
    meta = util.get_vm_meta(vm, key)
405 428
    meta.delete()
429
    vm.save()
406 430
    return HttpResponse(status=204)
b/api/templates/image.xml
1 1
{% spaceless %}
2 2
<?xml version="1.0" encoding="UTF-8"?>
3 3
<image xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom" id="{{ image.id }}" name="{{ image.name }}" serverRef="{{ image.serverRef }}" updated="{{ image.updated }}" created="{{ image.created }}" status="{{ image.status }}" progress="{{ image.progress }}">
4
</image>
4

  
5 5
{% if image.metadata %}
6 6
<metadata>
7 7
  {% for key, val in image.metadata.values.items %}
......
9 9
  {% endfor %}
10 10
</metadata>
11 11
{% endif %}
12

  
13
</image>
12 14
{% endspaceless %}
b/api/templates/list_networks.xml
1
{% spaceless %}
2
<?xml version="1.0" encoding="UTF-8"?>
3
<networks xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom">
4
  {% for network in networks %}
5
  <network id="{{ network.id }}" name="{{ network.name }}"{% if detail %} updated="{{ network.updated }}" created="{{ network.created }}"{% endif %}>
6

  
7
  {% if network.servers %}
8
  <servers>
9
    {% for server_id in network.servers.values %}
10
    <server id="{{ server_id }}"></server>
11
    {% endfor %}
12
  </servers>
13
  {% endif %}
14

  
15
  </network>
16
  {% endfor %}
17
</networks>
18
{% endspaceless %}
b/api/templates/network.xml
1
{% spaceless %}
2
<?xml version="1.0" encoding="UTF-8"?>
3
<network xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom" id="{{ network.id }}" name="{{ network.name }}" updated="{{ network.updated }}" created="{{ network.created }}">
4

  
5
<servers>
6
  {% for server_id in network.servers.values %}
7
  <server id="{{ server_id }}"></server>
8
  {% endfor %}
9
</servers>
10

  
11
</network>
12
{% endspaceless %}
b/api/tests.py
349 349
            owner=choice(users),
350 350
            sourceimage=choice(images),
351 351
            hostid=str(i),
352
            ipfour='0.0.0.0',
353
            ipsix='::1',
354 352
            flavor=choice(flavors))
355 353

  
356 354
def create_server_metadata(n=1):
......
361 359
            meta_value='Value %d' % (i + 1),
362 360
            vm = choice(servers))
363 361

  
364
def create_networks(n):
365
    users = SynnefoUser.objects.all()
366
    for i in range(n):
367
        Network.objects.create(
368
            name='Network%d' % (i + 1),
369
            owner=choice(users))
370

  
371 362

  
372 363
class AssertInvariant(object):
373 364
    def __init__(self, callable, *args, **kwargs):
......
390 381
    SERVERS = 1
391 382
    SERVER_METADATA = 0
392 383
    IMAGE_METADATA = 0
393
    NETWORKS = 0
394 384

  
395 385
    def setUp(self):
396 386
        self.client = AaiClient()
......
400 390
        create_image_metadata(self.IMAGE_METADATA)
401 391
        create_servers(self.SERVERS)
402 392
        create_server_metadata(self.SERVER_METADATA)
403
        create_networks(self.NETWORKS)
404 393

  
405 394
    def assertFault(self, response, status_code, name):
406 395
        self.assertEqual(response.status_code, status_code)
......
501 490
        return reply
502 491

  
503 492
    def get_network_details(self, network_id):
504
        path = '/api/v1.1/networks/%d' % network_id
493
        path = '/api/v1.1/networks/%s' % network_id
505 494
        response = self.client.get(path)
506 495
        self.assertEqual(response.status_code, 200)
507 496
        reply = json.loads(response.content)
......
509 498
        return reply['network']
510 499

  
511 500
    def update_network_name(self, network_id, new_name):
512
        path = '/api/v1.1/networks/%d' % network_id
501
        path = '/api/v1.1/networks/%s' % network_id
513 502
        data = json.dumps({'network': {'name': new_name}})
514 503
        response = self.client.put(path, data, content_type='application/json')
515 504
        self.assertEqual(response.status_code, 204)
516 505

  
517 506
    def delete_network(self, network_id):
518
        path = '/api/v1.1/networks/%d' % network_id
507
        path = '/api/v1.1/networks/%s' % network_id
519 508
        response = self.client.delete(path)
520 509
        self.assertEqual(response.status_code, 204)
521 510

  
522 511
    def add_to_network(self, network_id, server_id):
523
        path = '/api/v1.1/networks/%d/action' % network_id
512
        path = '/api/v1.1/networks/%s/action' % network_id
524 513
        data = json.dumps({'add': {'serverRef': server_id}})
525 514
        response = self.client.post(path, data, content_type='application/json')
526 515
        self.assertEqual(response.status_code, 202)
527 516

  
528 517
    def remove_from_network(self, network_id, server_id):
529
        path = '/api/v1.1/networks/%d/action' % network_id
518
        path = '/api/v1.1/networks/%s/action' % network_id
530 519
        data = json.dumps({'remove': {'serverRef': server_id}})
531 520
        response = self.client.post(path, data, content_type='application/json')
532 521
        self.assertEqual(response.status_code, 202)
......
843 832

  
844 833
class ListNetworks(BaseTestCase):
845 834
    SERVERS = 5
846
    NETWORKS = 5
847 835

  
848 836
    def setUp(self):
849 837
        BaseTestCase.setUp(self)
838
        
839
        for i in range(5):
840
            self.create_network('net%d' % i)
841
        
850 842
        machines = VirtualMachine.objects.all()
851 843
        for network in Network.objects.all():
852 844
            n = randint(0, self.SERVERS)
853
            network.machines.add(*sample(machines, n))
854
            network.save()
855

  
845
            for machine in sample(machines, n):
846
                machine.nics.create(network=network)
847
    
856 848
    def test_list_networks(self):
857 849
        networks = self.list_networks()
858 850
        for net in Network.objects.all():
859
            network = popdict(networks, id=net.id)
851
            net_id = str(net.id) if not net.public else 'public'
852
            network = popdict(networks, id=net_id)
860 853
            self.assertEqual(network['name'], net.name)
861 854
        self.assertEqual(networks, [])
862 855

  
863 856
    def test_list_networks_detail(self):
864 857
        networks = self.list_networks(detail=True)
865 858
        for net in Network.objects.all():
866
            network = popdict(networks, id=net.id)
859
            net_id = str(net.id) if not net.public else 'public'
860
            network = popdict(networks, id=net_id)
867 861
            self.assertEqual(network['name'], net.name)
868 862
            machines = set(vm.id for vm in net.machines.all())
869 863
            self.assertEqual(set(network['servers']['values']), machines)
......
872 866

  
873 867
class CreateNetwork(BaseTestCase):
874 868
    def test_create_network(self):
875
        self.assertEqual(self.list_networks(), [])
869
        before = self.list_networks()
876 870
        self.create_network('net')
877
        networks = self.list_networks()
878
        self.assertEqual(len(networks), 1)
879
        network = networks[0]
880
        self.assertEqual(network['name'], 'net')
871
        after = self.list_networks()
872
        self.assertEqual(len(after) - len(before), 1)
873
        found = False
874
        for network in after:
875
            if network['name'] == 'net':
876
                found = True
877
                break
878
        self.assertTrue(found)
881 879

  
882 880

  
883 881
class GetNetworkDetails(BaseTestCase):
884 882
    SERVERS = 5
885
    NETWORKS = 1
886

  
883
    
887 884
    def test_get_network_details(self):
885
        name = 'net'
886
        self.create_network(name)
887
        
888 888
        servers = VirtualMachine.objects.all()
889
        network = Network.objects.all()[0]
889
        network = Network.objects.all()[1]
890 890

  
891 891
        net = self.get_network_details(network.id)
892
        self.assertEqual(net['name'], network.name)
892
        self.assertEqual(net['name'], name)
893 893
        self.assertEqual(net['servers']['values'], [])
894 894

  
895 895
        server_id = choice(servers).id
896 896
        self.add_to_network(network.id, server_id)
897 897
        net = self.get_network_details(network.id)
898 898
        self.assertEqual(net['name'], network.name)
899
        self.assertEqual(net['servers']['values'], [server_id])
900 899

  
901 900

  
902 901
class UpdateNetworkName(BaseTestCase):
903
    NETWORKS = 5
904

  
905 902
    def test_update_network_name(self):
903
        name = 'net'
904
        self.create_network(name)
906 905
        networks = self.list_networks(detail=True)
907
        network = choice(networks)
906
        priv = [net for net in networks if net['id'] != 'public']
907
        network = choice(priv)
908 908
        network_id = network['id']
909 909
        new_name = network['name'] + '_2'
910 910
        self.update_network_name(network_id, new_name)
911 911

  
912 912
        network['name'] = new_name
913
        self.assertEqual(self.get_network_details(network_id), network)
913
        del network['updated']
914
        net = self.get_network_details(network_id)
915
        del net['updated']
916
        self.assertEqual(net, network)
914 917

  
915 918

  
916 919
class DeleteNetwork(BaseTestCase):
917
    NETWORKS = 5
918

  
919 920
    def test_delete_network(self):
921
        for i in range(5):
922
            self.create_network('net%d' % i)
923
        
920 924
        networks = self.list_networks()
921
        network = choice(networks)
925
        priv = [net for net in networks if net['id'] != 'public']
926
        network = choice(priv)
922 927
        network_id = network['id']
923 928
        self.delete_network(network_id)
929
        
930
        net = self.get_network_details(network_id)
931
        self.assertEqual(net['status'], 'DELETED')
924 932

  
925
        response = self.client.get('/api/v1.1/networks/%d' % network_id)
926
        self.assertItemNotFound(response)
927

  
928
        networks.remove(network)
929
        self.assertEqual(self.list_networks(), networks)
933
        priv.remove(network)
934
        networks = self.list_networks()
935
        new_priv = [net for net in networks if net['id'] != 'public']
936
        self.assertEqual(priv, new_priv)
930 937

  
931 938

  
932 939
class NetworkActions(BaseTestCase):
933 940
    SERVERS = 20
934
    NETWORKS = 1
935 941

  
936 942
    def test_add_remove_server(self):
943
        self.create_network('net')
944
        
937 945
        server_ids = [vm.id for vm in VirtualMachine.objects.all()]
938
        network = self.list_networks(detail=True)[0]
946
        network = self.list_networks(detail=True)[1]
939 947
        network_id = network['id']
940 948

  
941 949
        to_add = set(sample(server_ids, 10))
942 950
        for server_id in to_add:
943 951
            self.add_to_network(network_id, server_id)
944
            net = self.get_network_details(network_id)
945
            self.assertTrue(server_id in net['servers']['values'])
946

  
947
        net = self.get_network_details(network_id)
948
        self.assertEqual(set(net['servers']['values']), to_add)
949

  
952
        
950 953
        to_remove = set(sample(to_add, 5))
951 954
        for server_id in to_remove:
952 955
            self.remove_from_network(network_id, server_id)
953
            net = self.get_network_details(network_id)
954
            self.assertTrue(server_id not in net['servers']['values'])
955

  
956
        net = self.get_network_details(network_id)
957
        self.assertEqual(set(net['servers']['values']), to_add - to_remove)
958

  
b/api/util.py
19 19
from django.template.loader import render_to_string
20 20
from django.utils import simplejson as json
21 21

  
22
from synnefo.api.faults import (Fault, BadRequest, BuildInProgress, ItemNotFound,
23
                                ServiceUnavailable, Unauthorized)
22
from synnefo.api.faults import (Fault, BadRequest, BuildInProgress,
23
                                ItemNotFound, ServiceUnavailable, Unauthorized)
24 24
from synnefo.db.models import (SynnefoUser, Flavor, Image, ImageMetadata,
25
                                VirtualMachine, VirtualMachineMetadata, Network)
25
                                VirtualMachine, VirtualMachineMetadata,
26
                                Network, NetworkInterface)
26 27

  
27 28

  
28 29
class UTC(tzinfo):
......
120 121
    """Return a Network instance or raise ItemNotFound."""
121 122

  
122 123
    try:
123
        return Network.objects.get(id=network_id, owner=owner)
124
        if network_id == 'public':
125
            return Network.objects.get(public=True)
126
        else:
127
            network_id = int(network_id)
128
            return Network.objects.get(id=network_id, owner=owner)
124 129
    except ValueError:
125 130
        raise BadRequest('Invalid network ID.')
126 131
    except Network.DoesNotExist:
127 132
        raise ItemNotFound('Network not found.')
128 133

  
134
def get_nic(machine, network):
135
    try:
136
        return NetworkInterface.objects.get(machine=machine, network=network)
137
    except NetworkInterface.DoesNotExist:
138
        raise ItemNotFound('Server not connected to this network.')
139

  
129 140

  
130 141
def get_request_dict(request):
131 142
    """Returns data sent by the client as a python dict."""
......
154 165
    if request.serialization == 'xml':
155 166
        data = render_to_string('metadata.xml', {'metadata': metadata})
156 167
    else:
157
        d = {'metadata': {'values': metadata}} if use_values else {'metadata': metadata}
168
        if use_values:
169
            d = {'metadata': {'values': metadata}}
170
        else:
171
            d = {'metadata': metadata}
158 172
        data = json.dumps(d)
159 173
    return HttpResponse(data, status=status)
160 174

  
......
172 186
    if request.serialization == 'xml':
173 187
        data = render_to_string('fault.xml', {'fault': fault})
174 188
    else:
175
        d = {fault.name: {'code': fault.code, 'message': fault.message, 'details': fault.details}}
189
        d = {fault.name: {
190
                'code': fault.code,
191
                'message': fault.message,
192
                'details': fault.details}}
176 193
        data = json.dumps(d)
177 194

  
178 195
    resp = HttpResponse(data, status=fault.code)
......
213 230
        @wraps(func)
214 231
        def wrapper(request, *args, **kwargs):
215 232
            try:
216
                request.serialization = request_serialization(request, atom_allowed)
233
                request.serialization = request_serialization(
234
                    request,
235
                    atom_allowed)
217 236
                if not request.user:
218 237
                    raise Unauthorized('No user found.')
219 238
                if http_method and request.method != http_method:
b/contrib/ganeti-hooks/kvm-vif-bridge
1
#!/bin/bash
2

  
3
# This is an example of a Ganeti kvm ifup script that configures network
4
# interfaces based on the initial deployment of the Okeanos project
5

  
6
TAP_CONSTANT_MAC=cc:47:52:4e:45:54 # GRNET in hex :-)
7
MAC2EUI64=/etc/ganeti/mac2eui64.py
8

  
9
function routed_setup_ipv4 {
10
	# get the link's default gateway
11
	gw=$(ip route list table $LINK | sed -n 's/default via \([^ ]\+\).*/\1/p' | head -1)
12

  
13
	# mangle ARPs to come from the gw's IP
14
	arptables -D OUTPUT -o $INTERFACE --opcode request -j mangle >/dev/null 2>&1
15
	arptables -A OUTPUT -o $INTERFACE --opcode request -j mangle --mangle-ip-s "$gw"
16

  
17
	# route interface to the proper routing table
18
	while ip rule del dev $INTERFACE; do :; done
19
	ip rule add dev $INTERFACE table $LINK
20

  
21
	# static route mapping IP -> INTERFACE
22
	ip route replace $IP table $LINK proto static dev $INTERFACE
23

  
24
	# Enable proxy ARP
25
	echo 1 > /proc/sys/net/ipv4/conf/$INTERFACE/proxy_arp
26
}
27

  
28
function routed_setup_ipv6 {
29
	# Add a routing entry for the eui-64
30
	prefix=$(ip -6 route list table $LINK | awk '/\/64/ {print $1; exit}')
31
	uplink=$(ip -6 route list table $LINK | sed -n 's/default via .* dev \([^ ]\+\).*/\1/p' | head -1)
32
	eui64=$($MAC2EUI64 $MAC $prefix)
33

  
34
	while ip -6 rule del dev $INTERFACE; do :; done
35
	ip -6 rule add dev $INTERFACE table $LINK
36
	ip -6 ro replace $eui64/128 dev $INTERFACE table $LINK
37
	ip -6 neigh add proxy $eui64 dev $uplink
38

  
39
	# disable proxy NDP since we're handling this on userspace
40
	# this should be the default, but better safe than sorry
41
	echo 0 > /proc/sys/net/ipv6/conf/$INTERFACE/proxy_ndp
42
}
43

  
44
# pick a firewall profile per NIC, based on tags (and apply it)
45
function routed_setup_firewall {
46
	ifprefix="synnefo:network:$INTERFACE_INDEX:"
47
	for tag in $TAGS; do
48
		case ${tag#$ifprefix} in
49
		protected:1)
50
			chain=PROTECTED-1
51
		;;
52
		protected:2)
53
			chain=PROTECTED-2
54
		;;
55
		esac
56
	done
57

  
58
	iptables  -D FORWARD -o $INTERFACE -j $chain 2>/dev/null
59
	ip6tables -D FORWARD -o $INTERFACE -j $chain 2>/dev/null
60
	if [ "x$chain" != "x" ]; then
61
		iptables  -A FORWARD -o $INTERFACE -j $chain
62
		ip6tables -A FORWARD -o $INTERFACE -j $chain
63
	fi
64
}
65

  
66
function routed_setup_nfdhcpd {
67
	umask 022
68
	cat >/var/run/ganeti-dhcpd/$INTERFACE <<EOF
69
IP=$IP
70
MAC=$MAC
71
LINK=$LINK
72
HOSTNAME=$INSTANCE
73
TAGS="$TAGS"
74
EOF
75
}
76

  
77
if [ "$MODE" = "routed" ]; then
78
	# special proxy-ARP/NDP routing mode
79

  
80
	# use a constant predefined MAC address for the tap
81
	ip link set $INTERFACE addr $TAP_CONSTANT_MAC
82
	# bring the tap up
83
	ifconfig $INTERFACE 0.0.0.0 up
84

  
85
	# Drop unicast BOOTP/DHCP packets
86
	iptables -D FORWARD -i $INTERFACE -p udp --dport 67 -j DROP 2>/dev/null
87
	iptables -A FORWARD -i $INTERFACE -p udp --dport 67 -j DROP
88

  
89
	routed_setup_ipv4
90
	routed_setup_ipv6
91
	routed_setup_firewall
92
	routed_setup_nfdhcpd
93
elif [ "$MODE" = "bridged" ]; then
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff