views.py 14.2 KB
Newer Older
1 2
from os import getenv
import json
Őry Máté committed
3
import logging
4 5 6
import re

from django.contrib.auth.models import User, Group
7
from django.contrib.messages import warning
8
from django.core.exceptions import PermissionDenied
9 10
from django.core import signing
from django.core.urlresolvers import reverse, reverse_lazy
11
from django.http import HttpResponse, HttpResponseRedirect
12
from django.shortcuts import redirect
13
from django.views.decorators.http import require_POST
14
from django.views.generic.detail import SingleObjectMixin
15
from django.views.generic import TemplateView, DetailView, View, DeleteView
16 17
from django.contrib import messages
from django.utils.translation import ugettext as _
18

19
from django_tables2 import SingleTableView
Kálmán Viktor committed
20

21
from .tables import VmListTable
22 23
from vm.models import (Instance, InstanceTemplate, InterfaceTemplate,
                       InstanceActivity)
24
from firewall.models import Vlan
25
from storage.models import Disk
26

Őry Máté committed
27
logger = logging.getLogger(__name__)
28

29 30

class IndexView(TemplateView):
Kálmán Viktor committed
31
    template_name = "dashboard/index.html"
32

33
    def get_context_data(self, **kwargs):
34 35 36 37 38
        if self.request.user.is_authenticated():
            user = self.request.user
        else:
            user = None

39
        instances = Instance.objects.filter(owner=user)
40 41
        context = super(IndexView, self).get_context_data(**kwargs)
        context.update({
42
            'instances': instances[:5],
43
            'more_instances': instances.count() - len(instances[:5])
44 45 46
        })

        context.update({
47 48
            'running_vms': instances.filter(state='RUNNING'),
            'running_vm_num': instances.filter(state='RUNNING').count(),
49 50
            'stopped_vm_num': instances.exclude(
                state__in=['RUNNING', 'NOSTATE']).count()
51 52 53
        })
        return context

54

55 56 57 58 59 60 61 62 63 64
def get_acl_data(obj):
    levels = obj.ACL_LEVELS
    users = obj.get_users_with_level()
    users = [{'user': u, 'level': l} for u, l in users]
    groups = obj.get_groups_with_level()
    groups = [{'group': g, 'level': l} for g, l in groups]
    return {'users': users, 'groups': groups, 'levels': levels,
            'url': reverse('dashboard.views.vm-acl', args=[obj.pk])}


65 66 67 68 69 70 71 72 73 74 75 76
class CheckedDetailView(DetailView):
    read_level = 'user'

    def get_context_data(self, **kwargs):
        context = super(CheckedDetailView, self).get_context_data(**kwargs)
        instance = context['instance']
        if not instance.has_level(self.request.user, self.read_level):
            raise PermissionDenied()
        return context


class VmDetailView(CheckedDetailView):
Kálmán Viktor committed
77
    template_name = "dashboard/vm-detail.html"
78
    model = Instance
79 80

    def get_context_data(self, **kwargs):
81
        context = super(VmDetailView, self).get_context_data(**kwargs)
82 83 84
        instance = context['instance']
        if instance.node:
            port = instance.vnc_port
85
            host = str(instance.node.host.ipv4)
86
            value = signing.dumps({'host': host,
87 88
                                   'port': port},
                                  key=getenv("PROXY_SECRET", 'asdasd')),
89 90 91
            context.update({
                'vnc_url': '%s' % value
            })
92 93 94 95 96

        # activity data
        ia = InstanceActivity.objects.filter(
            instance=self.object, parent=None
        ).order_by('-started').select_related()
97

98
        context['activity'] = ia
99
        context['acl'] = get_acl_data(instance)
100
        return context
Kálmán Viktor committed
101

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
    def post(self, request, *args, **kwargs):
        if (request.POST.get('ram-size') and request.POST.get('cpu-count')
                and request.POST.get('cpu-priority')):
            return self.__set_resources(request)

    def __set_resources(self, request):
        self.object = self.get_object()
        if not self.object.has_level(request.user, 'owner'):
            raise PermissionDenied()

        resources = {
            'num_cores': request.POST.get('cpu-count'),
            'ram_size': request.POST.get('ram-size'),
            'priority': request.POST.get('cpu-priority')
        }
        Instance.objects.filter(pk=self.object.pk).update(**resources)

        success_message = _("Resources successfully updated!")
        if request.is_ajax():
            response = {'message': success_message}
            return HttpResponse(
                json.dumps(response),
                content_type="application/json"
            )
        else:
            messages.success(request, success_message)
            return redirect(reverse_lazy("dashboard.views.detail",
                                         kwargs={'pk': self.object.pk}))

Kálmán Viktor committed
131

