diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py index 607273e7c9f438c01d81c5a90d077e7e79b3bd95..85f63e4b843d0308f933c5af5c90e34281d3fcee 100644 --- a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py +++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.9 on 2020-08-19 13:24 +# Generated by Django 3.0.8 on 2020-09-02 16:52 from django.conf import settings import django.contrib.postgres.fields @@ -288,6 +288,7 @@ class Migration(migrations.Migration): ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)), ('description', models.CharField(help_text='A longer description of this object.', max_length=255)), ('capacity', models.BigIntegerField(help_text='Capacity in bytes')), + ('directory', models.CharField(help_text='Root directory under which we are allowed to write our data.', max_length=1024)), ], options={ 'abstract': False, @@ -342,6 +343,7 @@ class Migration(migrations.Migration): ('private_data', models.BooleanField(default=True, help_text='True if data of this project is sensitive. Sensitive data is not made public.')), ('expert', models.BooleanField(default=False, help_text='Expert projects put more responsibility on the PI.')), ('filler', models.BooleanField(default=False, help_text='Use this project to fill up idle telescope time.')), + ('archive_subdirectory', models.CharField(help_text='Subdirectory in which this project will store its data in the LTA. The full directory is constructed by prefixing with archive_location→directory.', max_length=1024)), ], options={ 'abstract': False, @@ -1044,6 +1046,11 @@ class Migration(migrations.Migration): name='resource_type', field=models.ForeignKey(help_text='Resource type.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.ResourceType'), ), + migrations.AddField( + model_name='project', + name='archive_location', + field=models.ForeignKey(help_text='Ingest data to this LTA cluster only (NULLable). NULL means: no preference.', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Filesystem'), + ), migrations.AddField( model_name='project', name='cycles', diff --git a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py index 4ac8634bab3ccf3a644f423d5fca7330ef387a2f..d77d1a43ea0086ec9a3d10a646187d772ea265b8 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py +++ b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py @@ -308,6 +308,13 @@ class DataproductTransform(BasicCommon): class Filesystem(NamedCommon): capacity = BigIntegerField(help_text='Capacity in bytes') cluster = ForeignKey('Cluster', on_delete=PROTECT, help_text='Cluster hosting this filesystem.') + directory = CharField(max_length=1024, help_text='Root directory under which we are allowed to write our data.') + + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + if self.directory and not self.directory.endswith('/'): + raise ValueError('directory value must end with a trailing slash!') # todo: ...and needs to start with slash? + + super().save(force_insert, force_update, using, update_fields) class Cluster(NamedCommon): diff --git a/SAS/TMSS/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/src/tmss/tmssapp/models/specification.py index f292c06a9e03d7a0e9a3d9e44626715c30daa714..d1d4c025536364b66a070d0e9ee9ec9c11ad9f18 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/models/specification.py @@ -323,6 +323,16 @@ class Project(NamedCommonPK): filler = BooleanField(default=False, help_text='Use this project to fill up idle telescope time.') project_category = ForeignKey('ProjectCategory', null=True, on_delete=PROTECT, help_text='Project category.') period_category = ForeignKey('PeriodCategory', null=True, on_delete=PROTECT, help_text='Period category.') + archive_location = ForeignKey('Filesystem', null=True, on_delete=PROTECT, help_text='Ingest data to this LTA cluster only (NULLable). NULL means: no preference.') + archive_subdirectory = CharField(max_length=1024, help_text='Subdirectory in which this project will store its data in the LTA. The full directory is constructed by prefixing with archive_location→directory.') + + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + if self.archive_subdirectory and not self.archive_subdirectory.endswith('/'): + raise ValueError('directory value must end with a trailing slash!') + if self.archive_subdirectory and self.archive_subdirectory.startswith('/'): + raise ValueError('directory value must be a relative path (and not start with a slash)!') + + super().save(force_insert, force_update, using, update_fields) # JK, 29/07/20 - after discussion with Sander, it turns out that the ticket TMSS-277 was a misunderstanding. # 'default' does not refer to 'default values' that are supposed to be filled in by the backend.