Statistics
| Branch: | Tag: | Revision:

root / api / servers.py @ b9a77976

History | View | Annotate | Download (10.1 kB)

1
#
2
# Copyright (c) 2010 Greek Research and Technology Network
3
#
4

    
5
from synnefo.api.actions import server_actions
6
from synnefo.api.errors import *
7
from synnefo.api.util import *
8
from synnefo.db.models import *
9
from synnefo.util.rapi import GanetiRapiClient
10

    
11
from django.conf import settings
12
from django.conf.urls.defaults import *
13
from django.http import HttpResponse
14
from django.template.loader import render_to_string
15

    
16
from logging import getLogger
17

    
18
import json
19

    
20

    
21
log = getLogger('synnefo.api.servers')
22
rapi = GanetiRapiClient(*settings.GANETI_CLUSTER_INFO)
23

    
24
urlpatterns = patterns('synnefo.api.servers',
25
    (r'^(?:/|.json|.xml)?$', 'demux'),
26
    (r'^/detail(?:.json|.xml)?$', 'list_servers', {'detail': True}),
27
    (r'^/(\d+)(?:.json|.xml)?$', 'server_demux'),
28
    (r'^/(\d+)/action(?:.json|.xml)?$', 'server_action'),
29
    (r'^/(\d+)/ips(?:.json|.xml)?$', 'list_addresses'),
30
    (r'^/(\d+)/ips/(.+?)(?:.json|.xml)?$', 'list_addresses_by_network'),
31
)
32

    
33

    
34
def demux(request):
35
    if request.method == 'GET':
36
        return list_servers(request)
37
    elif request.method == 'POST':
38
        return create_server(request)
39
    else:
40
        fault = BadRequest()
41
        return render_fault(request, fault)
42

    
43
def server_demux(request, server_id):
44
    if request.method == 'GET':
45
        return get_server_details(request, server_id)
46
    elif request.method == 'PUT':
47
        return update_server_name(request, server_id)
48
    elif request.method == 'DELETE':
49
        return delete_server(request, server_id)
50
    else:
51
        fault = BadRequest()
52
        return render_fault(request, fault)
53

    
54

    
55
def address_to_dict(ipfour, ipsix):
56
    return {'id': 'public',
57
            'values': [{'version': 4, 'addr': ipfour}, {'version': 6, 'addr': ipsix}]}
58

    
59
def server_to_dict(server, detail=False):
60
    d = dict(id=server.id, name=server.name)
61
    if detail:
62
        d['status'] = server.rsapi_state
63
        d['progress'] = 100 if server.rsapi_state == 'ACTIVE' else 0
64
        d['hostId'] = server.hostid
65
        d['updated'] = server.updated.isoformat()
66
        d['created'] = server.created.isoformat()
67
        d['flavorId'] = server.flavor.id            # XXX Should use flavorRef instead?
68
        d['imageId'] = server.sourceimage.id        # XXX Should use imageRef instead?
69
        d['description'] = server.description       # XXX Not in OpenStack docs
70
        
71
        server_meta = server.virtualmachinemetadata_set.all()
72
        metadata = dict((meta.meta_key, meta.meta_value) for meta in server_meta)
73
        if metadata:
74
            d['metadata'] = {'values': metadata}
75
        
76
        addresses = [address_to_dict(server.ipfour, server.ipsix)]
77
        d['addresses'] = {'values': addresses}
78
    return d
79

    
80
def render_server(request, serverdict, status=200):
81
    if request.type == 'xml':
82
        data = render_to_string('server.xml', dict(server=serverdict, is_root=True))
83
    else:
84
        data = json.dumps({'server': serverdict})
85
    return HttpResponse(data, status=status)
86

    
87

    
88
@api_method('GET')
89
def list_servers(request, detail=False):
90
    # Normal Response Codes: 200, 203
91
    # Error Response Codes: computeFault (400, 500),
