views.py 12.9 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 229 230 231 232 233 234 235
            networks = [{'vlan': Vlan.objects.get(pk=l), 'managed': True}
                        for l in request.POST.getlist('managed-vlans')
                        ]
            networks.extend([{'vlan': Vlan.objects.get(pk=l),
                              'managed': False}
                             for l in request.POST.getlist('unmanaged-vlans')
                             ])

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

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

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

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


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

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


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

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

    # we can get this only via AJAX ...
    if request.is_ajax():
        return HttpResponse(
            json.dumps({'message': success_message}),
            content_type="application/json"
        )
    else:
330 331 332
        messages.success(request, success_message)
        next = request.GET.get('next')
        return redirect(next if next else reverse_lazy('dashboard.index'))
333 334 335 336 337 338 339 340 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


@require_POST
def vm_activity(request, pk):
    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"
    )