132
class AclUpdateView(View, SingleObjectMixin):
133

134
    def post(self, request, *args, **kwargs):
135
        instance = self.get_object()
136 137
        if not (instance.has_level(request.user, "owner") or
                getattr(instance, 'owner', None) == request.user):
Őry Máté committed
138 139
            logger.warning('Tried to set permissions of %s by non-owner %s.',
                           unicode(instance), unicode(request.user))
140
            raise PermissionDenied()
141 142 143 144 145
        self.set_levels(request, instance)
        self.add_levels(request, instance)
        return redirect(instance)

    def set_levels(self, request, instance):
146 147 148 149 150
        for key, value in request.POST.items():
            m = re.match('perm-([ug])-(\d+)', key)
            if m:
                type, id = m.groups()
                entity = {'u': User, 'g': Group}[type].objects.get(id=id)
151
                instance.set_level(entity, value)
Őry Máté committed
152 153 154
                logger.info("Set %s's acl level for %s to %s by %s.",
                            unicode(entity), unicode(instance),
                            value, unicode(request.user))
155

156
    def add_levels(self, request, instance):
157 158
        name = request.POST['perm-new-name']
        value = request.POST['perm-new']
159 160 161 162 163 164
        if not name:
            return
        try:
            entity = User.objects.get(username=name)
        except User.DoesNotExist:
            entity = None
165 166
            try:
                entity = Group.objects.get(name=name)
167 168 169 170
            except Group.DoesNotExist:
                warning(request, _('User or group "%s" not found.') % name)
                return

171
        instance.set_level(entity, value)
Őry Máté committed
172 173 174
        logger.info("Set %s's new acl level for %s to %s by %s.",
                    unicode(entity), unicode(instance),
                    value, unicode(request.user))
175

Kálmán Viktor committed
176

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
class TemplateDetail(DetailView):
    model = InstanceTemplate

    def get(self, request, *args, **kwargs):
        if request.is_ajax():
            template = InstanceTemplate.objects.get(pk=kwargs['pk'])
            template = {
                'num_cores': template.num_cores,
                'ram_size': template.ram_size,
                'priority': template.priority,
                'arch': template.arch,
                'description': template.description,
                'system': template.system,
                'name': template.name,
                'disks': [{'pk': d.pk, 'name': d.name}
                          for d in template.disks.all()],
                'network': [
                    {'vlan_pk': i.vlan.pk, 'vlan': i.vlan.name,
                     'managed': i.managed}
                    for i in InterfaceTemplate.objects.filter(
                        template=self.get_object()).all()
                ]
            }
            return HttpResponse(json.dumps(template),
                                content_type="application/json")
        else:
            # return super(TemplateDetail, self).get(request, *args, **kwargs)
            return HttpResponse('soon')


207
class VmList(SingleTableView):
Kálmán Viktor committed
208
    template_name = "dashboard/vm-list.html"
209 210
    model = Instance
    table_class = VmListTable
211
    table_pagination = False
212 213 214 215 216 217


class VmCreate(TemplateView):

    def get_template_names(self):
        if self.request.is_ajax():
218
            return ['dashboard/modal-wrapper.html']
219
        else:
220
            return ['dashboard/nojs-wrapper.html']
221 222 223

    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
224 225 226 227
        context.update({
            'template': 'dashboard/vm-create.html',
            'box_title': 'Create a VM'
        })
228
        return self.render_to_response(context)
229 230 231

    def get_context_data(self, **kwargs):
        context = super(VmCreate, self).get_context_data(**kwargs)
232
        # TODO acl
233
        context.update({
234
            'templates': InstanceTemplate.objects.all(),
235
            'vlans': Vlan.objects.all(),
236
            'disks': Disk.objects.exclude(type="qcow2-snap")
237 238 239
        })

        return context
240

241
    # TODO handle not ajax posts
242
    def post(self, request, *args, **kwargs):
243 244 245 246 247
        if self.request.user.is_authenticated():
            user = self.request.user
        else:
            user = None

248 249 250
        resp = {}
        try:
            ikwargs = {
251 252 253
                'num_cores': int(request.POST.get('cpu-count')),
                'ram_size': int(request.POST.get('ram-size')),
                'priority': int(request.POST.get('cpu-priority')),
254 255
            }

256 257
            networks = [InterfaceTemplate(vlan=Vlan.objects.get(pk=l),
                                          managed=True)
258 259
                        for l in request.POST.getlist('managed-vlans')
                        ]
260 261 262
            networks.extend([InterfaceTemplate(vlan=Vlan.objects.get(pk=l),
                                               managed=False)
                            for l in request.POST.getlist('unmanaged-vlans')
263 264 265
                             ])

            disks = Disk.objects.filter(pk__in=request.POST.getlist('disks'))
