class TestGenericFile(unittest.TestCase): def setUp(self): Configuration.FILEPATH = 'test/resources/conf/mongofs.json' self.mongo = Mongo() GenericFile.mongo = self.mongo GenericFile.configuration = Configuration() self.utils = Utils(mongo=self.mongo) self.utils.load_files() def tearDown(self): self.mongo.clean_database() def test_get_target(self): self.assertEqual(self.utils.symbolic_link.get_target(), self.utils.symbolic_link_raw['target']) def test_is_file(self): self.assertFalse(self.utils.symbolic_link.is_file()) def test_is_dir(self): self.assertFalse(self.utils.symbolic_link.is_dir()) def test_is_link(self): self.assertTrue(self.utils.symbolic_link.is_link()) self.assertFalse(self.utils.directory.is_link()) self.assertFalse(self.utils.file.is_link()) self.assertFalse(self.utils.directory_file.is_link())
def setUp(self): Configuration.FILEPATH = 'test/resources/conf/mongofs.json' self.obj = Mongo() GenericFile.mongo = self.obj GenericFile.configuration = Configuration() self.utils = Utils(mongo=self.obj) self.utils.load_files()
def setUp(self): Configuration.FILEPATH = 'test/resources/conf/mongofs.json' self.mongo = Mongo(do_clean_up=True) GenericFile.mongo = self.mongo GenericFile.configuration = Configuration() self.utils = Utils(mongo=self.mongo) self.utils.load_files()
def __init__(self): self.configuration = Configuration() self.mongo = Mongo() self.files = {} # Additional setup GenericFile.mongo = self.mongo GenericFile.configuration = self.configuration if self.configuration.is_development(): # START DEBUG ONLY - Drop the old information from MongoDB self.mongo.clean_database() self.mongo = Mongo() # Create a new instance to be sure to have the top folder
class TestRootMode(unittest.TestCase): def setUpConfig(self, path): Configuration.FILEPATH = path GenericFile.configuration = Configuration() self.obj = Mongo(do_clean_up=True) GenericFile.mongo = self.obj def tearDown(self): self.obj.clean_database() def test_default_rootMode(self): self.setUpConfig('test/resources/conf/mongofs.json') self.assertEqual(GenericFile.configuration.default_root_mode(), 0o755) self.assertEqual(GenericFile.configuration.force_root_mode(), False) root = self.obj.get_generic_file(filepath='/') self.assertEqual(root.metadata['st_mode'] & 0o777, 0o755) self.assertEqual(root.metadata['st_mode'] & S_IFDIR, S_IFDIR) def test_special_rootMode(self): self.setUpConfig('test/resources/conf/mongofs-rootMode777.json') self.assertEqual(GenericFile.configuration.default_root_mode(), 0o777) self.assertEqual(GenericFile.configuration.force_root_mode(), True) root = self.obj.get_generic_file(filepath='/') self.assertEqual(root.metadata['st_mode'] & 0o777, 0o777) self.assertEqual(root.metadata['st_mode'] & S_IFDIR, S_IFDIR) def test_force_rootMode(self): self.setUpConfig('test/resources/conf/mongofs.json') root = self.obj.get_generic_file(filepath='/') self.assertEqual(root.metadata['st_mode'] & 0o777, 0o755) self.assertEqual(root.metadata['st_mode'] & S_IFDIR, S_IFDIR) # Now change to force mode to 777, it should update the existing data for / self.setUpConfig('test/resources/conf/mongofs-rootMode777.json') root = self.obj.get_generic_file(filepath='/') self.assertEqual(root.metadata['st_mode'] & 0o777, 0o777) self.assertEqual(root.metadata['st_mode'] & S_IFDIR, S_IFDIR)
class TestMongo(unittest.TestCase): def setUp(self): Configuration.FILEPATH = 'test/resources/conf/mongofs.json' self.obj = Mongo() GenericFile.mongo = self.obj GenericFile.configuration = Configuration() self.utils = Utils(mongo=self.obj) self.utils.load_files() def tearDown(self): self.obj.clean_database() def test_load_generic_file_file(self): self.assertIsInstance(self.obj.load_generic_file(self.utils.file_raw), File) def test_load_generic_file_directory(self): self.assertIsInstance(self.obj.load_generic_file(self.utils.directory_raw), Directory) def test_load_generic_file_symbolic_link(self): self.assertIsInstance(self.obj.load_generic_file(self.utils.symbolic_link_raw), SymbolicLink) def test_current_user(self): user = self.obj.current_user() self.assertIsInstance(user['uid'], int) self.assertIsInstance(user['gid'], int) self.assertIsInstance(user['pid'], int) def test_lock_id(self): filepath = 'test-file' hostname = Mongo.configuration.hostname() pid = self.obj.current_user()['pid'] expected_lock = filepath+';'+str(pid)+';'+hostname self.assertEqual(self.obj.lock_id(filepath=filepath), expected_lock) def test_master_lock_id(self): filepath = 'test-file' hostname = Mongo.configuration.hostname() pid = 0 expected_lock = filepath + ';' + str(pid) + ';' + hostname self.assertEqual(self.obj.master_lock_id(filepath=filepath), expected_lock) def test_create_generic_file(self): self.utils.insert_file() gf = self.utils.files_coll.find_one({'directory_id':self.utils.file.directory_id,'filename':self.utils.file.filename},{'uploadDate':False}) self.assertEqual(json_util.dumps(gf), json_util.dumps(self.utils.file_raw)) def test_remove_generic_file(self): self.utils.insert_file() self.obj.remove_generic_file(generic_file=self.utils.file) gf = self.utils.files_coll.find_one({'filename': self.utils.file.filename}) self.assertEqual(gf, None) def test_remove_generic_file_directory_not_empty(self): # Try to delete the parent directory while a file still exist in it self.utils.insert_directory() self.obj.create_generic_file(generic_file=self.utils.directory_file) try: self.obj.remove_generic_file(generic_file=self.utils.directory) self.assertTrue(False, msg="It was possible to remove a directory while it was still containing files.") except FuseOSError as e: self.assertTrue(True) def test_remove_generic_file_directory_empty(self): # Try to delete the parent directory after deleting the file in it self.utils.insert_directory() self.utils.insert_directory_file() self.obj.remove_generic_file(generic_file=self.utils.directory_file) self.obj.remove_generic_file(generic_file=self.utils.directory) self.assertTrue(True) def test_list_generic_files_in_directory(self): self.utils.insert_directory() self.utils.insert_file() self.utils.insert_symbolic_link() files = self.obj.list_generic_files_in_directory(filepath='/') self.assertEqual(len(files), 3) def test_generic_file_exists(self): self.assertFalse(self.obj.generic_file_exists(self.utils.file.filepath)) self.utils.insert_file() self.assertTrue(self.obj.generic_file_exists(self.utils.file.filepath)) def test_get_generic_file(self): self.utils.insert_file() gf = self.obj.get_generic_file(filepath=self.utils.file.filepath) self.assertIsInstance(gf, File) def test_get_generic_file_take_lock(self): self.utils.insert_file() gf = self.obj.get_generic_file(filepath=self.utils.file.filepath, take_lock=True) self.assertIsInstance(gf, File) # We are the same owner, so normally, we should still be able to take the file if there is a lock on it. gf = self.obj.get_generic_file(filepath=self.utils.file.filepath, take_lock=True) self.assertIsInstance(gf, File) def test_get_generic_file_missing(self): gf = self.obj.get_generic_file(filepath=self.utils.file.filepath) self.assertEqual(gf, None) def test_add_nlink_directory(self): # By default, a directory has 2 st_nlink. And by default, the "/" directory always exists. self.obj.add_nlink_directory(directory_id=self.utils.root_id, value=4) gf = self.utils.files_coll.find_one({'_id': self.utils.root_id}) self.assertEqual(gf['metadata']['st_nlink'], 6) def test_read_data(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() data = self.obj.read_data(file=self.utils.file, offset=0, size=4096) self.assertEqual(data, message) data = self.obj.read_data(file=self.utils.file, offset=3, size=4096) self.assertEqual(data, message[3:]) data = self.obj.read_data(file=self.utils.file, offset=0, size=8) self.assertEqual(data, message[:8]) data = self.obj.read_data(file=self.utils.file, offset=3, size=8) self.assertEqual(data, message[3:3+8]) def test_add_data_append(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() self.obj.add_data(file=self.utils.file, data=b'test', offset=len(message)) modified_message = self.utils.read_file_chunks() self.assertEqual(modified_message, message+b'test') def test_add_data_replace(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() message = list(message) message[2] = ord('t') message[3] = ord('h') message[4] = ord('i') message[5] = ord('n') message[6] = ord('g') message[7] = ord('s') expected_message = ''.join(map(chr, message)) self.obj.add_data(file=self.utils.file, data=b'things', offset=2) modified_message = self.utils.read_file_chunks() formatted_modified_message = ''.join(map(chr, list(modified_message))) self.assertEqual(formatted_modified_message, expected_message) def test_truncate(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() self.obj.truncate(file=self.utils.file, length=6) modified_message = self.utils.read_file_chunks() self.assertEqual(modified_message, message[0:6]) def test_truncate_zero(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() self.obj.truncate(file=self.utils.file, length=0) modified_message = self.utils.read_file_chunks() self.assertEqual(modified_message, message[0:0]) def test_rename_generic_file_to(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() initial_filepath = self.utils.file.filepath self.obj.rename_generic_file_to(generic_file=self.utils.file, initial_filepath=self.utils.file.filepath, destination_filepath='/rename-test') destination_message = self.utils.read_file_chunks() # Read chunks is based on the _id, so we can call it # Normally, the chunks should not change at all, but we never know. self.assertEqual(destination_message, message) old_file = self.utils.files_coll.find_one({'directory_id':self.utils.file.directory_id,'filename':initial_filepath.split('/')[-1]}) self.assertEqual(old_file, None) new_file = self.utils.files_coll.find_one({'directory_id':self.utils.root_id,'filename':'rename-test'}) self.assertNotEqual(new_file, None) def test_unlock_generic_file(self): self.utils.insert_file() gf = self.obj.get_generic_file(filepath=self.utils.file.filepath, take_lock=True) result = self.obj.unlock_generic_file(filepath=self.utils.file.filepath, generic_file=gf) self.assertTrue(result) gf = self.obj.get_generic_file(filepath=self.utils.file.filepath) self.assertTrue('lock' not in gf.json) def test_unlock_generic_file_no_lock(self): # Verify if it does not crash if there is no lock self.utils.insert_file() gf = self.obj.get_generic_file(filepath=self.utils.file.filepath, take_lock=False) result = self.obj.unlock_generic_file(filepath=self.utils.file.filepath, generic_file=gf) self.assertTrue(result) def test_basic_save(self): self.utils.insert_file() self.obj.basic_save(generic_file=self.utils.file, metadata={'st_nlink':1}, attrs={'thing':1}) result = self.utils.files_coll.find_one({'_id':self.utils.file._id}) self.assertTrue('st_nlink' in result['metadata'] and len(result['metadata']) == 1) self.assertTrue('thing' in result['attrs'] and len(result['attrs']) == 1)
class MongoFS(LoggingMixIn, Operations): # This is useful to be able to umount if there is an error to access MongoDB for example mounting_point = None def __init__(self): self.configuration = Configuration() self.mongo = Mongo() self.files = {} # Additional setup GenericFile.mongo = self.mongo GenericFile.configuration = self.configuration if self.configuration.is_development(): # START DEBUG ONLY - Drop the old information from MongoDB self.mongo.clean_database() self.mongo = Mongo() # Create a new instance to be sure to have the top folder # END DEBUG ONLY # The top folder of the FS is automatically created by the Mongo class. """ Create a file and returns a "file descriptor", which is in fact, simply the _id. """ def create(self, path, mode): file = GenericFile.new_generic_file(filepath=path, mode=mode, file_type=GenericFile.FILE_TYPE) return file.file_descriptor """ Acquire a lock on a specific file. """ def lock(self, path, fip, cmd, lock): # Getting the file object with a lock is enough to be sure there is one on it. self.mongo.get_generic_file(filepath=path, take_lock=True) return lock """ Release the lock on a specific file. """ def release(self, path, fh): print('Release the lock on '+str(path)+': '+str(fh)) gf = self.mongo.get_generic_file(filepath=path) print('Before unlock: '+str(gf.lock)) gf.unlock(filepath=path) return 0 """ Release the lock on a specific directory. """ def releasedir(self, path, fh): gf = self.mongo.get_generic_file(filepath=path) gf.unlock(filepath=path) return 0 """ Rename a generic file """ def rename(self, old, new): generic_file = self.mongo.get_generic_file(filepath=old) generic_file.rename_to(initial_filepath=old, destination_filepath=new) """ Create a symbolic link to a generic file (source -> target). No need to return a file descriptor. target: the file we want to display if we display "source" source: the symbolic link itself Important: It seems we receive the symlink parameters in a different order than "ln -s TARGET SYMLINK", we receive them (SYMLINK, TARGET) which is kinda weird. """ def symlink(self, source, target): GenericFile.new_generic_file(filepath=source, mode=0o777, file_type=GenericFile.SYMBOLIC_LINK_TYPE, target=target) """ Read a symbolic link and return the file we should be redirected to. """ def readlink(self, path): link = self.mongo.get_generic_file(filepath=path) return link.get_target() """ Read a part of a file """ def read(self, path, size, offset, fh): file = self.mongo.get_generic_file(filepath=path) tmp = file.read_data(offset=offset, size=size) return tmp """ Delete a file """ def unlink(self, path): file_or_link = self.mongo.get_generic_file(filepath=path) self.mongo.remove_generic_file(generic_file=file_or_link) """ Create a directory, no need to return anything """ def mkdir(self, path, mode): GenericFile.new_generic_file(filepath=path, mode=mode, file_type=GenericFile.DIRECTORY_TYPE) """ Delete a directory """ def rmdir(self, path): directory = self.mongo.get_generic_file(filepath=path) self.mongo.remove_generic_file(generic_file=directory) """ List files inside a directory """ def readdir(self, path, fh): files = self.mongo.list_generic_files_in_directory(filepath=path) # We need to only keep final filename filenames = [file.filename for file in files] return ['.', '..'] + filenames """ Write data to a file, from a specific offset. Returns the written data size """ def write(self, path, data, offset, fh): file = self.mongo.get_generic_file(filepath=path) file.add_data(data=data, offset=offset) return len(data) """ Truncate a file to a specific length """ def truncate(self, path, length, fh=None): file = self.mongo.get_generic_file(filepath=path) file.truncate(length=length) """ Return general information for a given path """ def getattr(self, path, fh=None): gf = self.mongo.get_generic_file(filepath=path) if gf is None: raise FuseOSError(ENOENT) return gf.metadata """ Set permissions to a given path """ def chmod(self, path, mode): gf = self.mongo.get_generic_file(filepath=path) gf.metadata['st_mode'] &= 0o770000 gf.metadata['st_mode'] |= mode gf.basic_save() return 0 """ Set owner (user & group) to a given path """ def chown(self, path, uid, gid): gf = self.mongo.get_generic_file(filepath=path) gf.metadata['st_uid'] = uid gf.metadata['st_gid'] = gid gf.basic_save() """ Return a specific special attribute for a given path (for selinux for example) """ def getxattr(self, path, name, position=0): gf = self.mongo.get_generic_file(filepath=path) try: return gf.attrs[name] except KeyError: return '' # Should return ENOATTR """ Return all special attributes for a given path (for selinux for example) """ def listxattr(self, path): gf = self.mongo.get_generic_file(filepath=path) return gf.attrs.keys() """ Remove a specific special attribute for a given path """ def removexattr(self, path, name): gf = self.mongo.get_generic_file(filepath=path) if name in gf.attrs: del gf.attrs[name] gf.basic_save() else: # Should return ENOATTR pass """ Update the access and update time for a given path """ def utimens(self, path, times=None): now = time.time() atime, mtime = times if times else (now, now) gf = self.mongo.get_generic_file(filepath=path) gf.metadata['st_atime'] = atime gf.metadata['st_mtime'] = mtime gf.basic_save() """ Update a specific special attribute for a given path """ def setxattr(self, path, name, value, options, position=0): gf = self.mongo.get_generic_file(filepath=path) gf.attrs[name] = value gf.basic_save() """ General (static) information about the current file system. """ def statfs(self, path): print('Call statfs for '+str(path)+'...') return dict(f_bsize=65536*8, f_frsize=65536*8, f_blocks=65536*8, f_bavail=65536) """ Flush data to MongoDB """ def flush(self, path, fh): file = self.mongo.get_generic_file(filepath=path) self.mongo.flush_data_to_write(file=file) return None
class TestGenericFile(unittest.TestCase): def setUp(self): Configuration.FILEPATH = 'test/resources/conf/mongofs.json' self.mongo = Mongo(do_clean_up=True) GenericFile.mongo = self.mongo GenericFile.configuration = Configuration() self.utils = Utils(mongo=self.mongo) self.utils.load_files() def tearDown(self): self.mongo.clean_database() def test_basic_save(self): self.utils.insert_file() initial_gf = self.utils.files_coll.find_one( { 'directory_id': self.utils.root_id, 'filename': self.utils.file.filename }, {'uploadDate': False}) self.utils.file.metadata['st_nlink'] = 37 self.utils.file.basic_save() modified_gf = self.utils.files_coll.find_one( { 'directory_id': self.utils.root_id, 'filename': self.utils.file.filename }, {'uploadDate': False}) self.assertEqual(modified_gf['metadata']['st_nlink'], 37) # We do not test is_dir / is_file / is_link here as we cannot load a GenericFile only, so the test would have no purpose. # We need to test those methods in test_File, test_Directory and test_SymbolicLink. def test_rename_to(self): self.utils.insert_file() initial_filename = self.utils.file.filename self.utils.file.rename_to(initial_filepath=self.utils.file.filepath, destination_filepath='/rename-file') old_file = self.utils.files_coll.find_one({ 'directory_id': self.utils.file.directory_id, 'filename': initial_filename }) self.assertEqual(old_file, None) new_file = self.utils.files_coll.find_one({ 'directory_id': self.utils.root_id, 'filename': 'rename-file' }) self.assertNotEqual(new_file, None) def test_release(self): self.utils.insert_file() gf = self.mongo.get_generic_file(filepath=self.utils.file.filepath, lock={'type': GenericFile.LOCK_WRITE}) self.assertTrue('lock' in gf.json) gf.release(filepath=self.utils.file.filepath) gf = self.mongo.get_generic_file(filepath=self.utils.file.filepath) self.assertTrue('lock' not in gf.json or len(gf.json['lock']) == 0) def test_new_generic_file_file(self): # Creation of a basic file GenericFile.new_generic_file(filepath=self.utils.file.filepath, mode=0o755, file_type=GenericFile.FILE_TYPE) inserted_file = self.utils.files_coll.find_one({ 'directory_id': self.utils.file.directory_id, 'filename': self.utils.file.filename }) self.assertEqual(inserted_file['filename'], self.utils.file.filename) self.assertEqual(inserted_file['metadata']['st_mode'], (S_IFREG | 0o755)) self.assertEqual(inserted_file['generic_file_type'], GenericFile.FILE_TYPE) def test_new_generic_file_directory(self): # Creation of a directory GenericFile.new_generic_file(filepath=self.utils.directory.filepath, mode=0o755, file_type=GenericFile.DIRECTORY_TYPE) inserted_file = self.utils.files_coll.find_one({ 'directory_id': self.utils.directory.directory_id, 'filename': self.utils.directory.filename }) self.assertEqual(inserted_file['filename'], self.utils.directory.filename) self.assertEqual(inserted_file['metadata']['st_mode'], (S_IFDIR | 0o755)) self.assertEqual(inserted_file['generic_file_type'], GenericFile.DIRECTORY_TYPE) def test_new_generic_file_symbolic_link(self): # Creation of a symbolic link to the initial self.utils.file just below self.utils.insert_file() GenericFile.new_generic_file( filepath=self.utils.symbolic_link.filepath, mode=0o755, file_type=GenericFile.SYMBOLIC_LINK_TYPE, target=self.utils.file.filename) inserted_file = self.utils.files_coll.find_one({ 'directory_id': self.utils.symbolic_link.directory_id, 'filename': self.utils.symbolic_link.filename }) self.assertEqual(inserted_file['filename'], self.utils.symbolic_link.filename) self.assertEqual(inserted_file['metadata']['st_mode'], (S_IFLNK | 0o755)) self.assertEqual(inserted_file['generic_file_type'], GenericFile.SYMBOLIC_LINK_TYPE) self.assertEqual(inserted_file['target'], self.utils.file.filename) def test_get_directory_id(self): self.utils.insert_directory() self.utils.insert_directory_file() directory_id = GenericFile.get_directory_id( filepath=self.utils.directory_file.filepath) self.assertEqual(directory_id, self.utils.directory_file.directory_id) def test_is_generic_filename_available(self): self.utils.insert_file() is_available = GenericFile.is_generic_filepath_available( filepath=self.utils.file.filepath) self.assertFalse(is_available) is_available = GenericFile.is_generic_filepath_available( filepath=self.utils.file.filepath + '.something') self.assertTrue(is_available)
class MongoFS(LoggingMixIn, Operations): # This is useful to be able to umount if there is an error to access MongoDB for example mounting_point = None def __init__(self): self.configuration = Configuration() self.mongo = Mongo() self.files = {} # Additional setup GenericFile.mongo = self.mongo GenericFile.configuration = self.configuration if self.configuration.is_development(): # START DEBUG ONLY - Drop the old information from MongoDB self.mongo.clean_database() self.mongo = Mongo( ) # Create a new instance to be sure to have the top folder # END DEBUG ONLY # The top folder of the FS is automatically created by the Mongo class. """ Create a file and returns a "file descriptor", which is in fact, simply the _id. """ def create(self, path, mode, fi=None): file = GenericFile.new_generic_file(filepath=path, mode=mode, file_type=GenericFile.FILE_TYPE) return file.file_descriptor """ Acquire a lock on a specific file. """ def lock(self, path, fh, cmd, lock): # lock is a pointer to a struct flock. The first field is a short # with the lock type, so we can just cast it to a POINTER(c_short) # We won't manage locking only part of file, so we don't care about the # complete struct lock_type_pointer = cast(lock, POINTER(c_short)) lock_type = lock_type_pointer[0] if cmd == fcntl.F_GETLK: blocking = self.mongo.test_lock_and_get_first_blocking( filepath=path, lock={'type': lock_type}) if blocking is None: # We modify the pointer by setting F_UNLCK lock_type_pointer[0] = fcntl.F_UNLCK else: # We modify the pointer by setting the blocking type of the locks lock_type_pointer[0] = blocking['type'] elif cmd == fcntl.F_SETLK or cmd == fcntl.F_SETLKW: if self.mongo.get_generic_file(filepath=path, lock={ 'type': lock_type, 'wait': cmd == fcntl.F_SETLKW }) is None: raise FuseOSError(errno.ENOENT) else: raise FuseOSError(errno.EBADF) return 0 """ Release the lock on a specific file. """ def release(self, path, fh): gf = self.mongo.get_generic_file(filepath=path) gf.release(filepath=path) return 0 """ Release the lock on a specific directory. """ def releasedir(self, path, fh): gf = self.mongo.get_generic_file(filepath=path) gf.release(filepath=path) return 0 """ Rename a generic file """ def rename(self, old, new): generic_file = self.mongo.get_generic_file(filepath=old) generic_file.rename_to(initial_filepath=old, destination_filepath=new) """ Create a symbolic link to a generic file (source -> target). No need to return a file descriptor. target: the file we want to display if we display "source" source: the symbolic link itself Important: It seems we receive the symlink parameters in a different order than "ln -s TARGET SYMLINK", we receive them (SYMLINK, TARGET) which is kinda weird. """ def symlink(self, source, target): GenericFile.new_generic_file(filepath=source, mode=0o777, file_type=GenericFile.SYMBOLIC_LINK_TYPE, target=target) """ Read a symbolic link and return the file we should be redirected to. """ def readlink(self, path): link = self.mongo.get_generic_file(filepath=path) return link.get_target() """ Read a part of a file """ def read(self, path, size, offset, fh): file = self.mongo.get_generic_file(filepath=path) tmp = file.read_data(offset=offset, size=size) return tmp """ Delete a file """ def unlink(self, path): file_or_link = self.mongo.get_generic_file(filepath=path) self.mongo.remove_generic_file(generic_file=file_or_link) """ Create a directory, no need to return anything """ def mkdir(self, path, mode): GenericFile.new_generic_file(filepath=path, mode=mode, file_type=GenericFile.DIRECTORY_TYPE) """ Delete a directory """ def rmdir(self, path): directory = self.mongo.get_generic_file(filepath=path) self.mongo.remove_generic_file(generic_file=directory) """ List files inside a directory """ def readdir(self, path, fh): files = self.mongo.list_generic_files_in_directory(filepath=path) # We need to only keep final filename filenames = [file.filename for file in files] return ['.', '..'] + filenames """ Write data to a file, from a specific offset. Returns the written data size """ def write(self, path, data, offset, fh): file = self.mongo.get_generic_file(filepath=path) file.add_data(data=data, offset=offset) return len(data) """ Truncate a file to a specific length """ def truncate(self, path, length, fh=None): file = self.mongo.get_generic_file(filepath=path) file.truncate(length=length) """ Return general information for a given path """ def getattr(self, path, fh=None): gf = self.mongo.get_generic_file(filepath=path) if gf is None: raise FuseOSError(errno.ENOENT) metadata = gf.metadata if gf.host != self.configuration.hostname(): if metadata['st_uid'] != 0: uid = self.mongo.get_userid(gf.uname) if uid is not None: metadata['st_uid'] = uid if metadata['st_gid'] != 0: gid = self.mongo.get_groupid(gf.gname) if gid is not None: metadata['st_gid'] = gid return metadata """ Set permissions to a given path """ def chmod(self, path, mode): if path == '/' and configuration.force_root_mode(): raise FuseOSError(errno.EACCESS) gf = self.mongo.get_generic_file(filepath=path) gf.metadata['st_mode'] &= 0o770000 gf.metadata['st_mode'] |= mode gf.basic_save() return 0 """ Set owner (user & group) to a given path """ def chown(self, path, uid, gid): gf = self.mongo.get_generic_file(filepath=path) gf.host = self.configuration.hostname() gf.metadata['st_uid'] = uid gf.metadata['st_gid'] = gid gf.uname = self.mongo.get_username(uid) gf.gname = self.mongo.get_groupname(gid) gf.basic_save() """ Return a specific special attribute for a given path (for selinux for example) """ def getxattr(self, path, name, position=0): gf = self.mongo.get_generic_file(filepath=path) try: return gf.attrs[name] except KeyError: return '' # Should return ENOATTR """ Return all special attributes for a given path (for selinux for example) """ def listxattr(self, path): gf = self.mongo.get_generic_file(filepath=path) return gf.attrs.keys() """ Remove a specific special attribute for a given path """ def removexattr(self, path, name): gf = self.mongo.get_generic_file(filepath=path) if name in gf.attrs: del gf.attrs[name] gf.basic_save() else: raise FuseOSError(errno.EACCES) """ Update the access and update time for a given path """ def utimens(self, path, times=None): now = time.time() atime, mtime = times if times else (now, now) gf = self.mongo.get_generic_file(filepath=path) gf.metadata['st_atime'] = atime gf.metadata['st_mtime'] = mtime gf.basic_save() """ Update a specific special attribute for a given path """ def setxattr(self, path, name, value, options, position=0): gf = self.mongo.get_generic_file(filepath=path) gf.attrs[name] = value gf.basic_save() """ General (static) information about the current file system. """ def statfs(self, path): print('Call statfs for ' + str(path) + '...') return dict(f_bsize=65536 * 8, f_frsize=65536 * 8, f_blocks=65536 * 8, f_bavail=65536) """ Flush data to MongoDB """ def flush(self, path, fh): file = self.mongo.get_generic_file(filepath=path) self.mongo.flush_data_to_write(file=file) return None
class TestGenericFile(unittest.TestCase): def setUp(self): Configuration.FILEPATH = 'test/resources/conf/mongofs.json' self.mongo = Mongo() GenericFile.mongo = self.mongo GenericFile.configuration = Configuration() self.utils = Utils(mongo=self.mongo) self.utils.load_files() def tearDown(self): self.mongo.clean_database() # The tests below are mostly taken from test_Mongo as we need to check the same thing... A few changes have been # made to be sure to the test the right interface (GenericFile and not Mongo) def test_add_data_append(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() self.utils.file.add_data(data=b'test', offset=len(message)) modified_message = self.utils.read_file_chunks() self.assertEqual(modified_message, message + b'test') def test_add_data_replace(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() message = list(message) message[2] = ord('t') message[3] = ord('h') message[4] = ord('i') message[5] = ord('n') message[6] = ord('g') message[7] = ord('s') expected_message = ''.join(map(chr, message)) self.utils.file.add_data(data=b'things', offset=2) modified_message = self.utils.read_file_chunks() formatted_modified_message = ''.join(map(chr, list(modified_message))) self.assertEqual(formatted_modified_message, expected_message) def test_read_data(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() data = self.utils.file.read_data(offset=0, size=4096) self.assertEqual(data, message) data = self.utils.file.read_data(offset=3, size=4096) self.assertEqual(data, message[3:]) data = self.utils.file.read_data(offset=0, size=8) self.assertEqual(data, message[:8]) data = self.utils.file.read_data(offset=3, size=8) self.assertEqual(data, message[3:3 + 8]) def test_truncate(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() self.utils.file.truncate(length=6) modified_message = self.utils.read_file_chunks() self.assertEqual(modified_message, message[0:6]) def test_truncate_zero(self): self.utils.insert_file() self.utils.insert_file_chunks() message = self.utils.read_file_chunks() self.utils.file.truncate(length=0) modified_message = self.utils.read_file_chunks() self.assertEqual(modified_message, message[0:0]) def test_is_file(self): self.assertTrue(self.utils.file.is_file()) self.assertFalse(self.utils.directory.is_file()) self.assertFalse(self.utils.symbolic_link.is_file()) self.assertTrue(self.utils.directory_file.is_file()) def test_is_dir(self): self.assertFalse(self.utils.file.is_dir()) def test_is_link(self): self.assertFalse(self.utils.file.is_link())
def setUpConfig(self, path): Configuration.FILEPATH = path GenericFile.configuration = Configuration() self.obj = Mongo(do_clean_up=True) GenericFile.mongo = self.obj