Commit 1bd15209 authored by Stefano Alberto Russo's avatar Stefano Alberto Russo
Browse files

Added support for read-only storages.

parent 588db653
Loading
Loading
Loading
Loading
+28 −6
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ from rest_framework import status, serializers, viewsets
from rest_framework.views import APIView
from .utils import format_exception, send_email, os_shell, now_t, get_ssh_access_mode_credentials, get_or_create_container_from_repository, booleanize
from .models import Profile, Task, TaskStatuses, Computing, Storage, KeyPair
from .exceptions import ConsistencyException
from .exceptions import PermissionDenied
import json

# Setup logging
@@ -153,6 +153,10 @@ class PrivatePOSTAPI(APIView):
            # Call API logic
            return self._post(request)
        except Exception as e:
            # TODO: refactor me
            if isinstance(e, PermissionDenied):
                return error400(format(e))
            else:
                logger.error(format_exception(e))
                return error500('Got error in processing request: {}'.format(e))

@@ -174,11 +178,14 @@ class PrivateGETAPI(APIView):
            # Call API logic
            return self._get(request)
        except Exception as e:
            # TODO: refactor me
            if isinstance(e, PermissionDenied):
                return error400(format(e))
            else:
                logger.error(format_exception(e))
                return error500('Got error in processing request: {}'.format(e))



#==============================
#  User & profile APIs
#==============================
@@ -689,6 +696,9 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):

    def delete(self, path, user, storage):

        if storage.read_only:
            raise PermissionDenied('This storage is read-only')

        if storage.type == 'generic_posix':

            shell_path = self.sanitize_and_prepare_shell_path(path, user, storage)
@@ -707,6 +717,9 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):

    def mkdir(self, path, user, storage, force=False):

        if storage.read_only:
            raise PermissionDenied('This storage is read-only')

        path = self.sanitize_and_prepare_shell_path(path, user, storage)

        if storage.type == 'generic_posix':
@@ -751,6 +764,9 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):

    def rename(self, old, new, user, storage):

        if storage.read_only:
            raise PermissionDenied('This storage is read-only')

        if storage.type == 'generic_posix':

            old = self.sanitize_and_prepare_shell_path(old, user, storage)
@@ -772,6 +788,9 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):

    def copy(self, source, target, user, storage):

        if storage.read_only:
            raise PermissionDenied('This storage is read-only')

        if storage.type == 'generic_posix':

            source = self.sanitize_and_prepare_shell_path(source, user, storage)
@@ -805,6 +824,9 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):

    def scp_to(self, source, target, user, storage, mode='get'):

        if storage.read_only:
            raise PermissionDenied('This storage is read-only')

        source = self.sanitize_shell_path(source) # This is a folder on Rosetta (/tmp)
        target = self.sanitize_and_prepare_shell_path(target, user, storage)

+16 −4
Original line number Diff line number Diff line
@@ -147,11 +147,17 @@ class InternalStandaloneComputingManager(StandaloneComputingManager):
                if '$USER' in expanded_bind_path:
                    expanded_bind_path = expanded_bind_path.replace('$USER', task.user.username)

                # Read only?
                if storage.read_only:
                    mode_string = ':ro'
                else:
                    mode_string = ''

                # Add the bind
                if not binds:
                    binds = '-v{}:{}'.format(expanded_base_path, expanded_bind_path)
                    binds = '-v{}:{}{}'.format(expanded_base_path, expanded_bind_path, mode_string)
                else:
                    binds += ' -v{}:{}'.format(expanded_base_path, expanded_bind_path)
                    binds += ' -v{}:{}{}'.format(expanded_base_path, expanded_bind_path, mode_string)

        # Host name, image entry command
        run_command += ' {} -h task-{} --name task-{} -d -t {}/{}:{}'.format(binds, task.short_uuid, task.short_uuid, task.container.registry, task.container.image_name, task.container.image_tag)
@@ -348,11 +354,17 @@ class SSHStandaloneComputingManager(StandaloneComputingManager, SSHComputingMana
                    if '$USER' in expanded_bind_path:
                        expanded_bind_path = expanded_bind_path.replace('$USER', task.user.username)

                    # Read only?
                    if storage.read_only:
                        mode_string = ':ro'
                    else:
                        mode_string = ''

                    # Add the bind
                    if not binds:
                        binds = '-v{}:{}'.format(expanded_base_path, expanded_bind_path)
                        binds = '-v{}:{}{}'.format(expanded_base_path, expanded_bind_path, mode_string)
                    else:
                        binds += ' -v{}:{}'.format(expanded_base_path, expanded_bind_path)
                        binds += ' -v{}:{}{}'.format(expanded_base_path, expanded_bind_path, mode_string)

            # TODO: remove this hardcoding
            prefix = 'sudo' if (computing_host == 'slurmclusterworker' and container_engine=='docker') else ''
+3 −0
Original line number Diff line number Diff line
@@ -4,3 +4,6 @@ class ErrorMessage(Exception):

class ConsistencyException(Exception):
    pass

class PermissionDenied(Exception):
    pass
 No newline at end of file
+18 −0
Original line number Diff line number Diff line
# Generated by Django 2.2.1 on 2025-03-05 09:46

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('core_app', '0036_container_disable_http_basicauth_embedding'),
    ]

    operations = [
        migrations.AddField(
            model_name='storage',
            name='read_only',
            field=models.BooleanField(default=False, verbose_name='Read only? (if supported)'),
        ),
    ]
+3 −0
Original line number Diff line number Diff line
@@ -387,6 +387,9 @@ class Storage(models.Model):
    base_path = models.CharField('Base path', max_length=4096, blank=False, null=False)
    bind_path = models.CharField('Bind path', max_length=4096, blank=True, null=True)

    # Read only?
    read_only = models.BooleanField('Read only? (if supported)', default=False)

    # Link with a computing resource
    computing = models.ForeignKey(Computing, related_name='storages', on_delete=models.CASCADE, blank=True, null=True) # Make optional?
    access_through_computing = models.BooleanField('Access through linked computing resource?', default=False)