views.py 11.2 KB
Newer Older
Kálmán Viktor committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
Kálmán Viktor committed
17 18 19
from __future__ import unicode_literals, absolute_import

from django.views.generic import (
20
    UpdateView, TemplateView, DetailView, CreateView, FormView, DeleteView,
Kálmán Viktor committed
21
)
22
from django.contrib import messages
23
from django.contrib.messages.views import SuccessMessageMixin
24
from django.shortcuts import redirect, get_object_or_404
25
from django.core.exceptions import PermissionDenied, SuspiciousOperation
26
from django.core.urlresolvers import reverse
27
from django.utils.translation import ugettext as _
28
from django.http import JsonResponse
Kálmán Viktor committed
29 30 31 32

from braces.views import SuperuserRequiredMixin, LoginRequiredMixin
from django_tables2 import SingleTableView

33 34
from request.models import (
    Request, TemplateAccessType, LeaseType, TemplateAccessAction,
35
    ExtendLeaseAction, ResourceChangeAction, DiskResizeAction
36
)
37
from storage.models import Disk
38
from vm.models import Instance
Kálmán Viktor committed
39 40 41 42
from request.tables import (
    RequestTable, TemplateAccessTypeTable, LeaseTypeTable,
)
from request.forms import (
43
    LeaseTypeForm, TemplateAccessTypeForm, TemplateRequestForm,
44
    LeaseRequestForm, ResourceRequestForm, ResizeRequestForm,
Kálmán Viktor committed
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
)


class RequestList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
    model = Request
    table_class = RequestTable
    template_name = "request/list.html"

    def get_context_data(self, **kwargs):
        context = super(RequestList, self).get_context_data(**kwargs)
        context['statuses'] = Request.STATUSES
        return context

    def get_table_data(self):
        data = Request.objects.all()
        status = self.request.GET.get("status")
        if status:
            data = data.filter(status=status)

        return data


67
class RequestDetail(LoginRequiredMixin, DetailView):
Kálmán Viktor committed
68 69 70
    model = Request
    template_name = "request/detail.html"

71
    def post(self, *args, **kwargs):
72 73 74 75 76 77 78
        user = self.request.user
        request = self.get_object()  # not self.request!

        if not user.is_superuser:
            raise SuspiciousOperation

        if self.get_object().status == "PENDING":
79
            accept = self.request.POST.get("accept")
80
            reason = self.request.POST.get("reason")
81 82 83
            if accept:
                request.accept(user)
            else:
84
                request.decline(user, reason)
85 86 87

        return redirect(request.get_absolute_url())

88 89
    def get_context_data(self, **kwargs):
        request = self.object
90 91 92 93 94
        user = self.request.user

        if not user.is_superuser and request.user != user:
            raise SuspiciousOperation

95 96 97
        context = super(RequestDetail, self).get_context_data(**kwargs)

        context['action'] = request.action
98
        context['is_acceptable'] = request.is_acceptable
99 100
        # workaround for http://git.io/vIIYi
        context['request'] = self.request
101 102 103

        return context

Kálmán Viktor committed
104 105

class TemplateAccessTypeDetail(LoginRequiredMixin, SuperuserRequiredMixin,
106
                               SuccessMessageMixin, UpdateView):
Kálmán Viktor committed
107 108 109
    model = TemplateAccessType
    template_name = "request/template-type-form.html"
    form_class = TemplateAccessTypeForm
110
    success_message = _("Template access type successfully updated.")
Kálmán Viktor committed
111 112 113


class TemplateAccessTypeCreate(LoginRequiredMixin, SuperuserRequiredMixin,
114
                               SuccessMessageMixin, CreateView):
Kálmán Viktor committed
115 116 117
    model = TemplateAccessType
    template_name = "request/template-type-form.html"
    form_class = TemplateAccessTypeForm
118
    success_message = _("New template access type successfully created.")
Kálmán Viktor committed
119 120