266 267
            template = InstanceTemplate.objects.get(
                pk=request.POST.get('template-pk'))
268

269
            inst = Instance.create_from_template(template=template,
270 271
                                                 owner=user, networks=networks,
                                                 disks=disks, **ikwargs)
272
            inst.deploy_async()
273

274
            resp['pk'] = inst.pk
275
            messages.success(request, _('VM successfully created!'))
276 277
        except InstanceTemplate.DoesNotExist:
            resp['error'] = True
278 279
        except Exception, e:
            print e
280
            resp['error'] = True
281

282 283 284 285 286 287
        if request.is_ajax():
            return HttpResponse(json.dumps(resp),
                                content_type="application/json",
                                status=500 if resp.get('error') else 200)
        else:
            return redirect(reverse_lazy('dashboard.views.detail', resp))
Kálmán Viktor committed
288 289


290 291
class VmDelete(DeleteView):
    model = Instance
292 293 294 295 296 297 298
    template_name = "dashboard/confirm/base-delete.html"

    def get_template_names(self):
        if self.request.is_ajax():
            return ['dashboard/confirm/ajax-delete.html']
        else:
            return ['dashboard/confirm/base-delete.html']
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316

    def get_context_data(self, **kwargs):
        # this is redundant now, but if we wanna add more to print
        # we'll need this
        context = super(VmDelete, self).get_context_data(**kwargs)
        return context

    # github.com/django/django/blob/master/django/views/generic/edit.py#L245
    def delete(self, request, *args, **kwargs):
        object = self.get_object()
        if not object.has_level(request.user, 'owner'):
            raise PermissionDenied()

        object.destroy_async()
        success_url = self.get_success_url()
        success_message = _("VM successfully deleted!")

        if request.is_ajax():
317 318
            if request.POST.get('redirect').lower() == "true":
                messages.success(request, success_message)
319 320 321 322 323 324 325 326 327 328 329 330 331 332
            return HttpResponse(
                json.dumps({'message': success_message}),
                content_type="application/json",
            )
        else:
            messages.success(request, success_message)
            return HttpResponseRedirect(success_url)

    def get_success_url(self):
        next = self.request.POST.get('next')
        if next:
            return next
        else:
            return reverse_lazy('dashboard.index')
333 334


335
@require_POST
336 337 338 339 340
def mass_delete_vm(request, **kwargs):
    vms = request.POST.getlist('vms')
    names = []
    if vms is not None:
        for i in Instance.objects.filter(pk__in=vms):
341 342 343 344 345 346
            if not i.has_level(request.user, 'owner'):
                logger.info('Tried to delete instance #%d without owner '
                            'permission by %s.', i.pk, unicode(request.user))
                raise PermissionDenied()  # no need for rollback or proper
                                          # error message, this can't
                                          # normally happen.
347 348 349 350
            i.destroy_async()
            names.append(i.name)

    success_message = _("Mass delete complete, the following VMs were " +
351
                        "deleted: %s!" % u', '.join(names))
352 353 354 355 356 357 358 359

    # we can get this only via AJAX ...
    if request.is_ajax():
        return HttpResponse(
            json.dumps({'message': success_message}),
            content_type="application/json"
        )
    else:
360 361 362
        messages.success(request, success_message)
        next = request.GET.get('next')
        return redirect(next if next else reverse_lazy('dashboard.index'))
363 364 365 366


@require_POST
def vm_activity(request, pk):
367 368 369
    if not object.has_level(request.user, 'owner'):
        raise PermissionDenied()

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
    latest = request.POST.get('latest')
    latest_sub = request.POST.get('latest_sub')

    instance = Instance.objects.get(pk=pk)
    new_sub_activities = InstanceActivity.objects.filter(
        parent=latest, pk__gt=latest_sub,
        instance=instance)
    # new_activities = InstanceActivity.objects.filter(
    #     parent=None, instance=instance, pk__gt=latest).values('finished')
    latest_sub_finished = InstanceActivity.objects.get(pk=latest_sub).finished

    time_string = "%H:%M:%S"
    new_sub_activities = [
        {'name': a.get_readable_name(), 'id': a.pk,
         'finished': None if a.finished is None else a.finished.strftime(
             time_string
         )
         } for a in new_sub_activities
    ]

    response = {
        'new_sub_activities': new_sub_activities,
        # TODO 'new_acitivites': new_activities,
        'is_parent_finished': True if InstanceActivity.objects.get(
            pk=latest).finished is not None else False,
        'latest_sub_finished': None if latest_sub_finished is None else
        latest_sub_finished.strftime(time_string)
    }

    return HttpResponse(
        json.dumps(response),
        content_type="application/json"
    )