Statistics
| Branch: | Tag: | Revision:

root / snf-app / synnefo / admin / views.py @ 483c9197

History | View | Annotate | Download (12.3 kB)

1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from functools import wraps
35

    
36
from django.conf import settings
37
from django.http import HttpResponse
38
from django.shortcuts import redirect
39
from django.template.loader import render_to_string
40

    
41
from synnefo.db import models
42
from synnefo.invitations.invitations import add_invitation, send_invitation
43
from synnefo.logic import backend, users
44
from synnefo.util.log import getLogger
45

    
46

    
47
log = getLogger('synnefo.admin')
48

    
49

    
50
def render(template, tab, **kwargs):
51
    kwargs.setdefault('tab', tab)
52
    return render_to_string(template, kwargs)
53

    
54

    
55
def requires_admin(func):
56
    @wraps(func)
57
    def wrapper(request, *args):
58
        if not request.user or request.user.type != 'ADMIN':
59
            return HttpResponse('Forbidden', status=403)
60
        return func(request, *args)
61
    return wrapper
62

    
63

    
64
def get_filters(request, session_key, all_filters, default=None):
65
    if default is None:
66
        default = all_filters
67
    filters = request.session.get(session_key, default)
68
    filter = request.GET.get('toggle_filter')
69
    if filter:
70
        if filter in filters:
71
            filters.remove(filter)
72
        elif filter in all_filters:
73
            filters.add(filter)
74
        request.session[session_key] = filters
75
    return filters
76

    
77

    
78
@requires_admin
79
def index(request):
80
    stats = {}
81
    stats['users'] = models.SynnefoUser.objects.count()
82
    stats['images'] = models.Image.objects.exclude(state='DELETED').count()
83
    stats['flavors'] = models.Flavor.objects.count()
84
    stats['vms'] = models.VirtualMachine.objects.filter(deleted=False).count()
85
    stats['networks'] = models.Network.objects.exclude(state='DELETED').count()
86
    stats['invitations'] = models.Invitations.objects.count()
87

    
88
    stats['ganeti_instances'] = len(backend.get_ganeti_instances())
89
    stats['ganeti_nodes'] = len(backend.get_ganeti_nodes())
90
    stats['ganeti_jobs'] = len(backend.get_ganeti_jobs())
91

    
92
    images = []
93
    for image in models.Image.objects.exclude(state='DELETED'):
94
        vms = models.VirtualMachine.objects.filter(sourceimage=image)
95
        count = vms.filter(deleted=False).count()
96
        images.append((count, image.name))
97
    images.sort(reverse=True)
98

    
99
    html = render('index.html', 'home', stats=stats, images=images)
100
    return HttpResponse(html)
101

    
102

    
103
@requires_admin
104
def flavors_list(request):
105
    all_states = set(['DELETED'])
106
    default = set()
107
    filters = get_filters(request, 'flavors_filters', all_states, default)
108
    
109
    flavors = models.Flavor.objects.all()
110
    if 'DELETED' not in filters:
111
        flavors = flavors.exclude(deleted=True)
112
    
113
    html = render('flavors_list.html', 'flavors',
114
                    flavors=flavors,
115
                    all_states=sorted(all_states),
116
                    filters=filters)
117
    return HttpResponse(html)
118

    
119

    
120
@requires_admin
121
def flavors_create(request):
122
    if request.method == 'GET':
123
        html = render('flavors_create.html', 'flavors')
124
        return HttpResponse(html)
125
    if request.method == 'POST':
126
        flavor = models.Flavor()
127
        flavor.cpu = int(request.POST.get('cpu'))
128
        flavor.ram = int(request.POST.get('ram'))
129
        flavor.disk = int(request.POST.get('disk'))
130
        flavor.save()
131
        log.info('User %s created Flavor %s', request.user.name, flavor.name)
132
        return redirect(flavors_info, flavor.id)
133

    
134

    
135
@requires_admin
136
def flavors_info(request, flavor_id):
137
    flavor = models.Flavor.objects.get(id=flavor_id)
138
    html = render('flavors_info.html', 'flavors',
139
                    flavor=flavor,
140
                    disk_templates=settings.GANETI_DISK_TEMPLATES)
