Changeset 1226
- Timestamp:
- 06/08/07 08:46:24 (1 year ago)
- Files:
-
- trunk/src/aksyfuse/aksyfs.py (modified) (15 diffs)
- trunk/src/aksyfuse/tests/test_aksyfs.py (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/src/aksyfuse/aksyfs.py
r1225 r1226 15 15 from aksy.devices.akai import sampler as samplermod 16 16 17 fuse.fuse_python_api = (0, 1) # TODO: migrate to 0.2 17 fuse.fuse_python_api = (0, 2) 18 19 fuse.feature_assert('stateful_files') 18 20 19 21 MAX_FILE_SIZE_SAMPLE = 512 * 1024 * 1024 # 512 MB 20 22 MAX_FILE_SIZE_OTHER = 16 * 1024 # 16K 21 EOF = '\x00'22 23 START_TIME = time() 23 24 25 class StatInfo(object): 26 @staticmethod 27 def set_owner(uid, gid): 28 StatInfo.uid = uid 29 StatInfo.gid = gid 30 31 def __init__(self, mode, size, writable=False): 32 self.st_dev = 0 # TODO: figure out whether required to provide unique value 33 self.st_ino = 0 # TODO: figure out whether required to provide unique value 34 self.st_mode = mode|stat.S_IRUSR|stat.S_IRGRP 35 if writable: 36 self.st_mode |= stat.S_IWUSR 37 38 self.st_size = size 39 self.st_atime = int(time()) 40 self.st_ctime = self.st_atime 41 if is_modified: 42 self.st_mtime = int(time()) 43 else: 44 self.st_mtime = self.st_atime 45 self.st_uid = StatInfo.uid 46 self.st_gid = StatInfo.gid 47 48 class DirStatInfo(StatInfo): 49 def __init__(self, writable=False, child_count=1): 50 StatInfo.__init__(self, stat.S_IFDIR|stat.S_IEXEC, 4096L) 51 self.st_nlink = child_count 52 53 class FileStatInfo(StatInfo): 54 def __init__(self, path, size, writable=False): 55 cache_path = _get_cache_path(path) 56 if size is None: 57 if os.path.exists(cache_path): 58 size = os.lstat(cache_path).st_size 59 elif fileutils.is_sample(path): 60 size = MAX_FILE_SIZE_SAMPLE 61 else: 62 size = MAX_FILE_SIZE_OTHER 63 StatInfo.__init__(self, stat.S_IFREG|stat.S_IWUSR, size, writable) 64 self.st_nlink = 0 65 24 66 def stat_inode(mode, size, child_count, uid, gid, writable=False, is_modified=False): 25 67 info = [None] * 10 … … 41 83 return info 42 84 43 def stat_dir(uid, gid, writable=True, child_count=1):85 def to_remove_stat_dir(uid, gid, writable=True, child_count=1): 44 86 return stat_inode(stat.S_IFDIR|stat.S_IEXEC, 4096L, child_count, uid, gid, writable) 45 87 46 88 # TODO: provide real values for samples (other sizes can't be determined 47 def stat_file(uid, gid, path, size=None, is_modified=False):89 def to_remove_stat_file(uid, gid, path, size=None, is_modified=False): 48 90 cache_path = _get_cache_path(path) 49 91 if size is None: … … 56 98 return stat_inode(stat.S_IFREG|stat.S_IWUSR, size, 0, uid, gid, is_modified) 57 99 58 def is_modified(stat_ tuple):59 return stat_ tuple[stat.ST_MTIME]> START_TIME100 def is_modified(stat_info): 101 return stat_info.st_mtime > START_TIME 60 102 61 103 def _splitpath(path): … … 75 117 return cache_path 76 118 77 class FileInfo(object): 78 def __init__(self, path, upload, handle=None, flags=None): 119 class AksyFile(fuse.FuseFileInfo): 120 @staticmethod 121 def set_sampler(sampler): 122 AksyFile.sampler = sampler 123 124 def __init__(self, path, flags, *mode): 125 self.direct_io = True 126 self.upload = bool(flags & os.O_WRONLY) 127 self.name = os.path.basename(path) 79 128 location = _splitpath(path)[0] 80 129 if location == "memory": … … 84 133 else: 85 134 raiseException(errno.ENOENT) 86 87 self.name = os.path.basename(path) 135 88 136 self.path = _create_cache_path(path) 89 self.upload = upload 90 91 if handle is None: 92 self.handle = os.open(self.path, flags) 93 else: 94 self.handle = handle 95 137 138 if not self.is_upload() or not _cache_path_exists(path): 139 AksyFile.sampler.get(self.name, self.path, self.location) 140 141 self.handle = os.open(self.path, flags, *mode) 142 143 144 def release(self, flags): 145 if self.is_upload(): 146 try: 147 AksyFile.sampler.put(self.get_path(), None, self.get_location()) 148 except IOError, exc: 149 # TODO: move to a method where we can raise exceptions 150 print "Exception occurred: ", repr(exc) 151 os.close(self.handle) 152 153 def write(self, buf, offset): 154 os.lseek(self.handle, offset, 0) 155 return os.write(self.handle, buf) 156 157 def read(self, length, offset): 158 os.lseek(self.handle, offset, 0) 159 return os.read(self.handle, length) 160 96 161 def get_name(self): 97 162 return self.name … … 100 165 return self.path 101 166 167 def is_upload(self): 168 return self.upload 169 102 170 def get_handle(self): 103 171 return self.handle 104 172 105 def is_upload(self):106 return self.upload107 108 173 def get_location(self): 109 174 return self.location 175 176 177 class FSStatInfo(fuse.StatVfs): 178 def __init__(self, mem_total, mem_free): 179 self.f_bsize = 1024 180 self.f_frsize = 1024 181 self.f_blocks = mem_total 182 self.f_bfree = mem_free 183 self.f_bavail = mem_free 184 self.f_files = 0 185 self.f_ffree = 0 186 self.f_flag = 0 187 self.f_namemax = 80 110 188 111 189 class FSRoot(model.Container): 112 190 def __init__(self, sampler): 113 191 self.sampler = sampler 114 self.file_cache = {}115 192 116 193 def is_writable(self): … … 119 196 def get_children(self): 120 197 return [self.sampler.memory, self.sampler.disks] 121 122 def open(self, path, flags, is_modified=False):123 info = self.file_cache.setdefault(path, FileInfo(path, False, flags=flags|os.O_CREAT))124 if not info.is_upload() or not _cache_path_exists(path):125 self.sampler.get(info.get_name(), info.get_path(), info.get_location())126 127 def close(self, path):128 info = self.file_cache.get(path, None)129 if info is not None:130 if info.is_upload():131 try:132 self.sampler.put(info.get_path(), None, info.get_location())133 except IOError, exc:134 # TODO: move to a method where we can raise exceptions135 print "Exception occurred: ", repr(exc)136 os.close(info.get_handle())137 del self.file_cache[path]138 139 def mknod(self, path):140 self.file_cache[path] = FileInfo(path, True, flags=os.O_CREAT|os.O_WRONLY)141 142 def write(self, path, buf, offset):143 handle = self.file_cache[path].get_handle()144 os.lseek(handle, offset, 0)145 return os.write(handle, buf)146 147 def read(self, path, length, offset):148 handle = self.file_cache[path].get_handle()149 os.lseek(handle, offset, 0)150 read = os.read(handle, length)151 if len(read) < length:152 return read + EOF153 else:154 return read155 156 def statfs(self):157 blocks_size = 1024158 blocks = self.sampler.systemtools.get_wave_mem_size()159 blocks_free = self.sampler.systemtools.get_free_wave_mem_size()160 files = 0161 files_free = 0162 blocks_available = blocks_free163 namelen = 80164 return (blocks_size, blocks, blocks_free, blocks_available, files, files_free, namelen)165 198 166 199 class AksyFS(fuse.Fuse): #IGNORE:R0904 167 def __init__(self, sampler): 168 self.multithreaded = 0 169 self.debug = True 170 fuse.Fuse.__init__(self, [], fuse_args='-odirect_io=True') 171 172 self.root = FSRoot(sampler) 173 174 self.cache = { '/': self.root } 175 200 def __init__(self, *args, **kw): 201 fuse.Fuse.__init__(self, *args, **kw) 202 self.file_class = AksyFile 176 203 stat_home = os.stat(os.path.expanduser('~')) 177 self.uid = stat_home[stat.ST_UID] 178 self.gid = stat_home[stat.ST_GID] 179 204 StatInfo.set_owner(stat_home[stat.ST_UID], stat_home[stat.ST_GID]) 205 206 def access(self, path, mode): 207 print "**access " + path 208 pass 209 180 210 def stat_directory(self, path): 181 211 folder = self.cache.get(path) … … 185 215 raiseException(errno.ENOENT) 186 216 self.cache[path] = folder 187 return stat_dir(self.uid, self.gid,folder.is_writable())217 return DirStatInfo(folder.is_writable()) 188 218 189 219 def get_parent(self, path): … … 208 238 return cached 209 239 if _cache_path_exists(path): 210 return os. stat(_create_cache_path(path))240 return os.lstat(_create_cache_path(path)) 211 241 212 242 file_obj = self.get_file(path) … … 214 244 raiseException(errno.ENOENT) 215 245 else: 216 return stat_file(self.uid, self.gid,path, file_obj.get_size(), False)246 return FileStatInfo(path, file_obj.get_size(), False) 217 247 218 248 def getattr(self, path): … … 223 253 return self.stat_file(path) 224 254 225 def getdir(self, path):226 print '*** getdir', path255 def readdir(self, path, offset): 256 print '*** readdir', path, offset 227 257 folder = self.cache[path] 228 return [(child.get_name(), 0) for child in folder.get_children()] 258 for child in folder.get_children(): 259 yield fuse.Direntry(child.get_name()) 229 260 230 261 def mkdir(self, path, mode): … … 238 269 child = folder.create_folder(os.path.basename(path)) 239 270 self.cache[path] = child 240 241 def open(self, path, flags):242 print '*** open', path, flags243 info = self.stat_file(path)244 self.root.open(path, flags, is_modified(info))245 246 def read(self, path, length, offset):247 print '*** read', path, length, offset248 return self.root.read(path, length, offset)249 250 def release(self, path, flags):251 print '*** release', path, flags252 self.root.close(path)253 271 254 272 def rename(self, old_path, new_path): … … 296 314 def mknod(self, path, mode, dev): 297 315 print '*** mknod ', path, mode, dev 298 self.cache[path] = stat_file(self.uid, self.gid, path) 299 self.root.mknod(path) 316 self.cache[path] = FileStatInfo(path, None) 300 317 self.get_parent(path).refresh() 301 302 def write(self, path, buf, offset):303 print '*** write', path, len(buf), offset304 return self.root.write(path, buf, offset)305 318 306 319 def truncate(self, path, size): #IGNORE:W0212 … … 310 323 def statfs(self): 311 324 print "*** statfs (metrics on memory contents only)" 312 return self.root.statfs() 325 mem_total = self.sampler.systemtools.get_wave_mem_size() 326 mem_free = self.sampler.systemtools.get_free_wave_mem_size() 327 return FSStatInfo(mem_total, mem_free) 328 329 def init_sampler(self, sampler): 330 self.root = FSRoot(sampler) 331 self.sampler = sampler 332 self.cache = { '/': self.root } 333 AksyFile.set_sampler(sampler) 334 335 def main(self, sampler, *args, **kw): 336 self.init_sampler(sampler) 337 return fuse.Fuse.main(self, *args, **kw) 313 338 314 339 def raiseUnsupportedOperationException(): … … 319 344 320 345 if __name__ == '__main__': 321 args = sys.argv 322 if len(args) < 3: 323 print "Usage: " + args[0] + " <mount point> <sampler id> [fuse option string]" 324 sys.exit(-1) 325 sampler_id = args.pop(2) 326 327 if sampler_id == "mock_z48": 346 347 usage = """ 348 Aksyfs: mount your sampler as a filesystem 349 350 """ + fuse.Fuse.fusage 351 352 fs = AksyFS(version="%prog " + fuse.__version__, 353 usage=usage, 354 dash_s_do='setsingle') 355 356 fs.parser.add_option(mountopt="sampler_id", metavar="SAMPLER_ID", default='mock_z48', 357 help="mount SAMPLER_ID [default: %default]") 358 fs.parse(values=fs, errex=1) 359 360 if fs.sampler_id == "mock_z48": 361 script_dir = os.path.abspath(os.path.split(__file__)[0]) 362 src_dir = os.path.dirname(script_dir) 328 363 sampler = Devices.get_instance('mock_z48', None, 329 debug= 0,330 sampleFile= 'src/aksy/test/test.wav')364 debug=1, 365 sampleFile=os.path.join(src_dir, 'aksy/test/test.wav')) 331 366 else: 332 sampler = Devices.get_instance(sampler_id, 'usb') 333 fs = AksyFS(sampler) 367 sampler = Devices.get_instance(fs.sampler_id, 'usb') 334 368 try: 335 fs.main( )369 fs.main(sampler) 336 370 finally: 337 sampler.close()371 fs.sampler.close() trunk/src/aksyfuse/tests/test_aksyfs.py
r1221 r1226 10 10 log = logging.getLogger('aksy') 11 11 12 class TestModuleTest(TestCase): 12 class TestStatInfo(TestCase): 13 def test_set_owner(self): 14 aksyfs.StatInfo.set_owner(1, 1) 15 self.assertEquals(1, aksyfs.StatInfo.uid) 16 self.assertEquals(1, aksyfs.StatInfo.gid) 17 info = aksyfs.StatInfo(0, 1) 18 self.assertEquals(1, info.st_uid) 19 self.assertEquals(1, info.st_gid) 20 21 class TestDirStatInfo(TestCase): 13 22 def test_stat_directory(self): 14 info = aksyfs.stat_dir(1000, 1000) 15 self.assertTrue(S_ISDIR(info[ST_MODE])) 16 self.assertEquals(len(os.stat('/')), len(info)) 23 info = aksyfs.DirStatInfo() 24 self.assertTrue(S_ISDIR(info.st_mode)) 17 25 26 class TestFileStatInfo(TestCase): 18 27 def test_stat_file(self): 19 info = aksyfs. stat_file(1000, 1000, 'test.akp')20 self.assertFalse(S_ISDIR(info [ST_MODE]))21 self.assertEquals( len(os.stat('/')), len(info))22 self.assert Equals(16*1024, info[ST_SIZE])23 28 info = aksyfs.FileStatInfo('test.akp', None) 29 self.assertFalse(S_ISDIR(info.st_mode)) 30 self.assertEquals(16*1024, info.st_size) 31 self.assertTrue(S_ISREG(info.st_mode)) 32 24 33 class AksyFSTest(TestCase): #IGNORE:R0904 25 34 def _assertdir(self, info): 26 self.assertTrue(S_ISDIR(info [ST_MODE]))35 self.assertTrue(S_ISDIR(info.st_mode)) 27 36 28 37 def setUp(self): … … 30 39 debug=0, 31 40 sampleFile=testutil.get_test_resource('test.wav')) 32 self.fs = aksyfs.AksyFS(z48) 41 self.fs = aksyfs.AksyFS() 42 self.fs.init_sampler(z48) 33 43 self.fs.getattr('/') 34 44 self.fs.getattr('/disks') … … 37 47 def test_getattr(self): 38 48 info = self.fs.getattr('/') 39 self. assertTrue(S_ISDIR(info[ST_MODE]))49 self._assertdir(info) 40 50 41 51 def test_getattr_unsupported(self): … … 44 54 def test_getattr_memory(self): 45 55 info = self.fs.getattr('/memory/Boo.wav') 46 self.assertTrue(S_ISREG(info [ST_MODE]))56 self.assertTrue(S_ISREG(info.st_mode)) 47 57 48 def test_ getdir(self):58 def test_readdir(self): 49 59 self.fs.getattr('/') 50 root = self.fs. getdir('/')51 self.assert Equals([('memory', 0), ('disks', 0)], root)60 root = self.fs.readdir('/', 0) 61 self.assert_dirlist(('memory', 'disks'), root) 52 62 53 def test_ getdir_memory(self):54 memory = self.fs.getdir('/memory')63 def test_readdir_memory(self): 64 memory = list(self.fs.readdir('/memory', 0)) 55 65 self.assertEquals(102, len(memory)) 56 self.assertEquals( ('Boo.wav', 0), memory[0])66 self.assertEquals('Boo.wav', memory[0].name) 57 67 58 68 def test_getattr_memory_non_existing(self): … … 63 73 self._assertdir(info) 64 74 65 def test_ getdir_rootdisk(self):66 children = self.fs. getdir('/disks')67 self.assert Equals([('Samples disk', 0), ('Cdrom', 0)], children)75 def test_readdir_rootdisk(self): 76 children = self.fs.readdir('/disks', 0) 77 self.assert_dirlist(('Samples disk', 'Cdrom',), children) 68 78 69 def test_ getdir_with_spaces(self):79 def test_readdir_with_spaces(self): 70 80 info = self.fs.getattr('/disks/Cdrom') 71 self.assert Equals([('Mellotron Samples', 0)], self.fs.getdir('/disks/Cdrom'))81 self.assert_dirlist(('Mellotron Samples',), self.fs.readdir('/disks/Cdrom', 0)) 72 82 73 def test_ getdir_disk(self):83 def test_readdir_disk(self): 74 84 info = self.fs.getattr('/disks/Samples disk') 75 85 self._assertdir(info) 76 children = self.fs. getdir('/disks/Samples disk')77 self.assert Equals([('Autoload', 0), ('Songs', 0)], children)86 children = self.fs.readdir('/disks/Samples disk', 0) 87 self.assert_dirlist(('Autoload', 'Songs'), children) 78 88 89 def test_readdir_disk_nested(self): 79 90 self.fs.getattr('/disks/Cdrom') 80 91 info = self.fs.getattr('/disks/Cdrom/Mellotron Samples') 81 children = self.fs.getdir('/disks/Cdrom/Mellotron Samples') 82 self.assertEquals([('Choir', 0), ('A Sample.AKP', 0), ('Sample.wav', 0)], 83 children) 92 children = self.fs.readdir('/disks/Cdrom/Mellotron Samples', 0) 93 self.assert_dirlist(('Choir','A Sample.AKP','Sample.wav'), children) 84 94 95 def assert_dirlist(self, expected, actual): 96 actual = [entry.name for entry in actual] 97 self.assertEquals(tuple(expected), tuple(actual)) 98 85 99 def test_mkdir_unsupported(self): 86 100 self.assertRaises(OSError, self.fs.mkdir, '/memory/subdir', 'mode_ignored') … … 93 107 self.fs.getattr('/disks/Samples disk') 94 108 self.fs.getattr('/disks/Samples disk/Songs') 95 self.assert Equals([], self.fs.getdir('/disks/Samples disk/Songs'))109 self.assert_dirlist([], self.fs.readdir('/disks/Samples disk/Songs', 0)) 96 110 newdir = '/disks/Samples disk/Songs/test' 97 111 self.fs.mkdir(newdir, 'mode_ignored') 98 self.assert Equals([('test', 0)],99 self.fs. getdir('/disks/Samples disk/Songs'))112 self.assert_dirlist(('test',), 113 self.fs.readdir('/disks/Samples disk/Songs', 0)) 100 114 self.assertNotEquals(None, self.fs.getattr(newdir)) 101 102 def test_open_memory(self):103 path = '/memory/Sample99.wav'104 try:105 self.fs.open(path, S_IRUSR)106 info = self.fs.root.file_cache[path]107 self.assertEquals(os.stat(testutil.get_test_resource('test.wav'))[ST_SIZE],108 os.fstat(info.get_handle())[ST_SIZE])109 finally:110 self.fs.release(path, 'ignored')111 self.assertEquals(None, self.fs.root.file_cache.get(path, None))112 113 115 114 116 def test_open_disk(self): … … 116 118 self.fs.getattr('/disks/Cdrom/Mellotron Samples') 117 119 self.fs.getattr('/disks/Cdrom/Mellotron Samples/Choir') 118 self.fs.open('/disks/Cdrom/Mellotron Samples/Choir/Choir.AKP', S_IRUSR) 119 self.fs.release('/disks/Cdrom/Mellotron Samples/Choir/Choir.AKP', 'ignored') 120 121 def test_release(self): 122 # should not throw 123 self.fs.release('/non-existent', 'ignored') 120 afile = aksyfs.AksyFile('/disks/Cdrom/Mellotron Samples/Choir/Choir.AKP', S_IRUSR) 121 afile.release('ignored') 124 122 125 123 def test_read(self): 126 tmp_file = self._prep_file()124 afile = aksyfs.AksyFile('/memory/Sample100.wav', os.O_RDONLY|S_IRUSR) 127 125 try: 128 read = self.fs.read('file', 10, 0)129 self.assertEquals(' abc' + aksyfs.EOF, read)126 read = afile.read(4, 0) 127 self.assertEquals('RIFF', read) 130 128 finally: 131 os.close(tmp_file)129 afile.release('ignored') 132 130 133 131 def test_mknod_write(self): 134 132 path = '/memory/Sample100.wav' 135 133 self.fs.mknod(path, 0, 'ignored') 136 self.fs.open('/memory/Sample100.wav',S_IRUSR)137 self.fs.write(path,'abc', 0)138 self.fs.release(path,'ignored')134 afile = aksyfs.AksyFile('/memory/Sample100.wav', os.O_WRONLY|S_IRUSR) 135 afile.write('abc', 0) 136 afile.release('ignored') 139 137 written = os.open(aksyfs._create_cache_path(path), os.O_RDONLY) 140 138 try: … … 167 165 self.assertRaises(OSError, self.fs.getattr, path) 168 166 169 def _prep_file(self):170 tmp_file = tempfile.mkstemp('aksy_test')[0]171 os.write(tmp_file, 'abc')172 self.fs.root.file_cache['file'] = aksyfs.FileInfo('/memory/sample.wav',173 False, handle=tmp_file)174 return tmp_file175 176 167 def test_suite(): 177 168 testloader = TestLoader()