121 122 123 124 125 126 127 128 129
class TemplateAccessTypeDelete(LoginRequiredMixin, SuperuserRequiredMixin,
                               DeleteView):
    model = TemplateAccessType
    template_name = "dashboard/confirm/base-delete.html"

    def get_success_url(self):
        return reverse("request.views.type-list")


130 131
class LeaseTypeDetail(LoginRequiredMixin, SuperuserRequiredMixin,
                      SuccessMessageMixin, UpdateView):
Kálmán Viktor committed
132 133 134
    model = LeaseType
    template_name = "request/lease-type-form.html"
    form_class = LeaseTypeForm
135
    success_message = _("Lease type successfully updated.")
Kálmán Viktor committed
136 137


138 139
class LeaseTypeCreate(LoginRequiredMixin, SuperuserRequiredMixin,
                      SuccessMessageMixin, CreateView):
Kálmán Viktor committed
140 141 142
    model = LeaseType
    template_name = "request/lease-type-form.html"
    form_class = LeaseTypeForm
143
    success_message = _("New lease type successfully created.")
Kálmán Viktor committed
144 145


146 147 148 149 150 151 152 153
class LeaseTypeDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
    model = LeaseType
    template_name = "dashboard/confirm/base-delete.html"

    def get_success_url(self):
        return reverse("request.views.type-list")


Kálmán Viktor committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
class RequestTypeList(LoginRequiredMixin, SuperuserRequiredMixin,
                      TemplateView):
    template_name = "request/type-list.html"

    def get_context_data(self, **kwargs):
        context = super(RequestTypeList, self).get_context_data(**kwargs)

        context['lease_table'] = LeaseTypeTable(
            LeaseType.objects.all(), request=self.request)
        context['template_table'] = TemplateAccessTypeTable(
            TemplateAccessType.objects.all(), request=self.request)

        return context


169
class TemplateRequestView(LoginRequiredMixin, FormView):
Kálmán Viktor committed
170 171
    form_class = TemplateRequestForm
    template_name = "request/request-template.html"
172
    success_message = _("Request successfully sent.")
Kálmán Viktor committed
173

174 175 176 177 178
    def get_form_kwargs(self):
        kwargs = super(TemplateRequestView, self).get_form_kwargs()
        kwargs['request'] = self.request
        return kwargs

Kálmán Viktor committed
179 180 181 182 183 184 185 186 187 188 189 190 191
    def form_valid(self, form):
        data = form.cleaned_data
        user = self.request.user

        ta = TemplateAccessAction(
            template_type=data['template'],
            level=data['level'],
            user=user,
        )
        ta.save()

        req = Request(
            user=user,
192
            message=data['message'],
Kálmán Viktor committed
193 194 195 196 197
            type=Request.TYPES.template,
            action=ta
        )
        req.save()

198 199
        messages.success(self.request, self.success_message)
        return redirect(reverse("dashboard.index"))
200 201


202
class VmRequestMixin(LoginRequiredMixin, object):
203 204 205 206 207
    def get_vm(self):
        return get_object_or_404(Instance, pk=self.kwargs['vm_pk'])

    def dispatch(self, *args, **kwargs):
        vm = self.get_vm()
208
        user = self.request.user
209
        if not vm.has_level(user, self.user_level):
210
            raise PermissionDenied()
211
        return super(VmRequestMixin, self).dispatch(*args, **kwargs)
212

213
    def get_context_data(self, **kwargs):
214
        context = super(VmRequestMixin, self).get_context_data(**kwargs)
215
        context['vm'] = self.get_vm()
216 217
        return context

218
    def get_form_kwargs(self):
219
        kwargs = super(VmRequestMixin, self).get_form_kwargs()
220 221 222
        kwargs['request'] = self.request
        return kwargs

223
    def form_valid(self, form):
224 225 226 227 228 229 230
        raise NotImplementedError