141
    return HttpResponse(html)
142

    
143

    
144
@requires_admin
145
def flavors_modify(request, flavor_id):
146
    flavor = models.Flavor.objects.get(id=flavor_id)
147
    flavor.cpu = int(request.POST.get('cpu'))
148
    flavor.ram = int(request.POST.get('ram'))
149
    flavor.disk = int(request.POST.get('disk'))
150
    flavor.disk_template = request.POST.get('disk_template')
151
    flavor.deleted = True if request.POST.get('deleted') else False
152
    flavor.save()
153
    log.info('User %s modified Flavor %s', request.user.name, flavor.name)
154
    return redirect(flavors_info, flavor.id)
155

    
156

    
157
@requires_admin
158
def flavors_delete(request, flavor_id):
159
    flavor = models.Flavor.objects.get(id=flavor_id)
160
    flavor.delete()
161
    log.info('User %s deleted Flavor %s', request.user.name, flavor.name)
162
    return redirect(flavors_list)
163

    
164

    
165
@requires_admin
166
def images_list(request):
167
    all_states = set(x[0] for x in models.Image.IMAGE_STATES)
168
    default = all_states - set(['DELETED'])
169
    filters = get_filters(request, 'images_filters', all_states, default)
170
    
171
    images = models.Image.objects.all()
172
    for state in all_states - filters:
173
        images = images.exclude(state=state)
174
    
175
    html = render('images_list.html', 'images',
176
                    images=images.order_by('id'),
177
                    all_states=sorted(all_states),
178
                    filters=filters)
179
    return HttpResponse(html)
180

    
181

    
182
@requires_admin
183
def images_register(request):
184
    if request.method == 'GET':
185
        formats = [x[0] for x in models.Image.FORMATS]
186
        html = render('images_register.html', 'images', formats=formats)
187
        return HttpResponse(html)
188
    elif request.method == 'POST':
189
        image = models.Image()
190
        image.state = 'ACTIVE'
191
        image.name = request.POST.get('name')
192
        owner_id = request.POST.get('owner') or None
193
        image.owner = owner_id and models.SynnefoUser.objects.get(id=owner_id)
194
        image.backend_id = request.POST.get('backend')
195
        image.format = request.POST.get('format')
196
        image.public = True if request.POST.get('public') else False
197
        image.save()
198
        log.info('User %s registered Image %s', request.user.name, image.name)
199
        return redirect(images_info, image.id)
200

    
201

    
202
@requires_admin
203
def images_info(request, image_id):
204
    image = models.Image.objects.get(id=image_id)
205
    states = [x[0] for x in models.Image.IMAGE_STATES]
206
    if not image.state:
207
        states = [''] + states
208
    formats = [x[0] for x in models.Image.FORMATS]
209
    if not image.format:
210
        formats = [''] + formats
211
    
212
    metadata = image.metadata.order_by('meta_key')
213
    html = render('images_info.html', 'images',
214
                    image=image,
215
                    states=states,
216
                    formats=formats,
217
                    metadata=metadata)
218
    return HttpResponse(html)
219

    
220

    
221
@requires_admin
222
def images_modify(request, image_id):
223
    image = models.Image.objects.get(id=image_id)
224
    image.name = request.POST.get('name')
225
    image.state = request.POST.get('state')
226
    owner_id = request.POST.get('owner') or None
227
    image.owner = owner_id and models.SynnefoUser.objects.get(id=owner_id)
228
    vm_id = request.POST.get('sourcevm') or None
229
    image.sourcevm = vm_id and models.VirtualMachine.objects.get(id=vm_id)
230
    image.backend_id = request.POST.get('backend')
231
    image.format = request.POST.get('format')
232
    image.public = True if request.POST.get('public') else False
233
    image.save()
234
    
235
    keys = request.POST.getlist('key')
236
    vals = request.POST.getlist('value')
237
    meta = dict(zip(keys, vals))
238
    image.metadata.all().delete()
239
    for key, val in meta.items():
240
        if key:
241
            image.metadata.create(meta_key=key, meta_value=val)
242
    
