rest.py 10 KB
Newer Older
Lukas Burgey's avatar
Lukas Burgey committed
1 2 3

import logging

Lukas Burgey's avatar
Lukas Burgey committed
4
from django.urls import path, re_path
5
from django.contrib.auth import logout
6
from django.shortcuts import get_object_or_404
7
from rest_framework import generics, exceptions, views
8
from rest_framework.response import Response
9
from rest_framework.permissions import AllowAny
Lukas Burgey's avatar
Lukas Burgey committed
10

11
from feudal.backend.views.renderers import PlainTextRenderer
12
from feudal.backend.models import Service
13
from feudal.backend.models.users import UserPreferences
14 15
from feudal.backend.models.serializers import (
    UserStateSerializer, ServiceSerializer, SSHPublicKeySerializer,
lukasburgey's avatar
lukasburgey committed
16 17
    DeploymentSerializer, DeploymentStateSerializer,
    UserPreferencesSerializer
18 19 20 21
)
from feudal.backend.models.deployments import VODeployment, ServiceDeployment
from feudal.backend.models.auth.vos import VO
from feudal.backend.models.auth.serializers import VOSerializer
Lukas Burgey's avatar
Lukas Burgey committed
22
from feudal.backend.permissions import UserOnly
Lukas Burgey's avatar
Lukas Burgey committed
23 24

LOGGER = logging.getLogger(__name__)
25 26 27
HELP_TEXT = """FEUDAL USER REST INTERFACE


28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
PATH            METHOD  DESCRIPTION

user            GET     Show your user
                DELETE  Delete your user

services        GET     Show services available to you
service/<id>    GET     Show service with id <id>

vos             GET     Show virtual organisations available to you
vo/<id>         GET     Show virtual organisation with id <id>

ssh-keys        GET     Show your ssh keys
                POST    Upload a new ssh key

ssh-key/<id>    GET     Show your ssh key with id <id>
                DELETE  Delete your ssh key with id <id>

deployments
deployments/service
deployments/vo
                GET     Show all / service / vo deployments

deployment/vo/<vo id>
                GET     Show your deployment for the VO with <vo id>
                PATCH   Request deployment or removal by patching state_target to deployed
                        or not_deployed
deployment/service/<service id>
                GET     Show your deployment for the service with <service id>
                PATCH   Request deployment or removal by patching state_target to deployed
                        or not_deployed

states          GET     Show your deployment states
state/<id>      GET     Show your deployment state with id <id>
                PATCH   Patch answers to answer questions by the client. This is needed if your
                        deployment state has state=questionnaire
"""

Lukas Burgey's avatar
Lukas Burgey committed
65 66
PERMISSION_CLASSES = (UserOnly,)

67 68
class HelpView(views.APIView):
    permission_classes = (AllowAny,)
69
    renderer_classes = (PlainTextRenderer,)
70

71
    def get(self, request):
72
        return Response(HELP_TEXT)
Lukas Burgey's avatar
Lukas Burgey committed
73 74


