-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
User-based ObjectStore #4840
User-based ObjectStore #4840
Changes from 19 commits
f85fd4d
0be18ea
8141f22
55d3839
3155b06
7d6a390
608c4ab
d89b9de
e25e994
714c5f8
8474295
bd046c8
8e1105c
999cc0a
54cfbb0
93f71cc
f760280
360b957
d524e4c
4d1395f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -558,7 +558,14 @@ def __verify_job_ready(self, job, job_wrapper): | |
|
||
if state == JOB_READY: | ||
state = self.__check_user_jobs(job, job_wrapper) | ||
if state == JOB_READY and self.app.config.enable_quotas: | ||
# If user has plugged a media, then they might have enough quota | ||
# on their media; hence, we should not raise the "over quota" flag | ||
# checking the default storage only. If their usage exceeds their | ||
# total quota on all their media, ObjectStore raises appropriate | ||
# exception(s). | ||
if state == JOB_READY and self.app.config.enable_quotas and \ | ||
(job.user is not None and | ||
(job.user.active_storage_media is None or not job.user.has_active_storage_media())): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be redone on top of #10221 - which I think abstracts out the quota checking into nice optimizable functions. I think rather than checking if has_active_storage_media we should build on the abstractions in that PR to just ask if the configured objectstore we're talking to has quota left and then we can disable quota on objectstores that use storage media. The ability to disable quota on an objectstore is included in that PR. |
||
quota = self.app.quota_agent.get_quota(job.user) | ||
if quota is not None: | ||
try: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
""" | ||
Manager and Serializer for storage media. | ||
""" | ||
|
||
import logging | ||
|
||
from galaxy import exceptions | ||
from galaxy import model | ||
from galaxy.managers import ( | ||
base, | ||
datasets, | ||
deletable, | ||
hdas, | ||
sharable | ||
) | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
class StorageMediaManager(base.ModelManager, deletable.PurgableManagerMixin): | ||
|
||
model_class = model.StorageMedia | ||
foreign_key_name = "storage_media" | ||
|
||
def __init__(self, app, *args, **kwargs): | ||
super(StorageMediaManager, self).__init__(app, *args, **kwargs) | ||
self.hda_manager = hdas.HDAManager(app) | ||
self.dataset_manager = datasets.DatasetManager(app) | ||
|
||
def delete(self, storage_media, **kwargs): | ||
""" | ||
Deletes the given storage media by taking the following steps: | ||
(1) marks the storage media `deleted` in the database (i.e., setting | ||
the `deleted` attribute to True); | ||
(2) marks `deleted` all the datasets persisted on the storage media; | ||
(3) marks `deleted` all the StorageMedia-Dataset associations. | ||
:param storage_media: The storage media to be deleted. | ||
:type storage_media: galaxy.model.StorageMedia | ||
:return: returns the deleted storage media. | ||
""" | ||
super(StorageMediaManager, self).delete(storage_media, kwargs) | ||
for assoc in storage_media.data_association: | ||
self.hda_manager.delete(assoc, kwargs) | ||
self.dataset_manager.delete(assoc.dataset, kwargs) | ||
super(StorageMediaManager, self).delete(assoc, kwargs) | ||
self.session().flush() | ||
return storage_media | ||
|
||
def undelete(self, storage_media, **kwargs): | ||
""" | ||
Un-deletes the given storage media by taking the following steps: | ||
(1) marks the storage media `un-deleted` in the database (i.e., setting | ||
the `deleted` attribute to False); | ||
(2) marks `un-deleted` all the datasets persisted on the storage media; | ||
(3) marks `un-deleted` all the StorageMedia-Dataset associations. | ||
:param storage_media: The storage media to be deleted. | ||
:type storage_media: galaxy.model.StorageMedia | ||
:return: returns the deleted storage media. | ||
""" | ||
super(StorageMediaManager, self).undelete(storage_media, kwargs) | ||
for assoc in storage_media.data_association: | ||
self.hda_manager.delete(assoc, kwargs) | ||
self.dataset_manager.delete(assoc.dataset, kwargs) | ||
super(StorageMediaManager, self).undelete(assoc, kwargs) | ||
self.session().flush() | ||
return storage_media | ||
|
||
def purge(self, storage_media, **kwargs): | ||
""" | ||
Purges a storage media by taking the following steps: | ||
(1) marks the storage media `purged` in the database; | ||
(2) deletes all the datasets persisted on the storage media; | ||
(3) marks all the HDAs associated with the deleted datasets as purged. | ||
This operation does NOT `delete` the storage media physically | ||
(e.g., it does not delete a S3 bucket), because the storage media | ||
(e.g., a S3 bucket) may contain data other than those loaded | ||
or mounted on Galaxy which deleting the media (e.g., deleting | ||
a S3 bucket) will result in unexpected file deletes. | ||
:param storage_media: The media to be purged. | ||
:type: storage_media: galaxy.model.StorageMedia | ||
:return: returns the purged storage media. | ||
""" | ||
if not storage_media.is_purgeable(): | ||
raise exceptions.ConfigDoesNotAllowException( | ||
"The storage media (ID: `{}`; category: `{}`) is not purgeable; because {}".format( | ||
storage_media.id, storage_media.category, | ||
"it`s purgeable attribute is set to `False`." if storage_media.purgeable is False | ||
else "it contains at least one dataset which is not purgeable.")) | ||
for i, assoc in enumerate(storage_media.data_association): | ||
for hda in assoc.dataset.history_associations: | ||
self.hda_manager.purge(hda) | ||
self.dataset_manager.purge(assoc.dataset, storage_media=storage_media) | ||
storage_media.data_association[i].purged = True | ||
storage_media.purged = True | ||
self.session().flush() | ||
return storage_media | ||
|
||
|
||
class StorageMediaSerializer(base.ModelSerializer, deletable.PurgableSerializerMixin): | ||
""" | ||
Interface/service object for serializing storage media into dictionaries. | ||
""" | ||
model_manager_class = StorageMediaManager | ||
|
||
def __init__(self, app, **kwargs): | ||
super(StorageMediaSerializer, self).__init__(app, **kwargs) | ||
self.storage_media_manager = self.manager | ||
|
||
self.default_view = "summary" | ||
self.add_view("summary", [ | ||
"id", | ||
"model_class", | ||
"user_id", | ||
"usage", | ||
"category", | ||
"path" | ||
]) | ||
self.add_view("detailed", [ | ||
"id", | ||
"model_class", | ||
"user_id", | ||
"create_time", | ||
"update_time", | ||
"usage", | ||
"category", | ||
"path", | ||
"deleted", | ||
"purged", | ||
"purgeable" | ||
]) | ||
|
||
def add_serializers(self): | ||
super(StorageMediaSerializer, self).add_serializers() | ||
deletable.PurgableSerializerMixin.add_serializers(self) | ||
|
||
# Arguments of the following lambda functions: | ||
# i : an instance of galaxy.model.StorageMedia. | ||
# k : serialized dictionary key (e.g., "model_class", "category", and "path"). | ||
# **c: a dictionary containing "trans" and "user" objects. | ||
self.serializers.update({ | ||
"id" : lambda i, k, **c: self.app.security.encode_id(i.id), | ||
"model_class": lambda *a, **c: "StorageMedia", | ||
"user_id" : lambda i, k, **c: self.app.security.encode_id(i.user_id), | ||
"usage" : lambda i, k, **c: str(i.usage), | ||
"category" : lambda i, k, **c: i.category, | ||
"path" : lambda i, k, **c: i.path, | ||
"deleted" : lambda i, k, **c: i.deleted, | ||
"purged" : lambda i, k, **c: i.purged, | ||
"purgeable" : lambda i, k, **c: i.purgeable | ||
}) | ||
|
||
|
||
class StorageMediaDeserializer(sharable.SharableModelDeserializer, deletable.PurgableDeserializerMixin): | ||
|
||
model_manager_class = StorageMediaManager | ||
|
||
def add_deserializers(self): | ||
super(StorageMediaDeserializer, self).add_deserializers() | ||
self.deserializers.update({ | ||
"path": self.default_deserializer | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've spent months of my life trying to optimize this process of initializing the output datasets. Can we have some property on app that we can check to see if this method would ever doing anything - and skip it if there is no possibility of assigning media?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! this config property is added that if disabled, this method will not do anything. Would that be addressing your concerns?