92
    #                       serviceUnavailable (503),
93
    #                       unauthorized (401),
94
    #                       badRequest (400),
95
    #                       overLimit (413)
96
    
97
    owner = get_user()
98
    user_servers = VirtualMachine.objects.filter(owner=owner, deleted=False)
99
    servers = [server_to_dict(server, detail) for server in user_servers]
100
    
101
    if request.type == 'xml':
102
        data = render_to_string('list_servers.xml', dict(servers=servers, detail=detail))
103
    else:
104
        data = json.dumps({'servers': {'values': servers}})
105
    
106
    return HttpResponse(data, status=200)
107

    
108
@api_method('POST')
109
def create_server(request):
110
    # Normal Response Code: 202
111
    # Error Response Codes: computeFault (400, 500),
112
    #                       serviceUnavailable (503),
113
    #                       unauthorized (401),
114
    #                       badMediaType(415),
115
    #                       itemNotFound (404),
116
    #                       badRequest (400),
117
    #                       serverCapacityUnavailable (503),
118
    #                       overLimit (413)
119
    
120
    req = get_request_dict(request)
121
    
122
    try:
123
        server = req['server']
124
        name = server['name']
125
        sourceimage = Image.objects.get(id=server['imageId'])
126
        flavor = Flavor.objects.get(id=server['flavorId'])
127
    except KeyError:
128
        raise BadRequest
129
    except Image.DoesNotExist:
130
        raise ItemNotFound
131
    except Flavor.DoesNotExist:
132
        raise ItemNotFound
133
    
134
    server = VirtualMachine.objects.create(
135
        name=name,
136
        owner=get_user(),
137
        sourceimage=sourceimage,
138
        ipfour='0.0.0.0',
139
        ipsix='::1',
140
        flavor=flavor)
141
                
142
    if request.META.get('SERVER_NAME', None) == 'testserver':
143
        name = 'test-server'
144
        dry_run = True
145
    else:
146
        name = server.backend_id
147
        dry_run = False
148
    
149
    jobId = rapi.CreateInstance(
150
        mode='create',
151
        name=name,
152
        disk_template='plain',
153
        disks=[{"size": 2000}],         #FIXME: Always ask for a 2GB disk for now
154
        nics=[{}],
155
        os='debootstrap+default',       #TODO: select OS from imageId
156
        ip_check=False,
157
        nam_check=False,
158
        pnode=rapi.GetNodes()[0],       #TODO: verify if this is necessary
159
        dry_run=dry_run,
160
        beparams=dict(auto_balance=True, vcpus=flavor.cpu, memory=flavor.ram))
161
    
162
    server.save()
163
        
164
    log.info('created vm with %s cpus, %s ram and %s storage' % (flavor.cpu, flavor.ram, flavor.disk))
165
    
166
    serverdict = server_to_dict(server, detail=True)
167
    serverdict['status'] = 'BUILD'
168
    serverdict['adminPass'] = random_password()
169
    return render_server(request, serverdict, status=202)
170

    
171
@api_method('GET')
172
def get_server_details(request, server_id):
173
    # Normal Response Codes: 200, 203
174
    # Error Response Codes: computeFault (400, 500),
175
    #                       serviceUnavailable (503),
176
    #                       unauthorized (401),
177
    #                       badRequest (400),
178
    #                       itemNotFound (404),
179
    #                       overLimit (413)
180
    
181
    try:
182
        server_id = int(server_id)
183
        server = VirtualMachine.objects.get(id=server_id)
184
    except VirtualMachine.DoesNotExist:
185
        raise ItemNotFound
186
    
187
    serverdict = server_to_dict(server, detail=True)
188
    return render_server(request, serverdict)
189

    
190
@api_method('PUT')
191
def update_server_name(request, server_id):
192
    # Normal Response Code: 204
193
    # Error Response Codes: computeFault (400, 500),
194
    #                       serviceUnavailable (503),
