You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2020/09/22 21:07:34 UTC

[airavata-django-portal] 01/05: AIRAVATA-3346 Use remote API to launch experiment and get relative data directory

This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch AIRAVATA-3346-implement-remote-fs-abstraction-of-user-storage
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 88a3e9b86055d01df484979d190c2cded3c0dff1
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Wed Sep 16 14:22:27 2020 -0400

    AIRAVATA-3346 Use remote API to launch experiment and get relative data directory
---
 django_airavata/apps/api/serializers.py | 61 ++++++++++++++++++++++++++-------
 django_airavata/apps/api/views.py       | 45 ++++++++++++++++++------
 django_airavata/apps/auth/views.py      |  4 +++
 3 files changed, 86 insertions(+), 24 deletions(-)

diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index 5622c65..0ecd926 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -64,6 +64,7 @@ from airavata.model.workspace.ttypes import (
 from airavata_django_portal_sdk import user_storage
 
 from . import models, thrift_utils
+import requests
 
 log = logging.getLogger(__name__)
 
@@ -457,11 +458,26 @@ class ExperimentSerializer(
             ResourcePermissionType.WRITE)
 
     def get_relativeExperimentDataDir(self, experiment):
+
         if (experiment.userConfigurationData and
                 experiment.userConfigurationData.experimentDataDir):
             request = self.context['request']
             data_dir = experiment.userConfigurationData.experimentDataDir
-            if user_storage.dir_exists(request, data_dir):
+            if getattr(
+                settings,
+                'GATEWAY_DATA_STORE_REMOTE_API',
+                    None) is not None:
+                # Load the relativeExperimentDataDir from the remote Django
+                # portal instance
+                headers = {
+                    'Authorization': f'Bearer {request.authz_token.accessToken}'}
+                r = requests.get(
+                    f'{settings.GATEWAY_DATA_STORE_REMOTE_API}/experiments/{quote(experiment.experimentId)}/',
+                    headers=headers,
+                )
+                r.raise_for_status()
+                return r.json()['relativeExperimentDataDir']
+            elif user_storage.dir_exists(request, data_dir):
                 return user_storage.get_rel_path(request, data_dir)
             else:
                 return None
@@ -673,7 +689,8 @@ class SharedEntitySerializer(serializers.Serializer):
         raise Exception("Not implemented")
 
     def update(self, instance, validated_data):
-        # Compute lists of ids to grant/revoke READ/WRITE/MANAGE_SHARING permission
+        # Compute lists of ids to grant/revoke READ/WRITE/MANAGE_SHARING
+        # permission
         existing_user_permissions = {
             user['user'].airavataInternalUserId: user['permissionType']
             for user in instance['userPermissions']}
@@ -682,10 +699,15 @@ class SharedEntitySerializer(serializers.Serializer):
             user['permissionType']
                 for user in validated_data['userPermissions']}
 
-        (user_grant_read_permission, user_grant_write_permission, user_grant_manage_sharing_permission,
-         user_revoke_read_permission, user_revoke_write_permission, user_revoke_manage_sharing_permission) = \
-            self._compute_all_revokes_and_grants(existing_user_permissions,
-                                                 new_user_permissions)
+        (
+            user_grant_read_permission,
+            user_grant_write_permission,
+            user_grant_manage_sharing_permission,
+            user_revoke_read_permission,
+            user_revoke_write_permission,
+            user_revoke_manage_sharing_permission) = self._compute_all_revokes_and_grants(
+            existing_user_permissions,
+            new_user_permissions)
 
         existing_group_permissions = {
             group['group'].id: group['permissionType']
@@ -694,10 +716,15 @@ class SharedEntitySerializer(serializers.Serializer):
             group['group']['id']: group['permissionType']
             for group in validated_data['groupPermissions']}
 
-        (group_grant_read_permission, group_grant_write_permission, group_grant_manage_sharing_permission,
-         group_revoke_read_permission, group_revoke_write_permission, group_revoke_manage_sharing_permission) = \
-            self._compute_all_revokes_and_grants(existing_group_permissions,
-                                                 new_group_permissions)
+        (
+            group_grant_read_permission,
+            group_grant_write_permission,
+            group_grant_manage_sharing_permission,
+            group_revoke_read_permission,
+            group_revoke_write_permission,
+            group_revoke_manage_sharing_permission) = self._compute_all_revokes_and_grants(
+            existing_group_permissions,
+            new_group_permissions)
 
         instance['_user_grant_read_permission'] = user_grant_read_permission
         instance['_user_grant_write_permission'] = user_grant_write_permission
@@ -749,15 +776,23 @@ class SharedEntitySerializer(serializers.Serializer):
                 grant_write_permission.append(id)
             if ResourcePermissionType.MANAGE_SHARING in grants:
                 grant_manage_sharing_permission.append(id)
