Python en tu filesystem: Creando filesystems en userspace con Python y Fuse
|
|
Primero que nada veamos que es un filesystem.
Una forma simple de verlo, seria un método de guardar datos y organizarlos de una forma que sea fácil encontrarlos y accederlos, estos se representan como directorios y archivos, estos usualmente se almacenan en dispositivos como puede ser un disco rígido o una memoria.
- Algunos ejemplos conocidos:
¿Que es Fuse?
Fuse (http://fuse.sourceforge.net/) es un modulo de kernel, como podría ser cualquiera de los nombrados anteriormente, pero que en vez de implementar un filesystem, nos expone su API en espacio de usuario. Pero... ¿Qué sistemas operativos puedo usar fuse?
- En la mayor parte de los sistemas operativos *nix, como puede ser GNU/Linux, MacOS, *bsd..
- En Windows existe una implementación de fuse llamada "Dokan", no puedo comentar mucho sobre esta, ya que no la he probado.
Algunos ejemplos de uso de fuse
- En Python:
- fusql
- Gmailfs
- Youtubefs
- Cuevanafs
- FatFuse
- Otros lenguajes:
- gnomeVFS2
- zfs-fuse
- sshfs
- etc..
Como podemos ver, podemos crear desde un filesystem mapeando recursos de internet/red, hasta filesystem tradicionales como puede ser zfs(muy popular en solaris) o fat.
Fusql es un fs fuera de lo común, lo que lo hace interesante es que mapea una base de datos relacional(Sqlite) como si fuera un filesystem, pudiéndose operar sobre ella completamente.
Las ventajas que tenemos al desarrollar un filesystem con Fuse son:
- Podemos programar en nuestro lenguaje favorito (en este caso python).
- Simplemente reiniciando la aplicación, estaremos haciendo pruebas con el codigo actualizado.
- Podemos acceder a las librerías del sistema para crearlos, por ej el stdlib de python.
- No tendremos que lidear con kernel panics, reinicios, utilización de maquinas virtuales para las pruebas, etc.
- Mayor portabilidad, ya que fuse existe en diversos sistemas operativos.
- Podemos ejecutar nuestros filesystem con permisos de usuario.
- Facil debugging de los mismos.
Fuse: El API
El API de fuse funciona por callbacks. Por ejemplo: cuando nosotros accedemos a un directorio, en nuestra aplicación se llama a getattr, opendir, readdir, releasedir.
create(path, mode) # creación de un archivo truncate(path, mode) # achicar o agrandar el tamaño de un archivo open(path, mode) # apertura de un archivoError: BadDrawable write(path, data, offset) # escritura de un archivo read(data, lenght, offset) # lectura de un archivo release(path) # liberación de un archivo fsync(path) # sincronización de un archivo chmod(path, mode) # cambio de permisos chown(path, uid, gid) # cambio de propietario mkdir(path, mode) # creación de directorio unlink(path) # eliminación de un archivolink rmdir(path) # eliminacón de un directorio rename(opath, npath) # renombrado link(srcpath, dstpath) # creación de un link
Como se usa
Este es un ejemplo mínimo de lectura y escritura de un archivo, hagamos de cuenta que estos métodos están en un objeto que tiene un diccionario llamado items con el path como key y los datos de este como valor
# lectura def read(self, path, offset, length): # establecemos el comienzo de nuestra lectura start - offset # establecemos el fin de la lectura end - start + length # retornamos la cantidad de datos solicitado return self.items[path][start:end] # escritura def write(self, path, offset, data): # tomamos el tamaño de los datos a escribir length - len(data) # tomamos los datos actuales de nuestro archivo item_data - self.items[path] # agregamos/reemplazamos la parte del archivo que se solicito item_data - itdat[:offset] + data + item[offset+length:] # remplazamos el contenido de nuestro item self.items[path] - item_data # devolvemos el tamaño de datos que escribimos return length # truncate def truncate(self, path, length): # tomamos los datos de nuestro archivo item_data - self.items[path] If len(item_data) > length: # si el tamaño de nuestros datos es mayor que el solicitado # lo acortamos a este self.items[path] - item_data[:length] else: # sino rellenamos con 0 al tamaño solicitado self.items[path] +- '0' * len(item_data)
Defuse
Trabajando con python-fuse, una de las cosas que me parecieron incomodas es el manejo de los path, viniendo del mundo de las webs, sobre todo con werkzeug/flask, se me ocurrió implementar algo similar a su manejo de rutas, pero para la escritura de un filesystem, así es como nació defuse https://github.com/Roger/defuse.
Lo que esto nos permite es utilizar decoradores para el manejo de las rutas, separando cada parte de nuestro filesystem como una clase con todos los métodos que nos proporciona fuse.
Un pequeño ejemplo
fs - FS.get() @fs.route('/') class Root(object): def __init__(self): root_mode - S_IRUSR|S_IXUSR|S_IWUSR|S_IRGRP|S_IXGRP|S_IXOTH|S_IROTH self.dir_metadata - BaseMetadata(root_mode, True) def getattr(self, *args): return self.dir_metadata def readdir(self, *args): for i in xrange(4): yield fuse.Direntry('test%s.txt' % i) @fs.route('/<filename>.<ext>') class Files(object): def __init__(self): file_mode - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH self.file_metadata - BaseMetadata(file_mode, False) def getattr(self, filename, ext): self.file_metadata.st_size - len(filename*4) return self.file_metadata def read(self, size, offset, filename, ext): data - filename * 4 return data[offset:size+offset]
En el ejemplo anterior podemos ver una implementación funcional de un filesystem que muestra 4 archivos, cuyo contenido es el nombre del archivo repetido otras 4 veces.
Como pueden ver el manejo de los path se hace mediante el uso de decoradores de clase, además de que en cada método, ya no nos llega el path, sino que se agrega a estos las variables que definimos en el decorador.
Por ej: @fs.route('/<dir1>/<dir2>/<archivo>.<ext>') en /root//subdir/test.py nos devolvería las variables:
- dir1-'root'
- dir2-'subdir'
- archivo-'test'
- ext-'py'
Conclusión
Este articulo mas que enseñarles todo acerca de python-fuse, lo que quiere es mostrarles lo simple que es utilizarlo y entusiasmarlos a que escriban sus propios filesystems.
En el pyday hubo algunas ideas interesantes, como traductores automáticos de archivos, o análisis con nltk.
Espero ver pronto sus filesystems!
Help PET: Donate
blog comments powered by Disqus