Changeset 1226

Show
Ignore:
Timestamp:
06/08/07 08:46:24 (1 year ago)
Author:
walco
Message:

* rewrote AksyFS to fuse-python 0.2 API

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/src/aksyfuse/aksyfs.py

    r1225 r1226  
    1515from aksy.devices.akai import sampler as samplermod 
    1616 
    17 fuse.fuse_python_api = (0, 1) # TODO: migrate to 0.2 
     17fuse.fuse_python_api = (0, 2) 
     18 
     19fuse.feature_assert('stateful_files') 
    1820 
    1921MAX_FILE_SIZE_SAMPLE = 512 * 1024 * 1024 # 512 MB 
    2022MAX_FILE_SIZE_OTHER = 16 * 1024 # 16K 
    21 EOF = '\x00' 
    2223START_TIME = time() 
    2324 
     25class 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 
     48class 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 
     53class 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         
    2466def stat_inode(mode, size, child_count, uid, gid, writable=False, is_modified=False): 
    2567    info = [None] * 10 
     
    4183    return info 
    4284 
    43 def stat_dir(uid, gid, writable=True, child_count=1): 
     85def to_remove_stat_dir(uid, gid, writable=True, child_count=1): 
    4486    return stat_inode(stat.S_IFDIR|stat.S_IEXEC, 4096L, child_count, uid, gid, writable) 
    4587 
    4688# TODO: provide real values for samples (other sizes can't be determined 
    47 def stat_file(uid, gid, path, size=None, is_modified=False): 
     89def to_remove_stat_file(uid, gid, path, size=None, is_modified=False): 
    4890    cache_path = _get_cache_path(path) 
    4991    if size is None: 
     
    5698    return stat_inode(stat.S_IFREG|stat.S_IWUSR, size, 0, uid, gid, is_modified) 
    5799 
    58 def is_modified(stat_tuple): 
    59     return stat_tuple[stat.ST_MTIME] > START_TIME 
     100def is_modified(stat_info): 
     101    return stat_info.st_mtime > START_TIME 
    60102 
    61103def _splitpath(path): 
     
    75117    return cache_path 
    76118 
    77 class FileInfo(object): 
    78     def __init__(self, path, upload, handle=None, flags=None): 
     119class 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) 
    79128        location = _splitpath(path)[0] 
    80129        if location == "memory": 
     
    84133        else: 
    85134            raiseException(errno.ENOENT) 
    86          
    87         self.name = os.path.basename(path) 
     135 
    88136        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 
    96161    def get_name(self): 
    97162        return self.name 
     
    100165        return self.path 
    101166     
     167    def is_upload(self): 
     168        return self.upload 
     169 
    102170    def get_handle(self): 
    103171        return self.handle 
    104172     
    105     def is_upload(self): 
    106         return self.upload 
    107      
    108173    def get_location(self): 
    109174        return self.location 
     175 
     176 
     177class 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 
    110188 
    111189class FSRoot(model.Container): 
    112190    def __init__(self, sampler): 
    113191        self.sampler = sampler 
    114         self.file_cache = {}  
    115192         
    116193    def is_writable(self): 
     
    119196    def get_children(self): 
    120197        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 exceptions 
    135                     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 + EOF 
    153         else: 
    154             return read 
    155  
    156     def statfs(self): 
    157         blocks_size = 1024 
    158         blocks = self.sampler.systemtools.get_wave_mem_size() 
    159         blocks_free = self.sampler.systemtools.get_free_wave_mem_size() 
    160         files = 0 
    161         files_free = 0 
    162         blocks_available = blocks_free 
    163         namelen = 80 
    164         return (blocks_size, blocks, blocks_free, blocks_available, files, files_free, namelen) 
    165198     
    166199class 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 
    176203        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     
    180210    def stat_directory(self, path): 
    181211        folder = self.cache.get(path) 
     
    185215                raiseException(errno.ENOENT) 
    186216            self.cache[path] = folder 
    187         return stat_dir(self.uid, self.gid, folder.is_writable()) 
     217        return DirStatInfo(folder.is_writable()) 
    188218 
    189219    def get_parent(self, path): 
     
    208238            return cached 
    209239        if _cache_path_exists(path): 
    210             return os.stat(_create_cache_path(path)) 
     240            return os.lstat(_create_cache_path(path)) 
    211241         
    212242        file_obj = self.get_file(path) 
     
    214244            raiseException(errno.ENOENT) 
    215245        else: 
    216             return stat_file(self.uid, self.gid, path, file_obj.get_size(), False) 
     246            return FileStatInfo(path, file_obj.get_size(), False) 
    217247 
    218248    def getattr(self, path): 
     
    223253            return self.stat_file(path) 
    224254 
    225     def getdir(self, path): 
    226         print '*** getdir', path 
     255    def readdir(self, path, offset): 
     256        print '*** readdir', path, offset 
    227257        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()) 
    229260 
    230261    def mkdir(self, path, mode): 
     
    238269        child = folder.create_folder(os.path.basename(path)) 
    239270        self.cache[path] = child 
    240  
    241     def open(self, path, flags): 
    242         print '*** open', path, flags 
    243         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, offset 
    248         return self.root.read(path, length, offset) 
    249  
    250     def release(self, path, flags): 
    251         print '*** release', path, flags 
    252         self.root.close(path) 
    253271 
    254272    def rename(self, old_path, new_path): 
     
    296314    def mknod(self, path, mode, dev): 
    297315        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) 
    300317        self.get_parent(path).refresh() 
    301  
    302     def write(self, path, buf, offset): 
    303         print '*** write', path, len(buf), offset 
    304         return self.root.write(path, buf, offset) 
    305318 
    306319    def truncate(self, path, size): #IGNORE:W0212 
     
    310323    def statfs(self): 
    311324        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) 
    313338 
    314339def raiseUnsupportedOperationException(): 
     
    319344 
    320345if __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 = """ 
     348Aksyfs: 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) 
    328363        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')
    331366    else: 
    332         sampler = Devices.get_instance(sampler_id, 'usb') 
    333     fs = AksyFS(sampler) 
     367        sampler = Devices.get_instance(fs.sampler_id, 'usb') 
    334368    try: 
    335         fs.main(
     369        fs.main(sampler
    336370    finally: 
    337         sampler.close() 
     371        fs.sampler.close() 
  • trunk/src/aksyfuse/tests/test_aksyfs.py

    r1221 r1226  
    1010log = logging.getLogger('aksy') 
    1111 
    12 class TestModuleTest(TestCase): 
     12class 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 
     21class TestDirStatInfo(TestCase): 
    1322    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)) 
    1725 
     26class TestFileStatInfo(TestCase): 
    1827    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.assertEquals(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         
    2433class AksyFSTest(TestCase): #IGNORE:R0904 
    2534    def _assertdir(self, info): 
    26         self.assertTrue(S_ISDIR(info[ST_MODE])) 
     35        self.assertTrue(S_ISDIR(info.st_mode)) 
    2736         
    2837    def setUp(self): 
     
    3039                                   debug=0,  
    3140                                   sampleFile=testutil.get_test_resource('test.wav')) 
    32         self.fs = aksyfs.AksyFS(z48) 
     41        self.fs = aksyfs.AksyFS() 
     42        self.fs.init_sampler(z48) 
    3343        self.fs.getattr('/') 
    3444        self.fs.getattr('/disks') 
     
    3747    def test_getattr(self): 
    3848        info = self.fs.getattr('/') 
    39         self.assertTrue(S_ISDIR(info[ST_MODE])
     49        self._assertdir(info
    4050     
    4151    def test_getattr_unsupported(self): 
     
    4454    def test_getattr_memory(self): 
    4555        info = self.fs.getattr('/memory/Boo.wav') 
    46         self.assertTrue(S_ISREG(info[ST_MODE])) 
     56        self.assertTrue(S_ISREG(info.st_mode)) 
    4757         
    48     def test_getdir(self): 
     58    def test_readdir(self): 
    4959        self.fs.getattr('/') 
    50         root = self.fs.getdir('/'
    51         self.assertEquals([('memory', 0), ('disks', 0)], root) 
     60        root = self.fs.readdir('/', 0
     61        self.assert_dirlist(('memory', 'disks'), root) 
    5262 
    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)
    5565        self.assertEquals(102, len(memory)) 
    56         self.assertEquals(('Boo.wav', 0), memory[0]
     66        self.assertEquals('Boo.wav', memory[0].name
    5767 
    5868    def test_getattr_memory_non_existing(self): 
     
    6373        self._assertdir(info) 
    6474 
    65     def test_getdir_rootdisk(self): 
    66         children = self.fs.getdir('/disks'
    67         self.assertEquals([('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) 
    6878 
    69     def test_getdir_with_spaces(self): 
     79    def test_readdir_with_spaces(self): 
    7080        info  = self.fs.getattr('/disks/Cdrom') 
    71         self.assertEquals([('Mellotron Samples', 0)], self.fs.getdir('/disks/Cdrom')) 
     81        self.assert_dirlist(('Mellotron Samples',), self.fs.readdir('/disks/Cdrom', 0)) 
    7282         
    73     def test_getdir_disk(self): 
     83    def test_readdir_disk(self): 
    7484        info  = self.fs.getattr('/disks/Samples disk') 
    7585        self._assertdir(info) 
    76         children = self.fs.getdir('/disks/Samples disk'
    77         self.assertEquals([('Autoload', 0), ('Songs', 0)], children) 
     86        children = self.fs.readdir('/disks/Samples disk', 0
     87        self.assert_dirlist(('Autoload', 'Songs'), children) 
    7888 
     89    def test_readdir_disk_nested(self): 
    7990        self.fs.getattr('/disks/Cdrom') 
    8091        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) 
    8494 
     95    def assert_dirlist(self, expected, actual): 
     96        actual = [entry.name for entry in actual] 
     97        self.assertEquals(tuple(expected), tuple(actual)) 
     98             
    8599    def test_mkdir_unsupported(self): 
    86100        self.assertRaises(OSError, self.fs.mkdir, '/memory/subdir', 'mode_ignored') 
     
    93107        self.fs.getattr('/disks/Samples disk') 
    94108        self.fs.getattr('/disks/Samples disk/Songs') 
    95         self.assertEquals([], self.fs.getdir('/disks/Samples disk/Songs')) 
     109        self.assert_dirlist([], self.fs.readdir('/disks/Samples disk/Songs', 0)) 
    96110        newdir = '/disks/Samples disk/Songs/test' 
    97111        self.fs.mkdir(newdir, 'mode_ignored') 
    98         self.assertEquals([('test', 0)],  
    99                           self.fs.getdir('/disks/Samples disk/Songs')) 
     112        self.assert_dirlist(('test',),  
     113                          self.fs.readdir('/disks/Samples disk/Songs', 0)) 
    100114        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  
    113115 
    114116    def test_open_disk(self): 
     
    116118        self.fs.getattr('/disks/Cdrom/Mellotron Samples') 
    117119        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') 
    124122 
    125123    def test_read(self): 
    126         tmp_file = self._prep_file(
     124        afile = aksyfs.AksyFile('/memory/Sample100.wav', os.O_RDONLY|S_IRUSR
    127125        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) 
    130128        finally: 
    131             os.close(tmp_file
     129            afile.release('ignored'
    132130 
    133131    def test_mknod_write(self): 
    134132        path = '/memory/Sample100.wav' 
    135133        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') 
    139137        written = os.open(aksyfs._create_cache_path(path), os.O_RDONLY) 
    140138        try: 
     
    167165        self.assertRaises(OSError, self.fs.getattr, path) 
    168166     
    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_file 
    175  
    176167def test_suite(): 
    177168    testloader = TestLoader()