rest.py 8.95 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
8
9
from django.http import HttpResponse
from rest_framework import generics, exceptions, views
from rest_framework.permissions import AllowAny
Lukas Burgey's avatar
Lukas Burgey committed
10

11
12
13
14
15
16
17
18
from feudal.backend.models import Service
from feudal.backend.models.serializers import (
    UserStateSerializer, ServiceSerializer, SSHPublicKeySerializer,
    DeploymentSerializer, DeploymentStateSerializer
)
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
19
from feudal.backend.permissions import UserOnly
Lukas Burgey's avatar
Lukas Burgey committed
20
21

LOGGER = logging.getLogger(__name__)
22
23
24
25
26
27
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
HELP_TEXT = """
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
60
61
PERMISSION_CLASSES = (UserOnly,)

62
63
64
65

class HelpView(views.APIView):
    permission_classes = (AllowAny,)

66
67
    def get(self, request):
        return HttpResponse(HELP_TEXT)
Lukas Burgey's avatar
Lukas Burgey committed
68
69


Lukas Burgey's avatar
Lukas Burgey committed
70
class UserDeletionView(generics.RetrieveDestroyAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
71
    permission_classes = PERMISSION_CLASSES
72
    serializer_class = UserStateSerializer
73
74

    def get_object(self):
Lukas Burgey's avatar
Lukas Burgey committed
75
76
77
78
79
80
81
82
83
        return self.request.user

    def permorm_destroy(self, instance):
        LOGGER.info('Deleting user on user request')
        logout(self.request)
        instance.delete()


class ServiceListView(generics.ListAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
84
    permission_classes = PERMISSION_CLASSES
85
    serializer_class = ServiceSerializer
86

Lukas Burgey's avatar
Lukas Burgey committed
87
    def get_queryset(self):
88
        return Service.objects.filter(vos__user=self.request.user)
Lukas Burgey's avatar
Lukas Burgey committed
89
90
91


class ServiceView(generics.RetrieveAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
92
    permission_classes = PERMISSION_CLASSES
93
    serializer_class = ServiceSerializer
Lukas Burgey's avatar
Lukas Burgey committed
94
95

    def get_object(self):
96
        return get_object_or_404(
97
            Service.objects.filter(vos__user=self.request.user),
Lukas Burgey's avatar
Lukas Burgey committed
98
            id=self.kwargs['id'],
99
100
        )

Lukas Burgey's avatar
Lukas Burgey committed
101
102

class VOListView(generics.ListAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
103
    permission_classes = PERMISSION_CLASSES
Lukas Burgey's avatar
Lukas Burgey committed
104
105
106
107
108
109
110
    serializer_class = VOSerializer

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


class VOView(generics.RetrieveAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
111
    permission_classes = PERMISSION_CLASSES
Lukas Burgey's avatar
Lukas Burgey committed
112
113
114
115
116
117
118
    serializer_class = VOSerializer

    def get_object(self):
        return get_object_or_404(
            self.request.user.vos.all(),
            id=self.kwargs['id'],
        )
119
120
121


class SSHPublicKeyListView(generics.ListCreateAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
122
    permission_classes = PERMISSION_CLASSES
123
    serializer_class = SSHPublicKeySerializer
124
125
126
127
128
129

    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
130
        self.request.user.add_key(key)
131
132


Lukas Burgey's avatar
Lukas Burgey committed
133
class SSHPublicKeyView(generics.RetrieveDestroyAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
134
    permission_classes = PERMISSION_CLASSES
135
    serializer_class = SSHPublicKeySerializer
136

Lukas Burgey's avatar
Lukas Burgey committed
137
138
139
140
141
    def get_object(self):
        return get_object_or_404(
            self.request.user.ssh_keys.all(),
            id=self.kwargs['id'],
        )
142

Lukas Burgey's avatar
Lukas Burgey committed
143
144
    def perform_destroy(self, instance):
        self.request.user.remove_key(instance)
145

Lukas Burgey's avatar
Lukas Burgey committed
146

Lukas Burgey's avatar
Lukas Burgey committed
147
class DeploymentListView(generics.ListAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
148
    permission_classes = PERMISSION_CLASSES
149
    serializer_class = DeploymentSerializer
Lukas Burgey's avatar
Lukas Burgey committed
150
151

    def get_queryset(self):
Lukas Burgey's avatar
Lukas Burgey committed
152
        if self.kwargs.get('type', '') == 'service':
153
            return self.request.user.deployments.instance_of(ServiceDeployment)
Lukas Burgey's avatar
Lukas Burgey committed
154
        if self.kwargs.get('type', '') == 'vo':
155
            return self.request.user.deployments.instance_of(VODeployment)
Lukas Burgey's avatar
Lukas Burgey committed
156
157

        return self.request.user.deployments.all()
Lukas Burgey's avatar
Lukas Burgey committed
158
159


Lukas Burgey's avatar
Lukas Burgey committed
160
class DeploymentView(generics.RetrieveUpdateAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
161
    permission_classes = PERMISSION_CLASSES
162
    serializer_class = DeploymentSerializer
Lukas Burgey's avatar
Lukas Burgey committed
163
164

    def get_serializer_context(self):
Lukas Burgey's avatar
Lukas Burgey committed
165
        if self.kwargs['type'] == 'vo':
Lukas Burgey's avatar
Lukas Burgey committed
166
            self.request.data['resourcetype'] = 'VODeployment'
Lukas Burgey's avatar
Lukas Burgey committed
167
168
        elif self.kwargs['type'] == 'service':
            self.request.data['resourcetype'] = 'ServiceDeployment'
Lukas Burgey's avatar
Lukas Burgey committed
169
170
171
172
173
174

        return {
            'request': self.request,
        }

    def get_object(self):
Lukas Burgey's avatar
Lukas Burgey committed
175
176
177
178
179
        dep_id = self.kwargs['id']

        if self.kwargs['type'] == 'vo':
            try:
                vo = self.request.user.vos.get(id=dep_id)
180
                dep, created = VODeployment.objects.get_or_create(
Lukas Burgey's avatar
Lukas Burgey committed
181
182
183
184
185
186
187
188
189
190
191
192
                    user=self.request.user,
                    vo=vo,
                    defaults={},
                )
                if created:
                    LOGGER.debug('Created new VO deployment')
                return dep

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

        if self.kwargs['type'] == 'service':
193
194
195
196
197
198
199
200
201
202
203
            # find service with id dep_id
            service = None
            for s in self.request.user.services:
                if s.id == int(dep_id):
                    if service is None:
                        service = s
                    else:
                        raise Exception('user has multiple services with identical id')

            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
204

205
206
207
208
209
210
211
            dep, created = ServiceDeployment.objects.get_or_create(
                user=self.request.user,
                service=service,
                defaults={},
            )
            if created:
                LOGGER.debug('Created new ServiceDeployment')
Lukas Burgey's avatar
Lukas Burgey committed
212

213
            return dep
Lukas Burgey's avatar
Lukas Burgey committed
214
215

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

    def perform_update(self, serializer):
        dep = serializer.save()
Lukas Burgey's avatar
Lukas Burgey committed
219
220
221
222

        if 'state_target' in self.request.data:
            LOGGER.debug('Patch changed state_target')
            dep.target_changed()
Lukas Burgey's avatar
Lukas Burgey committed
223

Lukas Burgey's avatar
Lukas Burgey committed
224
225

class DeploymentStateListView(generics.ListCreateAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
226
    permission_classes = PERMISSION_CLASSES
227
    serializer_class = DeploymentStateSerializer
228
229

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

Lukas Burgey's avatar
Lukas Burgey committed
232
233

class DeploymentStateView(generics.RetrieveUpdateAPIView):
Lukas Burgey's avatar
Lukas Burgey committed
234
    permission_classes = PERMISSION_CLASSES
235
    serializer_class = DeploymentStateSerializer
Lukas Burgey's avatar
Lukas Burgey committed
236
237

    def get_object(self):
Lukas Burgey's avatar
Lukas Burgey committed
238
        state_id = self.kwargs['id']
Lukas Burgey's avatar
Lukas Burgey committed
239
240
241

        return get_object_or_404(
            self.request.user.states.all(),
Lukas Burgey's avatar
Lukas Burgey committed
242
            id=state_id,
Lukas Burgey's avatar
Lukas Burgey committed
243
244
245
        )

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

Lukas Burgey's avatar
Lukas Burgey committed
247
248
        state = serializer.save()

Lukas Burgey's avatar
Lukas Burgey committed
249
        if 'answers' in serializer.validated_data:
Lukas Burgey's avatar
Lukas Burgey committed
250
251
252
253
254
255
256
257
258
            # previous answers
            old_answers = self.get_object().answers
            if len(old_answers.keys()) > 0:
                for key in old_answers:
                    if old_answers[key] != state.answers.get(key, ''):
                        LOGGER.debug('Patch changed answers')
                        state.answers_changed()
                        break
                else:
Lukas Burgey's avatar
Lukas Burgey committed
259
260
                    LOGGER.debug('Patch changed answers')
                    state.answers_changed()
Lukas Burgey's avatar
Lukas Burgey committed
261

Lukas Burgey's avatar
Lukas Burgey committed
262

263
# the HelpView is included one level above
Lukas Burgey's avatar
Lukas Burgey committed
264
URLPATTERNS = [
265
    path('user', UserDeletionView.as_view()),
266

267
268
    path('services', ServiceListView.as_view()),
    re_path(r'^service/(?P<id>[0-9]+)$', ServiceView.as_view()),
Lukas Burgey's avatar
Lukas Burgey committed
269

270
271
    path('vos', VOListView.as_view()),
    re_path(r'^vo/(?P<id>[0-9]+)$', VOView.as_view()),
Lukas Burgey's avatar
Lukas Burgey committed
272

273
274
    path('ssh-keys', SSHPublicKeyListView.as_view()),
    re_path(r'^ssh-key/(?P<id>[0-9]+)$', SSHPublicKeyView.as_view()),
Lukas Burgey's avatar
Lukas Burgey committed
275

Lukas Burgey's avatar
Lukas Burgey committed
276
    # type is either 'vo' or 'service'
277
278
279
280
281
    re_path(r'^deployments/?(?P<type>.+)?$', DeploymentListView.as_view()),
    re_path(r'^deployment/(?P<type>.+)/(?P<id>[0-9]+)$', DeploymentView.as_view()),

    path('states', DeploymentStateListView.as_view()),
    re_path(r'^state/(?P<id>[0-9]+)$', DeploymentStateView.as_view()),
282
283

    re_path(r'.*', HelpView.as_view()),
Lukas Burgey's avatar
Lukas Burgey committed
284
]