lukasburgey's avatar
lukasburgey committed
75
class UserView(generics.RetrieveDestroyAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
76
    permission_classes = PERMISSION_CLASSES
77
    serializer_class = UserStateSerializer
78 79

    def get_object(self):
Lukas Burgey's avatar
Lukas Burgey committed
80 81
        return self.request.user

lukasburgey's avatar
lukasburgey committed
82
    def perform_destroy(self, instance):
Lukas Burgey's avatar
Lukas Burgey committed
83 84 85 86 87
        LOGGER.info('Deleting user on user request')
        logout(self.request)
        instance.delete()


lukasburgey's avatar
lukasburgey committed
88 89 90 91 92 93 94 95 96 97
class UserPreferencesView(generics.RetrieveUpdateAPIView):
    permission_classes = PERMISSION_CLASSES
    serializer_class = UserPreferencesSerializer

    def get_object(self):
        return self.request.user.preferences


    def perform_update(self, serializer):

98
        preferences = serializer.save()
lukasburgey's avatar
lukasburgey committed
99 100 101

        LOGGER.debug(self.request.user.msg('Updated preferences'))

102 103 104 105 106 107 108 109 110 111 112 113
        if preferences.deployment_mode == UserPreferences.DEPLOYMENT_MODE_SERVICES_ONLY:
            qs = self.request.user.deployments.instance_of(VODeployment)
            if qs.exists():
                LOGGER.debug(self.request.user.msg('Removing my VODeployments because of a new preference'))
                qs.delete()

        elif preferences.deployment_mode == UserPreferences.DEPLOYMENT_MODE_VOS_ONLY:
            qs = self.request.user.deployments.instance_of(ServiceDeployment)
            if qs.exists():
                LOGGER.debug(self.request.user.msg('Removing my ServiceDeployments because of a new preference'))
                qs.delete()

lukasburgey's avatar
lukasburgey committed
114

Lukas Burgey's avatar
Lukas Burgey committed
115
class ServiceListView(generics.ListAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
116
    permission_classes = PERMISSION_CLASSES
117
    serializer_class = ServiceSerializer
118

Lukas Burgey's avatar
Lukas Burgey committed
119
    def get_queryset(self):
120
        return Service.objects.filter(vos__user=self.request.user)
Lukas Burgey's avatar
Lukas Burgey committed
121 122 123


class ServiceView(generics.RetrieveAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
124
    permission_classes = PERMISSION_CLASSES
125
    serializer_class = ServiceSerializer
Lukas Burgey's avatar
Lukas Burgey committed
126 127

    def get_object(self):
128 129
        available_services = Service.objects.filter(vos__user=self.request.user).distinct()
        LOGGER.debug('Available services: %s', available_services)
130
        return get_object_or_404(
131
            available_services,
Lukas Burgey's avatar
Lukas Burgey committed
132
            id=self.kwargs['id'],
133 134
        )

Lukas Burgey's avatar
Lukas Burgey committed
135 136

class VOListView(generics.ListAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
137
    permission_classes = PERMISSION_CLASSES
Lukas Burgey's avatar
Lukas Burgey committed
138 139 140 141 142 143 144
    serializer_class = VOSerializer

    def get_queryset(self):
        return self.request.user.vos.all()


class VOView(generics.RetrieveAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
145
    permission_classes = PERMISSION_CLASSES
Lukas Burgey's avatar
Lukas Burgey committed
146 147 148 149 150 151 152
    serializer_class = VOSerializer

    def get_object(self):
        return get_object_or_404(
            self.request.user.vos.all(),
            id=self.kwargs['id'],
        )
153 154 155


class SSHPublicKeyListView(generics.ListCreateAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
156
    permission_classes = PERMISSION_CLASSES
157
    serializer_class = SSHPublicKeySerializer
158 159 160 161 162 163

    def get_queryset(self):
        return self.request.user.ssh_keys.all()

    def perform_create(self, serializer):
        key = serializer.save(user=self.request.user)
Lukas Burgey's avatar
Lukas Burgey committed
164
        self.request.user.add_key(key)
165 166


Lukas Burgey's avatar
Lukas Burgey committed
167
class SSHPublicKeyView(generics.RetrieveDestroyAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
168
    permission_classes = PERMISSION_CLASSES
169
    serializer_class = SSHPublicKeySerializer
170

Lukas Burgey's avatar
Lukas Burgey committed
171 172 173 174 175
    def get_object(self):
        return get_object_or_404(
            self.request.user.ssh_keys.all(),
            id=self.kwargs['id'],
        )
176

Lukas Burgey's avatar
Lukas Burgey committed
177 178
    def perform_destroy(self, instance):
        self.request.user.remove_key(instance)
179

Lukas Burgey's avatar
Lukas Burgey committed
180

Lukas Burgey's avatar
Lukas Burgey committed
181
class DeploymentListView(generics.ListAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
182
    permission_classes = PERMISSION_CLASSES
183
    serializer_class = DeploymentSerializer
Lukas Burgey's avatar
Lukas Burgey committed
184 185

    def get_queryset(self):
Lukas Burgey's avatar
Lukas Burgey committed
186
        if self.kwargs.get('type', '') == 'service':
187
            return self.request.user.deployments.instance_of(ServiceDeployment)
Lukas Burgey's avatar
Lukas Burgey committed
188
        if self.kwargs.get('type', '') == 'vo':
189
            return self.request.user.deployments.instance_of(VODeployment)
Lukas Burgey's avatar
Lukas Burgey committed
190 191

        return self.request.user.deployments.all()
Lukas Burgey's avatar
Lukas Burgey committed
192 193


Lukas Burgey's avatar
Lukas Burgey committed
194
class DeploymentView(generics.RetrieveUpdateAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
195
    permission_classes = PERMISSION_CLASSES
196
    serializer_class = DeploymentSerializer
Lukas Burgey's avatar
Lukas Burgey committed
197 198

    def get_serializer_context(self):
Lukas Burgey's avatar
Lukas Burgey committed
199
        if self.kwargs['type'] == 'vo':
Lukas Burgey's avatar
Lukas Burgey committed
200
            self.request.data['resourcetype'] = 'VODeployment'
Lukas Burgey's avatar
Lukas Burgey committed
201 202
        elif self.kwargs['type'] == 'service':
            self.request.data['resourcetype'] = 'ServiceDeployment'
Lukas Burgey's avatar
Lukas Burgey committed
203 204 205 206 207 208

        return {
            'request': self.request,
        }

    def get_object(self):
Lukas Burgey's avatar
Lukas Burgey committed
209 210 211 212 213
        dep_id = self.kwargs['id']

        if self.kwargs['type'] == 'vo':
            try:
                vo = self.request.user.vos.get(id=dep_id)
214
                return VODeployment.get_or_create(self.request.user, vo)
Lukas Burgey's avatar
Lukas Burgey committed
215 216 217 218 219

            except VO.DoesNotExist:
                raise exceptions.ValidationError('You have no VO with id "{}"'.format(dep_id))

        if self.kwargs['type'] == 'service':
220 221 222 223
            # find service with id dep_id
            service = None
            for s in self.request.user.services:
                if s.id == int(dep_id):
224 225
                    service = s
                    break
226 227 228

            if service is None:
                raise exceptions.ValidationError('You are not permitted to access a service with id "{}"'.format(dep_id))
Lukas Burgey's avatar
Lukas Burgey committed
229

230
            return ServiceDeployment.get_or_create(self.request.user, service)
Lukas Burgey's avatar
Lukas Burgey committed
231 232

        raise exceptions.ValidationError('Type must be either "service" or "vo"')
Lukas Burgey's avatar
Lukas Burgey committed
233 234

    def perform_update(self, serializer):
235 236
        old_state_target = self.get_object().state_target

Lukas Burgey's avatar
Lukas Burgey committed
237
        dep = serializer.save()
Lukas Burgey's avatar
Lukas Burgey committed
238

239
        if dep.state_target != old_state_target:
Lukas Burgey's avatar
Lukas Burgey committed
240
            dep.target_changed()
lukasburgey's avatar
lukasburgey committed
241 242
        else:
            LOGGER.debug(dep.msg('Patched but the state_target was not changed'))
Lukas Burgey's avatar
Lukas Burgey committed
243

Lukas Burgey's avatar
Lukas Burgey committed
244 245

class DeploymentStateListView(generics.ListCreateAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
246
    permission_classes = PERMISSION_CLASSES
247
    serializer_class = DeploymentStateSerializer
248 249

    def get_queryset(self):
Lukas Burgey's avatar
Lukas Burgey committed
250
        return self.request.user.states.all()
251

Lukas Burgey's avatar
Lukas Burgey committed
252 253

class DeploymentStateView(generics.RetrieveUpdateAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
254
    permission_classes = PERMISSION_CLASSES
255
    serializer_class = DeploymentStateSerializer
Lukas Burgey's avatar
Lukas Burgey committed
256 257

    def get_object(self):
Lukas Burgey's avatar
Lukas Burgey committed
258
        state_id = self.kwargs['id']
Lukas Burgey's avatar
Lukas Burgey committed
259 260 261

        return get_object_or_404(
            self.request.user.states.all(),
Lukas Burgey's avatar
Lukas Burgey committed
262
            id=state_id,
Lukas Burgey's avatar
Lukas Burgey committed
263 264 265
        )

    def perform_update(self, serializer):
Lukas Burgey's avatar
Lukas Burgey committed
266

267
        old_answers = None
Lukas Burgey's avatar
Lukas Burgey committed
268
        if 'answers' in serializer.validated_data:
Lukas Burgey's avatar
Lukas Burgey committed
269
            old_answers = self.get_object().answers
270 271 272 273 274 275

        state = serializer.save()

        if old_answers is not None:
            for key in state.answers:
                if state.answers[key] != old_answers.get(key, ''):
Lukas Burgey's avatar
Lukas Burgey committed
276
                    state.answers_changed()
277
                    return
Lukas Burgey's avatar
Lukas Burgey committed
278

Lukas Burgey's avatar
Lukas Burgey committed
279

280
# the HelpView is included one level above
Lukas Burgey's avatar
Lukas Burgey committed
281
URLPATTERNS = [
lukasburgey's avatar
lukasburgey committed
282
    path('user', UserView.as_view(), name='user'),
283

lukasburgey's avatar
lukasburgey committed
284 285
    path('user-prefs', UserPreferencesView.as_view(), name='user-prefs'),

286
    path('services', ServiceListView.as_view(), name='user-service-list'),
lukasburgey's avatar
lukasburgey committed
287
    re_path(r'^service/(?P<id>[0-9]+)$', ServiceView.as_view(), name='user-service'),
Lukas Burgey's avatar
Lukas Burgey committed
288

289
    path('vos', VOListView.as_view(), name='user-vo-list'),
lukasburgey's avatar
lukasburgey committed
290
    re_path(r'^vo/(?P<id>[0-9]+)$', VOView.as_view(), name='user-vo'),
Lukas Burgey's avatar
Lukas Burgey committed
291

292 293
    path('ssh-keys', SSHPublicKeyListView.as_view(), name='user-ssh-key-list'),
    re_path(r'^ssh-key/(?P<id>[0-9]+)$', SSHPublicKeyView.as_view(), name='user-ssh-key'),
Lukas Burgey's avatar
Lukas Burgey committed
294

Lukas Burgey's avatar
Lukas Burgey committed
295
    # type is either 'vo' or 'service'
296
    re_path(r'^deployments/?(?P<type>.+)?$', DeploymentListView.as_view(), name='user-dep-list'),
lukasburgey's avatar
lukasburgey committed
297
    re_path(r'^deployment/(?P<type>.+)/(?P<id>[0-9]+)$', DeploymentView.as_view(), name='user-dep'),
298

299
    path('states', DeploymentStateListView.as_view(), name='user-dep-state-list'),
lukasburgey's avatar
lukasburgey committed
300
    re_path(r'^state/(?P<id>[0-9]+)$', DeploymentStateView.as_view(), name='user-dep-state'),
301

302
    # this catch all must be last in the list!
303
    path('', HelpView.as_view(), name='user-help'),
304
    re_path(r'.*$', HelpView.as_view()),
Lukas Burgey's avatar
Lukas Burgey committed
305
]