-        return (grant_read_permission, grant_write_permission, grant_manage_sharing_permission,
-                revoke_read_permission, revoke_write_permission, revoke_manage_sharing_permission)
+        return (
+            grant_read_permission,
+            grant_write_permission,
+            grant_manage_sharing_permission,
+            revoke_read_permission,
+            revoke_write_permission,
+            revoke_manage_sharing_permission)
 
     def _compute_revokes_and_grants(self, current_permission=None,
                                     new_permission=None):
         read_permissions = set((ResourcePermissionType.READ,))
         write_permissions = set((ResourcePermissionType.READ,
                                  ResourcePermissionType.WRITE))
-        manage_share_permissions = set((ResourcePermissionType.READ, ResourcePermissionType.WRITE, ResourcePermissionType.MANAGE_SHARING))
+        manage_share_permissions = set(
+            (ResourcePermissionType.READ,
+             ResourcePermissionType.WRITE,
+             ResourcePermissionType.MANAGE_SHARING))
         current_permissions_set = set()
         new_permissions_set = set()
         if current_permission == ResourcePermissionType.READ:
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index 13224ff..90273bb 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -3,18 +3,25 @@ import json
 import logging
 import os
 from datetime import datetime, timedelta
+from urllib.parse import quote
 
+import requests
 from django.conf import settings
 from django.contrib.auth.decorators import login_required
 from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
 from django.http import FileResponse, Http404, HttpResponse, JsonResponse
 from django.urls import reverse
 from rest_framework import mixins
-from rest_framework.decorators import action, api_view, detail_route, list_route
+from rest_framework.decorators import (
+    action,
+    api_view,
+    detail_route,
+    list_route
+)
 from rest_framework.exceptions import ParseError
 from rest_framework.renderers import JSONRenderer
 from rest_framework.response import Response
-from rest_framework.status import HTTP_404_NOT_FOUND, HTTP_400_BAD_REQUEST
+from rest_framework.status import HTTP_400_BAD_REQUEST, HTTP_404_NOT_FOUND
 from rest_framework.views import APIView
 
 from airavata.model.appcatalog.computeresource.ttypes import (
@@ -278,15 +285,31 @@ class ExperimentViewSet(APIBackedViewSet):
     @detail_route(methods=['post'])
     def launch(self, request, experiment_id=None):
         try:
-            experiment = request.airavata_client.getExperiment(
-                self.authz_token, experiment_id)
-            self._set_storage_id_and_data_dir(experiment)
-            self._move_tmp_input_file_uploads_to_data_dir(experiment)
-            request.airavata_client.updateExperiment(
-                self.authz_token, experiment_id, experiment)
-            request.airavata_client.launchExperiment(
-                request.authz_token, experiment_id, self.gateway_id)
-            return Response({'success': True})
+            if getattr(
+                settings,
+                'GATEWAY_DATA_STORE_REMOTE_API',
+                    None) is not None:
+                # Proxy the launch/ request to the remote Django portal
+                # instance since it must setup the experiment data directory
+                # which is only on the remote Django portal instance
+                headers = {
+                    'Authorization': f'Bearer {request.authz_token.accessToken}'}
+                r = requests.post(
+                    f'{settings.GATEWAY_DATA_STORE_REMOTE_API}/experiments/{quote(experiment_id)}/launch/',
+                    headers=headers,
+                )
+                r.raise_for_status()
+                return Response(r.json())
+            else:
+                experiment = request.airavata_client.getExperiment(
+                    self.authz_token, experiment_id)
+                self._set_storage_id_and_data_dir(experiment)
+                self._move_tmp_input_file_uploads_to_data_dir(experiment)
+                request.airavata_client.updateExperiment(
+                    self.authz_token, experiment_id, experiment)
+                request.airavata_client.launchExperiment(
+                    request.authz_token, experiment_id, self.gateway_id)
+                return Response({'success': True})
         except Exception as e:
             return Response({'success': False, 'errorMessage': e.message})
 
diff --git a/django_airavata/apps/auth/views.py b/django_airavata/apps/auth/views.py
index 568d6a9..d87b031 100644
--- a/django_airavata/apps/auth/views.py
+++ b/django_airavata/apps/auth/views.py
@@ -93,6 +93,10 @@ def handle_login(request):
     logger.debug("authenticated user: {}".format(user))
     try:
         if user is not None:
+            # Middleware will add authz_token attr to request, but since user
+            # just authenticated, authz_token won't be added yet. Login signals
+            # need the authz_token so adding it to the request now.
+            request.authz_token = utils.get_authz_token(request, user=user)
             login(request, user)
             if login_desktop:
                 return _create_login_desktop_success_response(request)