195
    #                       unauthorized (401),
196
    #                       badRequest (400),
197
    #                       badMediaType(415),
198
    #                       itemNotFound (404),
199
    #                       buildInProgress (409),
200
    #                       overLimit (413)
201
    
202
    req = get_request_dict(request)
203
    
204
    try:
205
        name = req['server']['name']
206
        server_id = int(server_id)
207
        server = VirtualMachine.objects.get(id=server_id)
208
    except KeyError:
209
        raise BadRequest
210
    except VirtualMachine.DoesNotExist:
211
        raise ItemNotFound
212
    
213
    server.name = name
214
    server.save()
215
    
216
    return HttpResponse(status=204)
217

    
218
@api_method('DELETE')
219
def delete_server(request, server_id):
220
    # Normal Response Codes: 204
221
    # Error Response Codes: computeFault (400, 500),
222
    #                       serviceUnavailable (503),
223
    #                       unauthorized (401),
224
    #                       itemNotFound (404),
225
    #                       unauthorized (401),
226
    #                       buildInProgress (409),
227
    #                       overLimit (413)
228
    
229
    try:
230
        server_id = int(server_id)
231
        server = VirtualMachine.objects.get(id=server_id)
232
    except VirtualMachine.DoesNotExist:
233
        raise ItemNotFound
234
    
235
    server.start_action('DESTROY')
236
    rapi.DeleteInstance(server.backend_id)
237
    return HttpResponse(status=204)
238

    
239
@api_method('POST')
240
def server_action(request, server_id):
241
    try:
242
        server_id = int(server_id)
243
        server = VirtualMachine.objects.get(id=server_id)
244
    except VirtualMachine.DoesNotExist:
245
        raise ItemNotFound
246

    
247
    req = get_request_dict(request)
248
    if len(req) != 1:
249
        raise BadRequest
250
    
251
    key = req.keys()[0]
252
    if key not in server_actions:
253
        raise BadRequest
254
    
255
    return server_actions[key](server, req[key])
256

    
257
@api_method('GET')
258
def list_addresses(request, server_id):
259
    # Normal Response Codes: 200, 203
260
    # Error Response Codes: computeFault (400, 500),
261
    #                       serviceUnavailable (503),
262
    #                       unauthorized (401),
263
    #                       badRequest (400),
264
    #                       overLimit (413)
265
    
266
    try:
267
        server_id = int(server_id)
268
        server = VirtualMachine.objects.get(id=server_id)
269
    except VirtualMachine.DoesNotExist:
270
        raise ItemNotFound
271
    
272
    addresses = [address_to_dict(server.ipfour, server.ipsix)]
273
    
274
    if request.type == 'xml':
275
        data = render_to_string('list_addresses.xml', {'addresses': addresses})
276
    else:
277
        data = json.dumps({'addresses': {'values': addresses}})
278
    
279
    return HttpResponse(data, status=200)
280

    
281
@api_method('GET')
282
def list_addresses_by_network(request, server_id, network_id):
283
    # Normal Response Codes: 200, 203
284
    # Error Response Codes: computeFault (400, 500),
285
    #                       serviceUnavailable (503),
286
    #                       unauthorized (401),
287
    #                       badRequest (400),
288
    #                       itemNotFound (404),
289
    #                       overLimit (413)
290
    
291
    try:
292
        server_id = int(server_id)
293
        server = VirtualMachine.objects.get(id=server_id)
294
    except VirtualMachine.DoesNotExist:
295
        raise ItemNotFound
296
    
297
    if network_id != 'public':
298
        raise ItemNotFound
299
    
300
    address = address_to_dict(server.ipfour, server.ipsix)
301
    
302
    if request.type == 'xml':
303
        data = render_to_string('address.xml', {'address': address})
304
    else:
305
        data = json.dumps({'network': address})
306
    
307
    return HttpResponse(data, status=200)