Skip to content
Snippets Groups Projects
Commit 1bd15209 authored by Stefano Alberto Russo's avatar Stefano Alberto Russo
Browse files

Added support for read-only storages.

parent 588db653
No related branches found
No related tags found
No related merge requests found
......@@ -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,8 +153,12 @@ class PrivatePOSTAPI(APIView):
# Call API logic
return self._post(request)
except Exception as e:
logger.error(format_exception(e))
return error500('Got error in processing request: {}'.format(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))
class PrivateGETAPI(APIView):
'''Base private GET API class'''
......@@ -174,9 +178,12 @@ class PrivateGETAPI(APIView):
# Call API logic
return self._get(request)
except Exception as e:
logger.error(format_exception(e))
return error500('Got error in processing request: {}'.format(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))
#==============================
......@@ -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)
......
......@@ -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 ''
......
......@@ -4,3 +4,6 @@ class ErrorMessage(Exception):
class ConsistencyException(Exception):
pass
class PermissionDenied(Exception):
pass
\ No newline at end of file
# 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)'),
),
]
......@@ -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)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment