views.py 13.1 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
class AclUpdateView(View, SingleObjectMixin):
104

105
    def post(self, request, *args, **kwargs):
106
        instance = self.get_object()
107 108
        if not (instance.has_level(request.user, "owner") or
                getattr(instance, 'owner', None) == request.user):
Őry Máté committed
109 110
            logger.warning('Tried to set permissions of %s by non-owner %s.',
                           unicode(instance), unicode(request.user))
111
            raise PermissionDenied()
112 113 114 115 116
        self.set_levels(request, instance)
        self.add_levels(request, instance)
        return redirect(instance)

    def set_levels(self, request, instance):
117 118 119 120 121
        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)
122
                instance.set_level(entity, value)
Őry Máté committed
123 124 125
                logger.info("Set %s's acl level for %s to %s by %s.",
                            unicode(entity), unicode(instance),
                            value, unicode(request.user))
126

127
    def add_levels(self, request, instance):
128 129
        name = request.POST['perm-new-name']
        value = request.POST['perm-new']
130 131 132 133 134 135
        if not name:
            return
        try:
            entity = User.objects.get(username=name)
        except User.DoesNotExist:
            entity = None
136 137
            try:
                entity = Group.objects.get(name=name)
138 139 140 141
            except Group.DoesNotExist:
                warning(request, _('User or group "%s" not found.') % name)
                return

142
        instance.set_level(entity, value)
Őry Máté committed
143 144 145
        logger.info("Set %s's new acl level for %s to %s by %s.",
                    unicode(entity), unicode(instance),
                    value, unicode(request.user))
146

Kálmán Viktor committed
147

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
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')


178
class VmList(SingleTableView):
Kálmán Viktor committed
179
    template_name = "dashboard/vm-list.html"
180 181
    model = Instance
    table_class = VmListTable
182
    table_pagination = False
183 184 185 186 187 188


class VmCreate(TemplateView):

    def get_template_names(self):
        if self.request.is_ajax():
189
            return ['dashboard/modal-wrapper.html']
190
        else:
191
            return ['dashboard/nojs-wrapper.html']
192 193 194

    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
195 196 197 198
        context.update({
            'template': 'dashboard/vm-create.html',
            'box_title': 'Create a VM'
        })
199
        return self.render_to_response(context)
200 201 202

    def get_context_data(self, **kwargs):
        context = super(VmCreate, self).get_context_data(**kwargs)
203
        # TODO acl
204
        context.update({
205
            'templates': InstanceTemplate.objects.all(),
206
            'vlans': Vlan.objects.all(),
207
            'disks': Disk.objects.exclude(type="qcow2-snap")
208 209 210
        })

        return context
211

212
    # TODO handle not ajax posts
213
    def post(self, request, *args, **kwargs):
214 215 216 217 218
        if self.request.user.is_authenticated():
            user = self.request.user
        else:
            user = None

219 220 221
        resp = {}
        try:
            ikwargs = {
222 223 224
                'num_cores': int(request.POST.get('cpu-count')),
                'ram_size': int(request.POST.get('ram-size')),
                'priority': int(request.POST.get('cpu-priority')),
225 226
            }

227 228
            networks = [InterfaceTemplate(vlan=Vlan.objects.get(pk=l),
                                          managed=True)
229 230
                        for l in request.POST.getlist('managed-vlans')
                        ]
231 232 233
            networks.extend([InterfaceTemplate(vlan=Vlan.objects.get(pk=l),
                                               managed=False)
                            for l in request.POST.getlist('unmanaged-vlans')
234 235 236
                             ])

            disks = Disk.objects.filter(pk__in=request.POST.getlist('disks'))
237 238
            template = InstanceTemplate.objects.get(
                pk=request.POST.get('template-pk'))
239

240
            inst = Instance.create_from_template(template=template,
241 242
                                                 owner=user, networks=networks,
                                                 disks=disks, **ikwargs)
243
            inst.deploy_async()
244

245
            resp['pk'] = inst.pk
246
            messages.success(request, _('VM successfully created!'))
247 248
        except InstanceTemplate.DoesNotExist:
            resp['error'] = True
249 250
        except Exception, e:
            print e
251
            resp['error'] = True
252

253 254 255 256 257 258
        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
259 260


261 262
class VmDelete(DeleteView):
    model = Instance
263 264 265 266 267 268 269
    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']
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287

    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():
288 289
            if request.POST.get('redirect').lower() == "true":
                messages.success(request, success_message)
290 291 292 293 294 295 296 297 298 299 300 301 302 303
            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')
304 305


306
@require_POST
307 308 309 310 311
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):
312 313 314 315 316 317
            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.
318 319 320 321
            i.destroy_async()
            names.append(i.name)

    success_message = _("Mass delete complete, the following VMs were " +
322
                        "deleted: %s!" % u', '.join(names))
323 324 325 326 327 328 329 330

    # we can get this only via AJAX ...
    if request.is_ajax():
        return HttpResponse(
            json.dumps({'message': success_message}),
            content_type="application/json"
        )
    else:
331 332 333
        messages.success(request, success_message)
        next = request.GET.get('next')
        return redirect(next if next else reverse_lazy('dashboard.index'))
334 335 336 337


@require_POST
def vm_activity(request, pk):
338 339 340
    if not object.has_level(request.user, 'owner'):
        raise PermissionDenied()

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
    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"
    )