class LeaseRequestView(VmRequestMixin, FormView):
    form_class = LeaseRequestForm
    template_name = "request/request-lease.html"
    user_level = "operator"
231
    success_message = _("Request successfully sent.")
232 233

    def form_valid(self, form):
234 235 236 237 238 239 240 241 242 243 244 245
        data = form.cleaned_data
        user = self.request.user
        vm = self.get_vm()

        el = ExtendLeaseAction(
            lease_type=data['lease'],
            instance=vm,
        )
        el.save()

        req = Request(
            user=user,
246
            message=data['message'],
247 248 249 250 251
            type=Request.TYPES.lease,
            action=el
        )
        req.save()

252
        messages.success(self.request, self.success_message)
253
        return redirect(vm.get_absolute_url())
254 255


256
class ResourceRequestView(VmRequestMixin, FormView):
257 258
    form_class = ResourceRequestForm
    template_name = "request/request-resource.html"
259
    user_level = "user"
260
    success_message = _("Request successfully sent.")
261 262 263 264

    def get_form_kwargs(self):
        kwargs = super(ResourceRequestView, self).get_form_kwargs()
        kwargs['can_edit'] = True
265
        kwargs['instance'] = self.get_vm()
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
        return kwargs

    def get_initial(self):
        vm = self.get_vm()
        initial = super(ResourceRequestView, self).get_initial()
        initial['num_cores'] = vm.num_cores
        initial['priority'] = vm.priority
        initial['ram_size'] = vm.ram_size
        return initial

    def form_valid(self, form):
        vm = self.get_vm()
        data = form.cleaned_data
        user = self.request.user

        rc = ResourceChangeAction(
            instance=vm,
            num_cores=data['num_cores'],
            priority=data['priority'],
            ram_size=data['ram_size'],
        )
        rc.save()

        req = Request(
            user=user,
291
            message=data['message'],
292 293 294 295 296
            type=Request.TYPES.resource,
            action=rc
        )
        req.save()

297
        messages.success(self.request, self.success_message)
298
        return redirect(vm.get_absolute_url())
299 300 301 302 303 304


class ResizeRequestView(VmRequestMixin, FormView):
    form_class = ResizeRequestForm
    template_name = "request/_request-resize-form.html"
    user_level = "owner"
305
    success_message = _("Request successfully sent.")
306 307

    def get_disk(self, *args, **kwargs):
308 309 310 311
        disk = get_object_or_404(Disk, pk=self.kwargs['disk_pk'])
        if disk not in self.get_vm().disks.all():
            raise SuspiciousOperation
        return disk
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333

    def get_form_kwargs(self):
        kwargs = super(ResizeRequestView, self).get_form_kwargs()
        kwargs['disk'] = self.get_disk()
        return kwargs

    def get_template_names(self):
        if self.request.is_ajax():
            return ['dashboard/_modal.html']
        else:
            return ['dashboard/_base.html']

    def get_context_data(self, **kwargs):
        context = super(ResizeRequestView, self).get_context_data(**kwargs)
        context['disk'] = self.get_disk()
        context['template'] = self.template_name
        context['box_title'] = context['title'] = _("Disk resize request")
        context['ajax_title'] = True
        return context

    def form_valid(self, form):
        disk = self.get_disk()
334 335 336 337
        if not disk.is_resizable:
            raise SuspiciousOperation

        vm = self.get_vm()
338 339 340 341 342 343
        data = form.cleaned_data
        user = self.request.user

        dra = DiskResizeAction(instance=vm, disk=disk, size=data['size'])
        dra.save()

344 345
        req = Request(user=user, message=data['message'], action=dra,
                      type=Request.TYPES.resize)
346 347
        req.save()

348 349 350 351 352 353
        if self.request.is_ajax():
            return JsonResponse({'success': True,
                                 'messages': [self.success_message]})
        else:
            messages.success(self.request, self.success_message)
            return redirect(vm.get_absolute_url())