243
    log.info('User %s modified Image %s', request.user.name, image.name)
244

    
245
    return redirect(images_info, image.id)
246

    
247

    
248
@requires_admin
249
def servers_list(request):
250
    all_states = set(x[0] for x in models.VirtualMachine.OPER_STATES)
251
    default = all_states - set(['DESTROYED'])
252
    filters = get_filters(request, 'servers_filters', all_states, default)
253
    
254
    servers = models.VirtualMachine.objects.all()
255
    for state in all_states - filters:
256
        servers = servers.exclude(operstate=state)
257
    
258
    html = render('servers_list.html', 'servers',
259
                    servers=servers.order_by('id'),
260
                    all_states=sorted(all_states),
261
                    filters=filters)
262
    return HttpResponse(html)
263

    
264

    
265
@requires_admin
266
def users_list(request):
267
    all_states = set(x[0] for x in models.SynnefoUser.ACCOUNT_STATE)
268
    default = all_states - set(['DELETED'])
269
    filters = get_filters(request, 'users_filters', all_states, default)
270
    
271
    users = models.SynnefoUser.objects.all()
272
    for state in all_states - filters:
273
        users = users.exclude(state=state)
274
    
275
    html = render('users_list.html', 'users',
276
                    users=users.order_by('id'),
277
                    all_states=sorted(all_states),
278
                    filters=filters)
279
    return HttpResponse(html)
280

    
281

    
282
@requires_admin
283
def users_invite(request):
284
    if request.method == 'GET':
285
        html = render('users_invite.html', 'users')
286
        return HttpResponse(html)
287
    elif request.method == 'POST':
288
        inviter_id = request.POST.get('inviter')
289
        realname = request.POST.get('realname')
290
        uniq = request.POST.get('uniq')
291
        inviter = models.SynnefoUser.objects.get(id=inviter_id)
292
        inv = add_invitation(inviter, realname, uniq)
293
        send_invitation(inv)
294
        log.info('User %s sent Invitation to %s as %s', request.user.name,
295
                    uniq, inviter.name)
296
        return redirect(users_list)
297

    
298

    
299
@requires_admin
300
def users_info(request, user_id):
301
    user = models.SynnefoUser.objects.get(id=user_id)
302
    types = [x[0] for x in models.SynnefoUser.ACCOUNT_TYPE]
303
    if not user.type:
304
        types = [''] + types
305
    states = [x[0] for x in models.SynnefoUser.ACCOUNT_STATE]
306
    html = render('users_info.html', 'users',
307
                    user=user, types=types, states=states)
308
    return HttpResponse(html)
309

    
310

    
311
@requires_admin
312
def users_modify(request, user_id):
313
    user = models.SynnefoUser.objects.get(id=user_id)
314
    user.name = request.POST.get('name')
315
    user.realname = request.POST.get('realname')
316
    user.uniq = request.POST.get('uniq')
317
    user.credit = int(request.POST.get('credit'))
318
    user.type = request.POST.get('type')
319
    user.state = request.POST.get('state')
320
    invitations = request.POST.get('invitations')
321
    user.max_invitations = int(invitations) if invitations else None
322
    user.save()
323
    log.info('User %s modified User %s', request.user.name, user.name)
324
    return redirect(users_info, user.id)
325

    
326

    
327
@requires_admin
328
def users_delete(request, user_id):
329
    user = models.SynnefoUser.objects.get(id=user_id)
330
    users.delete_user(user)
331
    log.info('User %s deleted User %s', request.user.name, user.name)
332
    return redirect(users_list)
333

    
334

    
335
@requires_admin
336
def invitations_list(request):
337
    invitations = models.Invitations.objects.order_by('id')
338
    html = render('invitations_list.html', 'invitations',
339
                     invitations=invitations)
340
    return HttpResponse(html)
341

    
342

    
343
@requires_admin
344
def invitations_resend(request, invitation_id):
345
    invitation = models.Invitations.objects.get(id=invitation_id)
346
    send_invitation(invitation)
347
    log.info('User %s resent Invitations from %s to %s', request.user.name,
348
                invitation.source.name, invitation.target.name)
349
    return redirect(invitations_list)