views.py 11.1 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 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337

    def get_disk(self, *args, **kwargs):
        return get_object_or_404(Disk, pk=self.kwargs['disk_pk'])

    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):
        vm = self.get_vm()
        disk = self.get_disk()
        data = form.cleaned_data
        user = self.request.user

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

338 339
        req = Request(user=user, message=data['message'], action=dra,
                      type=Request.TYPES.resize)
340 341
        req.save()

342 343 344 345 346 347
        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())