X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/3e1af54110b9168cccb9ea624da72e202d275b5f..a04ea95e79c60ed2a54eaec5b5c2e235fe39ef9a:/services/fuse/arvados_fuse/__init__.py diff --git a/services/fuse/arvados_fuse/__init__.py b/services/fuse/arvados_fuse/__init__.py index 148a9a654b..2d4f6c9dcd 100644 --- a/services/fuse/arvados_fuse/__init__.py +++ b/services/fuse/arvados_fuse/__init__.py @@ -20,6 +20,7 @@ import _strptime import calendar import threading import itertools +import ciso8601 from arvados.util import portable_data_hash_pattern, uuid_pattern, collection_uuid_pattern, group_uuid_pattern, user_uuid_pattern, link_uuid_pattern @@ -30,42 +31,12 @@ _logger = logging.getLogger('arvados.arvados_fuse') # appear as underscores in the fuse mount.) _disallowed_filename_characters = re.compile('[\x00/]') -class SafeApi(object): - '''Threadsafe wrapper for API object. This stores and returns a different api - object per thread, because httplib2 which underlies apiclient is not - threadsafe. - ''' - - def __init__(self, config): - self.host = config.get('ARVADOS_API_HOST') - self.api_token = config.get('ARVADOS_API_TOKEN') - self.insecure = config.flag_is_true('ARVADOS_API_HOST_INSECURE') - self.local = threading.local() - self.block_cache = arvados.KeepBlockCache() - - def localapi(self): - if 'api' not in self.local.__dict__: - self.local.api = arvados.api('v1', False, self.host, - self.api_token, self.insecure) - return self.local.api - - def localkeep(self): - if 'keep' not in self.local.__dict__: - self.local.keep = arvados.KeepClient(api_client=self.localapi(), block_cache=self.block_cache) - return self.local.keep - - def __getattr__(self, name): - # Proxy nonexistent attributes to the local API client. - try: - return getattr(self.localapi(), name) - except AttributeError: - return super(SafeApi, self).__getattr__(name) - - def convertTime(t): - '''Parse Arvados timestamp to unix time.''' + """Parse Arvados timestamp to unix time.""" + if not t: + return 0 try: - return calendar.timegm(time.strptime(t, "%Y-%m-%dT%H:%M:%SZ")) + return calendar.timegm(ciso8601.parse_datetime_unaware(t).timetuple()) except (TypeError, ValueError): return 0 @@ -280,6 +251,7 @@ class Directory(FreshBase): n.clear() llfuse.invalidate_entry(self.inode, str(n)) self.inodes.del_entry(oldentries[n]) + llfuse.invalidate_inode(self.inode) self.invalidate() def mtime(self): @@ -298,15 +270,26 @@ class CollectionDirectory(Directory): self.collection_object = None if isinstance(collection, dict): self.collection_locator = collection['uuid'] + self._mtime = convertTime(collection.get('modified_at')) else: self.collection_locator = collection + self._mtime = 0 def same(self, i): return i['uuid'] == self.collection_locator or i['portable_data_hash'] == self.collection_locator + # Used by arv-web.py to switch the contents of the CollectionDirectory + def change_collection(self, new_locator): + """Switch the contents of the CollectionDirectory. Must be called with llfuse.lock held.""" + self.collection_locator = new_locator + self.collection_object = None + self.update() + def new_collection(self, new_collection_object, coll_reader): self.collection_object = new_collection_object + self._mtime = convertTime(self.collection_object.get('modified_at')) + if self.collection_object_file is not None: self.collection_object_file.update(self.collection_object) @@ -327,9 +310,13 @@ class CollectionDirectory(Directory): if self.collection_object is not None and portable_data_hash_pattern.match(self.collection_locator): return True + if self.collection_locator is None: + self.fresh() + return True + with llfuse.lock_released: coll_reader = arvados.CollectionReader( - self.collection_locator, self.api, self.api.localkeep(), + self.collection_locator, self.api, self.api.keep, num_retries=self.num_retries) new_collection_object = coll_reader.api_response() or {} # If the Collection only exists in Keep, there will be no API @@ -348,7 +335,7 @@ class CollectionDirectory(Directory): self.fresh() return True - except apiclient.errors.NotFoundError: + except arvados.errors.NotFoundError: _logger.exception("arv-mount %s: error", self.collection_locator) except arvados.errors.ArgumentError as detail: _logger.warning("arv-mount %s: error %s", self.collection_locator, detail) @@ -376,10 +363,6 @@ class CollectionDirectory(Directory): else: return super(CollectionDirectory, self).__contains__(k) - def mtime(self): - self.checkupdate() - return convertTime(self.collection_object["modified_at"]) if self.collection_object is not None and 'modified_at' in self.collection_object else 0 - class MagicDirectory(Directory): '''A special directory that logically contains the set of all extant keep @@ -480,8 +463,8 @@ class TagsDirectory(RecursiveInvalidateDirectory): ).execute(num_retries=self.num_retries) if "items" in tags: self.merge(tags['items'], - lambda i: i['name'] if 'name' in i else i['uuid'], - lambda a, i: a.tag == i, + lambda i: i['name'], + lambda a, i: a.tag == i['name'], lambda i: TagDirectory(self.inode, self.inodes, self.api, self.num_retries, i['name'], poll=self._poll, poll_time=self._poll_time)) @@ -768,6 +751,8 @@ class Operations(llfuse.Operations): entry.st_mode = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH if isinstance(e, Directory): entry.st_mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | stat.S_IFDIR + elif isinstance(e, StreamReaderFile): + entry.st_mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | stat.S_IFREG else: entry.st_mode |= stat.S_IFREG @@ -916,5 +901,5 @@ class Operations(llfuse.Operations): # arv-mount. # The workaround is to implement it with the proper number of parameters, # and then everything works out. - def create(self, p1, p2, p3, p4, p5): + def create(self, inode_parent, name, mode, flags, ctx): raise llfuse.FUSEError(errno.EROFS)