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
]