Entrega Diciembre
Table of Contents

Planificación de fase

Table of Contents

Planificación de fase

Hitos principales:

  • Octubre: Definición del proyecto, requisitos, casos de uso, riesgos

Al terminar octubre tenemos una idea general de cómo será nuestra aplicación: funcionalidades básicas, planificación sobre cómo llevarla a cabo, límites de hardware, de dispositivos, etc. En definitiva, el ámbito general del proyecto.

  • Noviembre: Investigación principal de tecnologías

Habremos decidido qué lenguaje de programación utilizaremos, al menos para el núcleo del programa, así como las tecnologías de cada uno de los módulos del proyecto, o al menos varias opciones de implementación, para poder elegir más tarde con cual quedarse. Las tecnologías más importantes a investigar son: entorno gráfico que usaremos, tecnologías para el núcleo de copia de seguridad, de base de datos, y de comunicación interna y externa.

  • Diciembre: Prototipo Alpha (prueba de integración)

Se tendrá el núcleo de la aplicación, o al menos ciertos módulos para comenzar a integrar. De este modo tendremos el esqueleto de la arquitectura, con las principales tecnologías investigadas por completo e implementadas para más adelante continuar con funcionalidades menos críticas.

  • Enero: Fase de investigación; procedimiento de integración cerrado.

Subsiguiente prototipo de investigación de tecnologías (comunicación frontend / backend, planificación de tareas), si bien carente de la crítica de backup, restore, eliminación de copias, etc. Finalización del proceso de integración de bases de código distintas, quedando preparadas las metodologias para las fases de desarrollo posteriores.
Los principales riesgos tecnológicos están ya localizados. La arquitectura se ha desarrollado hasta quedar prácticamente cerrada, a falta de algunas apis importantes.

  • Febrero: Repaso de documentación e investigación sobre funcionalidades adicionales

Esta iteración se centrará en documentar los nuevos procesos desarrollados y completar la documentación existente, pero dada la falta de tiempo por los exámenes, se reduce la cantidad de tiempo para el proyecto, con lo que no se esperan grandes avances. Se dedica a la investigación también porque, en nuestra experiencia, los exámenes son época de trastear con las más extrañas tecnologías en los ratos muertos que se debieran aprovechar para estudiar.

  • Marzo: Prototipo Beta (funcionalidad crítica). Replanificación y reevaluación de viabilidad. Reasignación de personal.

Eliminados los riesgos tecnológicos, es necesario replantearse la planificación y la viabilidad de los casos de uso menos importantes para el tiempo existente. Se deberá concluir la construcción de la aplicación y sus funcionalidades críticas, con vistas a poder comenzar cuanto antes el mantenimiento y testeo de esta funcionalidad, que se preve largo; en los tres meses desde Enero la arquitectura está más que fijada y desarrollar debería ser previsible en tiempo y esfuerzo, al contar con prototipos funcionales de todas las tecnologías. Se tratará de avanzar en funcionalidad menos importante (al menos estudiando su viabilidad).
La semana santa dificultará la planificación, en función del personal y su disponibilidad por viajes, prácticas, etc.
Además, se debe tratar de completar lo más posible la documentación, en particular todos los tipos de diagramas UML utilizables en nuestro proyecto, así como identificar patrones de diseño tanto preexistentes en nuestro código pero no explicitados, como susceptibles de ser aplicados.

  • Abril: Beta 2 (funcionalidad decidida completa). Mantenimiento, pruebas de usabilidad con usuario real, optimización en general, pruebas de empaquetado para distribuir.

Completar la funcionalidad ausente en la anterior iteración. Asimismo, preparar el despliegue de la aplicación, y iniciar un proceso de prueba intensiva, lo más automatizada que sea posible y razonable; consumido el margen de tiempo, se debe tratar de testear lo más posible durante el desarrollo de la aplicación -y no exclusivamente en Abril- para conseguir la máxima fiabilidad del programa, lo que es importante para su éxito.

  • Mayo: Release: Empaquetar, y preparar la entrega (diapositivas, vídeo, web, presentación…). Mejorar la calidad todo lo que de tiempo (que será poco por la proximidad de los exámenes).

Perfil de personal

Adri

Administrador de sistemas y hábil en general. Autogestionado. Encargado de la gestión de sistemas del infierno, si se lo propone. Si es fácil, no es para él… C++ -er. Analista en general. Entusiasta donde los haya. Sincero ;). Buen gestor de personal; manejo del látigo eficaz, en particular del de 9 colas, lo que le convierte en el jefe de release ideal.

Carlos

Programador por encargo. Viciado en general. Viajero incansable.

Daniel

Primera baja del proyecto. M.I.A.

David

Databaser. Autogestionado. Programador por encargo bastante rápido; trabaja bajo presión. Asiduo del copy-paste (algo dadaista).

Diana

Analiza tus frases y suelta verdades como puños. Gran consejera. Programadora por encargo.

Eze

Gestor de todo lo gestionable. Maestro en tecnologías varias habidas y por haber. Hijo del bleeding edge (si funciona, actualiza). Capitán diccionario. Analista en general. Arquitecto (le falta la barba blanca solo). Entusiasta pero muy limitado en tiempo.

Fede

Líder nato y relaciones públicas. GNU master. Analista en general. Mejora la moral de la tropa. Hospitalario ^^. Hermano de Eze (por aquello de que también es hijo del bleeding edge). Fuente incansable de ideas a añadir a la apretada agenda.

Jorge

Autogestionado. Programador altamente competente. Tú fast, tú furious.

Mario

Redacta diversos tipos de documentos, observando escrupulosamente las reglas de redacción y ortografía (teniente diccionario, digamos). Programador por encargo. Relajado (paz, hermano).

Rober

Programador por encargo. Mejora notablemente la moral de la tropa. Formateador de cerebros. Currante nato.

Salva

Analista en general. Lead developer estilo ninja (no hace ruido, nadie le ve, pero aparecen sus líneas de código de repente). Master (del universo, digamos). Competente como programador. Entusiasta del proyecto, pero limitado en tiempo. Altamente experimentado. En ocasiones desvaría y hay que meterle en cintura ;) (por ejemplo, con el citado látigo de 9 colas).

Tabas

Programador por encargo. Dbusman y showman en general. Caja de sorprrresas.

Carmen

El nuevo fichaje. Programadora por encargo. Escritora hipermaniática. Friki en sus ratos libres (el resto lo es a tiempo completo)


Planificación iteración Enero

Tipo de iteración

Elaboración


Objetivos generales

Aprovechando que, pese a las fiestas, la iteración de Enero es la más larga (una semana extra) y que en vacaciones hay más tiempo en general, pretendemos llegar al primer prototipo funcional completo de la arquitectura y con ello cerrar la fase de elaboración. Este prototipo debe incluir las funcionalidades mínimas de guardar backups, recuperar backups y listar los presentes mediante un interfaz gráfica además de la de consola; posiblemente se incluya también borrar snapshots, importante para mitigar el impacto en uso de disco de nuestra aplicación.

Además pretendemos fijar con ello la arquitectura del sistema para poder desarrollar el resto de funcionalidades, así como poder repartir mejor el trabajo desde ese momento (ya que, fijadas las interfaces, es más sencillo repartir las clases).

Por último, y como parte del objetivo de desarrollar un interfaz útil, es necesario comenzar el manual de usuario.


Primer prototipo funcional

Para conseguir este objetivo concreto actuaremos en dos frentes: por un lado, concluir una interfaz de usuario funcional, y por el otro reducir al mínimo el riesgo del núcleo de la aplicación (los backups).

Solucionar el riesgo de rdiff-backup

En la iteración anterior se localizaron problemas en este sistema de almacenamiento que suponen un bloqueo para continuar completando la funcionalidad de la aplicación (ver Apéndice A del informe de investigación de snapshot core). Este riesgo es de máxima prioridad para nuestra aplicación y debe ser resuelto en esta iteración, bien completando de algún modo la funcionalidad que nos falta en rdiff-backup para usarla bajo la nuestra, o bien desarrollando nuestro propio sistema de snapshots (lo que aumentará el riesgo mientras se finaliza, así como el tiempo de desarrollo y de comprobación de errores, que debe ser exhaustiva).

Fijar la arquitectura del sistema

Con vistas a poder finalizar la fase de elaboración lo antes posible, intentaremos en esta iteración consensuar una arquitectura del sistema lo bastante sólida y flexible como para cubrir nuestros casos de uso, en base a las interacciones que describimos en el brainstorming y las consiguientes clases y sus interacciones. Será necesario actualizar los documentos de arquitectura existentes, así como describirla en UML. Contamos no obstante con que las necesidades del desarrollo obligarán probablemente a varias revisiones de la arquitectura, quizá prolongándose otra iteración más.

Además, crear un interfaz y probar su usabilidad nos obligará a definir con más detalle la estructura de clases del GUI, por ahora relegada a un segundo plano.

Como ejemplo de la necesidad de este trabajo, la funcionalidad deseada de la clase Watcher aún no está reunida, sino que está distribuida por otros módulos que aún no se ajustan a la arquitectura deseada (por facilidad del desarrollo del prototipo). Se busca en suma evitar este tipo de problemas, que a medio plazo nos pueden limitar mucho y dificultar el trabajo.

UML

Según lo explicado en clase se modelará la arquitectura de clases y sus interacciones mediante diagramas UML. Para ello es necesario familiarizarse con las herramientas, así como localizar aquellas que permitan un flujo de trabajo bidireccional entre código en python y diagramas, para poder reducir el tiempo invertido. Todo ello implicará crear nuevos documentos donde se aclare al equipo el funcionamiento de los programas, así como probablemente modificaciones a los procesos establecidos de trabajo y los estándares, para poder facilitar la integración de código posterior y mejorar el reparto de trabajo, uno de los problemas importantes que se detectaron en la anterior iteración.

Explicitar prioridades de casos de uso

Con vistas a conseguir el prototipo en el tiempo que tenemos, es necesario partir los casos de uso lo más posible en subtareas, y priorizarlas para buscar el camino crítico sin el cual no habrá prototipo alguno. Una vez se tenga explicitado, se debe priorizar el desarrollo de los componentes de ese camino crítico. La importancia de los casos de uso es algo medianamente consensuado en el equipo, pero que aún no ha sido explicitado. En una estimación previa, los componentes sin desarrollar y por investigar más importantes son:

Finalizar gestión de preferencias

La configuración del programa está en una fase avanzada de desarrollo, pero aún no es funcional y probablemente deberá sufrir rediseños en función de problemas de usabilidad que se detecten. Sin embargo, una interfaz mínima que funcione para configurar el programa es importante, y será lo primero que se termine por estar ya muy avanzado.

Tabla de historia

Es una tabla a donde se migra la información de los backups desde el journal (que a su vez es donde se anota qué operaciones hay que hacer cuanto antes, para poder planificarlas y reiniciar transacciones en caso de fallo). Esta tabla necesita información adicional, como dónde se encuentra un archivo (no necesariamente local), su propietario y otros atributos (tamaño, permisos, etc). Toda esa información adicional, que debemos aún concretar, no es crítica para el prototipo pero sí para la funcionalidad de exportar backups, sea a CD o a disco duro externo, por lo que el diseño debe tenerla en cuenta y merece la pena una implementación preliminar de la misma.

Comunicación frontend / backend

Por ahora backend y frontend han seguido desarrollos separados, solo integrándose parcialmente para la demostración de tecnologías de la iteración de diciembre. Es necesario desarrollar la comunicación entre los mismos y también el API, a fin de que ambos equipos de desarrollo puedan probar su código teniendo en mente las necesidades y posibilidades de la aplicación completa, además de que hace falta para completar el prototipo sobre la arquitectura deseada. Para esta parte se utilizará la tecnología dbus de comunicación interproceso, ya investigada parcialmente pero aún sin prototipo funcional; por tanto es también prioritario.

Interfaz de navegación de archivos

Dado que el GUI ya tiene una interfaz de preferencias casi funcional, el siguiente paso necesario para poder demostrar el programa es una interfaz que muestre los archivos con copia de seguridad y permita navegar entre ellos, así como restaurarlos. Ello requiere del anterior punto para usar el backend real y consensuar cómo comunicarse con él, así como de cierto tiempo de desarrollo para terminar de dominar los componentes del toolkit gráfico GTK.

Integración con nautilus

Una de los requisitos funcionales es que la aplicación sea usable para cualquiera, lo que requiere que esté integrada de modo transparente en el uso habitual del equipo. Para ello se decidió que integraríamos nuestro código con nautilus, el explorador de archivos de GNOME, mediante su API de plugins; no obstante la investigación preliminar ha demostrado que apenas hay documentación del mismo. Por ser menos prioritario, se relega el desarrollo de la integración a Febrero.

Integración con Beagle

Beagle permitiría buscar dentro del contenido almacenado en nuestros backups, facilitando mucho el uso del programa e integrándolo aún más. No obstante el riesgo de este desarrollo es mucho menor, requiere en potencia mucho más tiempo y además depende de finalizar el modelo de almacenamiento final de nuestra aplicación, por lo que también queda relegado indefinidamente hasta que se juzgue adecuado.

Tecnología HAL

La tecnología HAL permite detectar dispositivos conectados al equipo y podría ser potencialmente útil para facilitar el uso de un disco duro externo como almacenamiento. Su investigación también queda relegada a una fase posterior, una vez el prototipo permita esa funcionalidad de externalizar la copia de seguridad.

Estimar requisitos de tiempo y espacio de la aplicación

A petición del cliente, trataremos de realizar una estimación del espacio y tiempo que usará la aplicación, que posteriormente iremos refinando conforme se optimice y se concrete más el código.

Nueva documentación.

Algunos de los documentos por escribir que se consideran necesarios para el trabajo son:

  1. Guidelines de código en Python: de cara a la integración. Cómo probar el código, dónde colocar todo, etc.
  2. Manual de integración: una vez se termine de investigar el modo más eficaz de usar el control de versiones distribuido, se debe informar al equipo de cómo se usa para facilitar el trabajo y permitir mejor integración entre grupos separados de desarrolladores.
  3. Guidelines para errores: desde unas directrices sobre qué información generar para logs del programa, hasta una jerarquía de excepciones que usar consistentemente en todo el programa.
  4. Manual de uso de la aplicación.

Reparto de trabajo

Los objetivos de cada grupo son:

  • Documentación: Actualizar la arquitectura preliminar que tenemos según las Interacciones entre clases. Concretar la arquitectura del GUI y documentarla. En general, mantener el wiki actualizado según las exigencias de entregas de Gervás, y concluir la documentación deseada como objetivos. Ezequiel se responsabiliza, y Adrián ayudará como responsable de la integración en la iteración pasada. Además, de sobrar tiempo Ezequiel comenzará la investigación y el desarrollo de manuales sobre la integración de nautilus.
    • Manual de uso: Como depende del GUI lo escribirán ellos.
  • Watcher: Desarrollo de la clase Watcher e inicio del Planner. Adrián es el responsable y reparte trabajo.
    • Adrián: Concluir Watcher. Comienzos de Planner.
    • Diana: Terminar la funcionalidad de crontab y gestionar las peticiones de anacron. Ayudar en D-Bus Manager.
    • Jorge: Concluir funcionalidad de Inotify handler; refinar funcionalidad.
  • GUI: Carlos, Rober y Tabas. Responsable: Rober. Se ocuparán de concluir un GUI con la funcionalidad crítica desarrollada.
  • BBDD: David. Como previsiblemente habrá poco trabajo y es dinámico según le vayan necesitando, ayudará en documentación y en Snapshot manager.
  • D-Bus Manager: Atender a las necesidades del GUI y distribuir trabajo por el backend (previsiblemente a planner). Diana se responsabiliza.
    • Diana: representante de backend que conoce dónde está cada cosa, y esbozará la clase planner junto con Adrián para repartir trabajo por el backend (inicialmente sin lógica de planificación). Ayudada por Adrián en ello.
    • Rober / Tabas: Una vez investigado el modo de uso de dbus, enviarán las peticiones necesarias y recibirán datos del backend para poder presentarlos al usuario.
  • Snapshot: Salva, Fede, David y Mario hasta que se solucione el riesgo, el más importante actualmente. Responsable: Salva, quien asignará trabajo concreto.
    • Realizar estimaciones de tiempo y espacio del almacenamiento para la documentación.
  • Integración de código: Adrián realizó la integración pasada; una vez aclaremos los procesos Ezequiel y David ayudarán.

Estimaciones de tiempo

  • 31 diciembre (+/- 2 días): Terminado preferencias. Guidelines de integración; resueltos los procesos de tirar código ordenado y útil. Tomarse las uvas.
  • 7 enero: Dejar de jugar con los regalos de los reyes. Watcher. Investigación terminada de dbus con prototipos funcionales; clase dbus manager funcionando para poder hacerle peticiones. Prioridad de desarrollo de los casos de uso establecida (lo que condiciona y modifica el alcance); plan más detallado de desarrollo en el seguimiento.
  • 14 enero: Watcher integrado con inotify, cron. UML. Estimaciones tiempo y espacio. Toda la documentación complementaria salvo posiblemente manual de usuario sujeta a revisión del equipo.
  • 21 enero: Primera integración parcial para detectar problemas en el seguimiento de las guidelines y la arquitectura. Cierre parcial de código. GUI funcionando completo, a falta posiblemente de resolver errores puntuales. Finalizar los objetivos a falta de las previsibles correcciones de errores.
  • 25 enero, 14:00: Cierre total de código, comienzo de integración. Cierre de API obligatorio; bugfix únicamente a través de los integradores.
  • 31 enero: entrega.

Como condicionante importante, en Navidades no todo el mundo podrá trabajar: Salva no tiene ordenador y por tanto tiene dos semanas inhábiles, Ezequiel estará fuera una de las dos semanas y tiene bastante carga de trabajo de otras asignaturas, Mario estará fuera, y en general todo el mundo está ocupado con PLG y recuperándose de los excesos navideños, por lo que no serán dos semanas demasiado productivas. Por otra parte, al no tener horas lectivas ni laborales esto se compensará en gran medida y prevemos que cunda.


Seguimiento

Ver seguimiento-enero.


Seguimiento Diciembre

Objetivos cumplidos

En la iteración se han logrado dos prototipos. Por una parte, una GUI funcional que permite editar las preferencias (aunque aún no leerlas), además de mostrar una ventana de log en el menú View para comprobar que se monitorizan la creación, borrado y modificación de los archivos o carpetas configurados.

Por otra, se ha diseñado una aplicación que, mediante consola, permite realizar backups, listarlos y recuperarlos. Cuando se realiza un backup de algo que no estaba en las preferencias la aplicación añade dicha carpeta a las mismas.

Durante la integración hemos trabajado sobre los problemas de arquitectura y organización de código que deberemos utilizar, lo que nos resultará útil para futuras integraciones. El brainstorming ha ayudado a definir la arquitectura respecto a nuestras estimaciones iniciales, ayudando a tener en mente algunos casos de uso (como Exportar a disco externo importantes que hemos relegado para fases posteriores del desarrollo. Este caso y su simétrico, importar, cubren la importante funcionalidad de poder recuperarse después de un fallo catastrófico (formateo), o de un cambio de equipo, siempre que se conserven copias de seguridad.

La investigación en esta iteración se ha centrado en:

Python

Todos los equipos de desarrollo han debido familiarizarse con el nuevo entorno y bibliotecas; el nuevo lenguaje de programación no ha traído demasiados problemas. Se han puesto de manifiesto algunas carencias en la documentación interna (tutoriales) que deberán ser solventadas; no era posible detectarlo hasta que se realizaran las primeras integraciones y se probara un poco más el lenguaje.

Rdiff-backup

Por decisión común se decidió en la anterior iteración utilizar la aplicación rdiff-backup como medio de gestión de snapshots. Así, el desarrollo se limitaba al diseño de una API general expuesta al resto de módulos y una serie de wrappers que envolviesen el funcionamiento de la aplicación.

La investigación fue corta pero concluyente y se encuentra reflejada en el apéndice I del informe de investigación sobre snapshot-manager.

El desarrollo fue apresurado y difícil debido en primer lugar a la escasez de tiempo disponible por las circunstancias académicas y personales de los desarrolladores. Aun así, la entrega se efectuó a tiempo y la API cubría algunos de los casos de uso críticos expuestos en el documento correspondiente.

Uno de los principales problemas fue causado por el abandono de uno de los integrantes del equipo HD Lorean asignado precisamente a este módulo. Las consecuencias de la marcha de nuestro compañero resultaron en un aumento de la carga de trabajo del otro componente del equipo que se vio desbordado; un riesgo que no podemos mitigar completamente debido a la falta de personal, pero que en adelante mitigaremos mediante rebalanceos más frecuentes del trabajo. Además el análisis de alcance y el reparto de objetivos era incompleto, ya que no estaban explicitadas las prioridades; queda pendiente para la siguiente iteración.

La investigación a fondo de la aplicación ha puesto de manifiesto varios problemas. Por una parte, rdiff-backup genera deltas inversas, a diferencia de lo que creíamos: ello que cambia buena parte de las presuposiciones que hacíamos sobre el programa (tanto en funcionamiento como en requisitos), y a priori dificultaría el borrado de snapshots concretos, una funcionalidad necesaria entre otras para reducir el uso de disco. Además, por estar orientado a una granularidad mayor que la nuestra no ofrece la posibilidad de actualizar un archivo individual en el backup: el escaneo siempre es sobre los tiempos de modificación de la carpeta completa monitorizada, lo que inutiliza inotify y ralentiza el programa respecto al uso que queremos darle. Por otra parte, en el desarrollo de la arquitectura quedó patente que una base de datos que indexe contenido es necesaria aunque se use rdiff-backup debajo, lo cual limita aún más sus ventajas. Queda pendiente la investigación sobre si es posible y merece la pena solucionar estos problemas, o se debe descartar el uso de rdiff-backup y buscar alternativas o construirlas desde cero.

Cron

Administrador regular de procesos en segundo plano (véase demonio) que ejecuta programas a intervalos regulares (por ejemplo, cada minuto, día, semana o mes). En un primer momento se intentó modificar el crontab del usuario, pero para acceder a él se necesitaban privilegios de superusuario. Un requisito de la aplicación es que ésta se pudiera ejecutar como usuario (por ser el usuario medio el objetivo del desarrollo), y no necesariamente como administrador, lo cual suponía un problema.

Linux nos otorga ciertos comandos para poder acceder al crontab de forma segura, sin requerir estos permisos. Entre ellos existe uno que nos lista el contenido del fichero. La solución adoptada es abrir un pipe asociado a la salida de este comando para obtener su contenido, el cual verteremos en un archivo temporal. Este archivo temporal podemos modificarlo a nuestro antojo (manteniendo siempre la sintaxis de cron). Por último actualizaremos el crontab del usuario con este archivo temporal.

Un problema es que cron supone sistemas que están operativos siempre, y por ello si el sistema no esta activo en el momento de ejecutar la tarea, esta simplemente se pierde. Sin embargo, un pc personal suele apagarse y encenderse con cierta frecuencia, por lo que tenemos que buscar algún otro mecanismo para solventar este problema.

Anacron es un programador de tareas similar a cron, con la diferencia de que no necesita que el sistema esté en ejecución. Se puede utilizar para ejecutar los procesos que cron ejecuta normalmente de forma diaria, semanal y mensual. Lo más recomendable es que se utilicen ambos, cron para intervalos pequeños de tiempo, y anacron para intervalos mayores (a partir de un día). Con Anacron no se ha conseguido solucionar el problema de los permisos, por lo que posiblemente, si no se encuentra la manera, haya que decidir que nos interesa más, si usar anacron y ejecutar la aplicación como root o usar cron y que sólo se hagan las copias de seguridad cuando el equipo esté encendido.

Además se investigó fcron, similar a anacron, pero por el momento queda descartado, ya que instalarlo modifica instalaciones por defecto de Ubuntu.

Inotify

hemos descubierto que los límites por defecto se pueden saltar y definir según nuestras necesidades, lo que mitiga el riesgo de que no sirviera o de que dificultara en exceso el desarrollo. La funcionalidad que ofrece este componente está prácticamente concluida.

Formato de ayuda de YELP (ayuda de GNOME)

esta todo en xml y además resulta incómodo a la hora de editar. Sin embargo se ha localizado un editor (Conglomerate) específico para el formato docbook, todavía en desarrollo.

Objetivos no alcanzados

No ha dado tiempo a estabilizar la arquitectura ni a realizar la API; queda como tarea pendiente para la iteración de enero a fin de poder pasar a construcción cuando se concreten las clases y se realice el UML.

Riesgos reducidos
  • La incompatibilidad de horarios ha sido mitigada al establecer como estándar el martes por la tarde para las reuniones, el día que viene "menos mal" a todo el grupo.
  • El riesgo "no llegar a superar inotify" está cubierto e implementado, así como el riesgo que apareció durante la investigación de no tener suficientes handlers.
  • GTK razonablemente superado; ya hay un GUI, pero aún falta funcionalidad por implementar en el mismo.
  • Riesgo de corrupción de backup queda cubierto *siempre y cuando* se utilice rdiff-backup, que implementa checksums.
  • Launchpad reducido, lo hemos usado todos.
  • No lograr funcionar sin internet está superado, dado que dependemos menos del wiki y el control de versiones es distribuído (en última instancia puede funcionar "punto a punto", sin pasar por los servidores de launchpad).
Nuevos riesgos
  • rdiff-backup no cubre los casos de uso, lo que nos aprieta bastante los tiempos de desarrollo y puede suponer un bloqueo para seguir avanzando con la aplicación (mejores interfaces, etc), así como dificulta su desarrollo respecto de la previsión (que era corta porque, en la primera investigación, creímos erróneamente que nos solucionaba todos los casos de uso sin trabajo adicional).
  • Detectar dispositivos externos puede requerir del subsistema HAL, una nueva tecnología a investigar.
  • La integración en bazaar es delicada; hemos comprobado que es posible romper el repositorio por parte de cualquiera. La solución prevista, a falta de comprobar si podemos restringir escrituras dentro del grupo en el servidor, es una política clara de integración: resumiendo, nadie sube a ninguna parte fuera de su grupo de trabajo, y de integrar se encargan personas determinadas. A tal fin se desarrollarán tutoriales de integración y guidelines de desarrollo específicos en python para facilitar la integración.
  • Los exámenes están próximos, quedan bastantes entregas en las otras asignaturas y el problema de falta de tiempo se incrementa. Además hemos sufrido un abandono, lo que reduce aún más el personal disponible y convierte en más crítico otra hipotética baja de media duración. Mucha gente tendrá disponibilidad reducida o nula en vacaciones; para compensar, esta es la iteración más larga (un mes y una semana), y en vacaciones al no haber jornada lectiva ni laborable hay más tiempo para trabajar.
  • No hay api de nautilus; va implícita en el código, y no hay documentación ninguna. La integración con el mismo se complicará bastante por ello.
  • Cómo exportar nuestro sistema de backups aplanado a sistema de ficheros aún no está claro, pues es un problema no trivial en absoluto; además la documentación de FUSE, el sistema que se usaría para ello, es confusa. Como la funcionalidad que nos proporcionaría es principalmente interna este riesgo tiene un impacto bastante leve.
  • Python es un lenguaje interpretado y puede ser muy ineficiente en comparación con otros módulos. No obstante, la optimización queda pendiente para futuras fases de desarrollo, es posible enlazar python con módulos en C y además presumimos que el cuello de botella no estará marcado por tiempo de cpu sino de entrada / salida.
  • Fat32 impone límites al almacenamiento de archivo que se deben cubrir, sea mediante una aplicación externa o mediante un formato de archivo propio. Al ser el formato más común en discos externos (suelen venir preformateados) es crítico que funcione almacenar sobre fat32 si se quieren externalizar las copias. Algunos de esos riesgos son que no se soportan links (ni hard ni soft), ni permisos, ni archivos de más de 4 GB de tamaño. Los riesgos son compartidos en parte por ntfs.
  • Rdiff impone límites en sus funciones internas para paths muy largos; hay que investigar el alcance de ese riesgo y actuar en consecuencia. Los desarrolladores de rdiff-backup parecen tenerlo solucionado no obstante.
  • Las herramientas de modelado UML no suelen tener soporte para python: es escaso a la hora de realizar ingeniería inversa sobre código, y casi nulo para generarlo. Localizadas herramientas que cubren en teoría ambas funcionalidades, queda comprobar hasta qué punto lo hacen y familiarizarse con ellas.
Tiempo real empleado

En general no ha habido un buen reparto del trabajo, lo cual viene motivado en parte porque la investigación implica desconocimiento del tiempo de desarrollo, pero también porque el reparto no era bueno en origen y ha habido cierta desorganización. Para mitigar este problema, según la sugerencia dada en clase trataremos de hacer planificación semanal, mediante tareas atómicas ordenadas según su nivel de importancia y relevancia para el camino crítico, y revisando más frecuentemente el reparto. No obstante esto será difícil hasta la primera iteración de construcción, pues aún queda investigación por realizar. Asimismo se ha propuesto un método de refuerzo positivo: quien llegue tarde a la fecha de entrega pierde puntos, y quien la logre los obtiene. Esos puntos serían canjeables por fermentaciones líquidas varias en la cafetería, a gusto del consumidor.

Conclusiones

Aunque se han cumplido la mayoría de los objetivos, se puede mejorar bastante el proceso de trabajo. Es necesario mejorar la integración para conseguir interoperabilidad fácilmente entre el código de todos, y conseguir un reparto de trabajo más equitativo. Aunque la integración ha sido difícil era algo previsto por ser la primera; intentaremos resolver los procesos para ello para la próxima iteración. Además, la opinión general sobre Python, el nuevo lenguaje de programación usado en el proyecto, es que para lo que podía ser ha funcionado razonablemente bien, sin grandes dificultades de desarrollo.

Respecto del abandono de un miembro del equipo, solo destacar que al no ser un miembro activo hasta ese punto no ha afectado tanto a las planificaciones de reparto de trabajo. Confiamos en que no tengamos de nuevo ese problema, ya que el equipo de desarrollo está muy motivado.


Clases

Table of Contents

Tras el brainstorming de clases, llegamos a las siguientes:

Database (base de datos)

Descripción

Interfaz sobre las bases de datos para encapsular la funcionalidad que se requiera de las mismas.

Responsabilidades

Abstraer el comportamiento interno de las bases de datos al resto de la aplicación para no depender de ellas y ocultar la complejidad del SQL.


Journal

Descripción

Mantiene un listado de operaciones pendientes de realización a fin de poder recuperarnos de caídas del sistema y de no sobrecargar el sistema, permitiendo planificar.

Responsabilidades

  • Escribir en la base de datos lo que le ha pedido Watcher.
  • Borrar la tarea una vez terminada.

Colaboraciones


History (Historia)

Descripción

Guarda toda la información del sistema de snapshots.

Responsabilidades

  • Mantener la información de todas las versiones que guarda el usuario, tanto locales como en dispositivos externos.

Colaboraciones


XDelta3 Wrapper

Descripción

Ofrece a HD Lorean la funcionalidad presente en la aplicación xdelta que cubre necesidades del programa y traduce entre la misma y las facilidades que ofrece xdelta. Parte de una factoría gestionada por storage manager.

Colaboraciones


LVM Wrapper

Descripción

Ofrece a HD Lorean la funcionalidad presente en el sistema de archivos LVM que cubre las necesidades del programa y traduce entre las mismas y las facilidades que ofrece LVM. Parte de una factoría gestionada por storage manager.

Por ahora no desarrollada por estar fuera del alcance.


ZFS Wrapper

Descripción

Ofrece a HD Lorean la funcionalidad presente en el sistema de archivos ZFS que cubre las necesidades del programa y traduce entre las mismas y las facilidades que ofrece ZFS. Parte de una factoría gestionada por storage manager.

Por ahora no desarrollada por estar fuera del alcance.


Snapshot manager (Administrador de snapshots)

Descripción

Administra los snapshots.

Responsabilidades

  • Borrar un snapshot.
  • Crear un snapshot.
  • Recuperar determinada versión de un archivo en un instante dado.
  • Busca una versión entre todas las almacenadas en un instante dado.

Colaboraciones


Storage manager (Administrador de almacenamiento)

Descripción

Interfaz para los distintos sistemas de archivos que puede soportar la aplicación.

Responsabilidades

Abstraer el comportamiento interno del sistema de archivos al resto de la aplicación para no depender de él.

Colaboraciones


Snapshot viewer (Visor de diferencias entre los snapshots)

Descripción

Permite la comparación visual entre dos snapshots.
No desarrollado por el momento por estar fuera del acance


Scheduler

Descripción

Establece el orden en que se realizan las operaciones de copia de seguridad y cuándo deben suceder.

Responsabilidades

  • Atender al journal.
  • Planificar los snapshots mediante los monitores de carga del sistema.
  • Ordenar grabar y borrar los snapshots.
  • Avisar a Watcher de que se ha finalizado una operación.

Colaboraciones


Watcher

Descripción

Atiende a los cambios que se hayan realizado en el sistema (ya sea por eventos periódicos del planificador del propio sistema operativo, esto es cron, por cambios en archivos vigilados que se deban almacenar, usando inotify, o bien por orden del usuario). Asímismo ordena realizar backups.

Responsabilidades

  • Escribir en el journal cuando se reciba un evento.
  • Actualizar inotify.

Colaboraciones


Metadata Indexer (Indexador de metadatos)

Descripción

Indexa información útil para la posterior búsqueda de archivos en el sistema (tanto mediante su contenido como mediante metadatos).
Por ahora no desarrollada por estar fuera del alcance.


Help (ayuda)

Descripción

Muestra el manual de la aplicación.


Stats (Estadísticas)

Descripción

Recopila información útil sobre el uso de HD Lorean que permite elaborar estadísticas y predicciones (por ejemplo, sobre el uso del disco).
Por ahora no desarrollada por estar fuera del alcance.


Usuario

Descripción

Representa los datos del usuario y estadísticas sobre el mismo.
Por ahora no desarrollado por estar fuera del alcance.

Colaboraciones


Preferences (Diálogo de preferencias)

Descripción

Permite configurar la aplicación.

Responsabilidades

  • Permitir la configuración de todo tipo de opciones, como indexar y/o excluir nuevos contenidos (carpetas, archivos, patrones…), periodicidad, uso de disco, etc.

Colaboraciones


Config wizard (Asistente de configuración)

Descripción

Permite fijar de manera sencilla y rápida las opciones de configuración más comunes del programa.
Por ahora no desarrollada por estar fuera del alcance.

Responsabilidades

  • Obtener todos los datos de configuración necesarios.
  • Almacenar esos cambios.
  • Avisar al backend de que se han producido.

Colaboraciones


Config file manager (lector de archivo de configuración)

Descripción

Lee el archivo de configuración y lo traduce a parámetros de la aplicación.

Responsabilidades

  • Validación de los datos.
  • Escritura de opciones.
  • Crear lista de archivos monitoreados.

Colaboraciones

En función de la implementación, con regexp parser.


Regexp parser (Parser de expresiones regulares).

Descripción

Interpreta expresiones regulares y ofrece funcionalidad basada en las mismas.


Inotify handler

Descripción

Interpreta las señales que envía inotify y ofrece una API para su manejo.

Responsabilidades

  • Añadir o eliminar notificadores.
  • Notificar los eventos recibidos.

Cron handler (manejador cron)

Descripción

Ofrece una API para interactuar con el demonio cron de planificación de tareas del sistema operativo.


D-Bus Manager

Descripción

Permite la comunicación vía paso de mensajes entre el frontend (o GUI) y el backend de la aplicación.

Responsabilidades

  • Transformar los mensajes de D-Bus en órdenes internas del programa => enviar órdenes.

Colaboraciones


Battery monitor (control de la batería)

Descripción

Monitoriza el estado de la batería.
Parte de una factoría, aún no implementada en su conjunto.


HD Load monitor (Control de carga del disco duro)

Descripción

Monitoriza la carga del disco duro (nivel de operaciones de entrada/salida del sistema) a fin de proporcionar información al planificador sobre cuándo conviene efectuar las operaciones.

Por ahora no desarrollada por estar fuera del alcance.

Posiblemente parte de una factoría para unificar las interfaces.


CPU load monitor (control de carga de la CPU)

Descripción

Monitoriza el estado de carga de la CPU. Posiblemente parte de una factoría.

Por ahora no desarrollada por estar fuera del alcance.


Storage monitor (Monitor de almacenamiento)

Descripción

Supervisa el espacio de almacenamiento y proporciona información sobre el mismo. Posiblemente parte de una factoría.

Responsabilidades

  • Comprobar si hay espacio en disco para escribir.
  • Comprobar cuánto va a ocupar el backup que se almacene.

FUSE adapter (Adaptador FUSE)

Descripción

Clase que permite traducir la información almacenada en nuestro sistema de snapshots a una vista compatible con las operaciones de archivo estándar de linux.
Por ahora no desarrollada por estar fuera del alcance.


HAL manager (notificador de cambios en el hardware)

Descripción

También conocido como "HAL 9000", se encarga de notificar los cambios en las unidades conectadas al sistema que puedan afectar a los backups (por ejemplo si se extrae un disco sobre el que se está efectuando un backup).

Por ahora no desarrollada por estar fuera del alcance.


Device manager (administrador de dispositivos externos)

Descripción

Gestiona las copias de los snapshots de HD Lorean en medios de almacenamiento externos como discos ópticos o memorias externas.

Por ahora no desarrollada por estar fuera del alcance.


Optical Media manager (Administrador de la grabadora)

Descripción

Permite exportar snapshots a medios ópticos. Posiblemente parte de una factoría para unificar las interfaces.


External API (API pública del sistema)

Descripción

Exporta la funcionalidad de HD Lorean y permite su uso por parte de aplicaciones de terceros.

Por ahora no desarrollada por estar fuera del alcance.


UI (Interfaz de usuario)

Descripción

Implementa la interfaz de usuario.


Nautilus integration (Integración con Nautilus)

Descripción

Se encarga de la integración con Nautilus, a modo de adaptador entre la interfaz que ofrezca el API de Nautilus y la información que proporciona HD Lorean.

Por ahora no desarrollada por estar fuera del alcance.

Colaboraciones


Snapshot finder (Buscador de versiones)

Descripción

Busca entre las versiones almacenadas, posiblemente por contenido.

Por ahora no desarrollada por estar fuera del alcance.

Colaboraciones


Beagle integration (Integración con Beagle)

Descripción

Se encarga de la integración con Beagle para permitir al sistema de indexación de contenidos que indexe nuestros archivos y posiblemente integrarlos con sus resultados para realizar nuestras búsquedas.

Por ahora no desarrollada por estar fuera del alcance.


Interacción clases

Table of Contents

Introducción

HD Lorean posee dos bloques bien diferenciados, uno es el backend y el otro el frontend o interfaz de usuario. El primero se encarga de la comunicación entre el hardware y las tecnologías que permiten la gestión de snapshots y la monitorización de ficheros. El otro se encarga de la comunicación con el usuario.

Entre ellos, pero formando parte de backend, se encuentra el módulo D-Bus Manager. Esta clase no se limita únicamente a encapsular d-bus sino que su misión es la de traducir los mensajes que reciba en un conjunto de órdenes internas de HD Lorean, las cuales enviará a los distintos módulos de la aplicación para que se realice el trabajo necesario.

Dentro del backend, a su vez, es posible distinguir dos subgrupos importantes que separan además dos cursos de colaboración frecuentes. Por un lado, el grupo formado por las clases config file manager, watcher, scheduler, inotify handler y cron handler forman la ruta de planificación y configuración. Por otro, el formado por snapshot manager y el conjunto de clases que utilizan como interfaz storage manager, que forman la ruta de gestión de snapshots.

Ruta de comunicación con el usuario

Gran parte de los casos de uso son desencadenados por el usuario y por tanto requieren la comunicación con HD Lorean. Para ello existen las clases pertenecientes al frontend. En general, el comportamiento de esta ruta es el siguiente.

  1. Frontend (alguna de sus clases: el diálogo de preferencias, el asistente de configuración, el visor de snapshots…).
  2. D-Bus Manager (recibe un mensaje con la tarea requerida por el usuario).
  3. Ruta de gestión de planificación y configuración

La sencillez de la ruta se apoya en la necesidad de mantener lo más separado posible la interfaz de usuario del backend.

Por último, existe también una ruta inversa (puede denominarse Ruta de comunicación con el usuario inversa) que permite que resultados internos de HD Lorean lleguen en forma de mensajes al frontend y este pueda exponerlos al usuario de alguna forma. Ésta es:

Ruta de comunicación con el usuario inversa

  1. Ruta de gestión de planificación y configuración (desde aquí se emite algún resultado que debe mostrarse al usuario).
  2. D-Bus Manager (dbus recibe el resultado y transmite un mensaje al frontend; alternativamente el frontend escucha mensajes del backend y presenta la información según sea necesario).
  3. Frontend.

Ruta de planificación y configuración

Como se dijo anteriormente, la ruta de planificación engloba las colaboraciones entre config-file-manager, watcher, scheduler, inotify-handler y cron-handler. Config file manager se encarga de la gestión del archivo de configuración, tanto de su versión física escrita en disco como de su versión virtual a la que el resto de módulos puede acceder para conocer diversos aspectos de la configuración. Los inotify handler y cron handler disparan eventos de inotify y cron que permiten monitorización de cambios en tiempo real y bajo planificación respectivamente. También disponen de acceso, a través de la clase clases, a una base de datos rápida o journal donde anotan las operaciones que han de realizarse próximamente, para poder planificarlas y reiniciarlas (a modo de transacciones semiatómicas) a fin de poder recuperarse de caídas del sistema o del programa, planificadas o no.

El scheduler por otro lado se encarga de gestionar y priorizar las órdenes que le llegan desde los disparadores o directamente desde el módulo de dbus, en función de la carga del sistema recogida de diversos monitores. También se comunica con la ruta de gestión de snapshots y atiende a sus resultados como veremos en breve.

La ruta de colaboraciones típica se muestra a continuación:

  1. D-Bus Manager (dbus tiene algún mensaje que convertir en órdenes)
  2. Config file manager (desde dbus se indica si ha de modificarse alguna configuración)
  3. Scheduler (desde dbus se informa al scheduler de alguna orden recibida desde el frontend)
    1. Battery monitor
    2. CPU load monitor
    3. HD Load monitor
  4. Ruta de gestión de snapshots

Desde aquí, dependiendo de la orden, el planificador accedería a la ruta de gestión de snapshots bien para ordenar la creación de un nuevo snapshot, bien para recuperar la información de alguno de los existentes, bien para eliminar algunos de ellos. A esta ruta la llamaremos ruta de usuario.

Como HD Lorean actúa en su mayor parte del tiempo sin necesidad de interacción por parte del usuario, incitado por inotify o cron, otra ruta alternativa probablemente más frecuente que la anterior es la siguiente:

  1. Cron handler o inotify handler (se ha producido algun cambio en un fichero o directorio que debe ser guardado, o se ha producido una señal planificada).
  2. Watcher (escritura al journal y tratamiento del suceso en función de si es cron o inotify)
  3. Scheduler (prioriza las señales recibidas de los módulos anteriores)
  4. Ruta de gestión de snapshots

A esta última ruta la llamaremos ruta de monitorización.

Igual que en el caso anterior, existe una ruta inversa que parte de los resultados de la ruta de gestión hasta llegar a dbus.

Ruta de monitorización inversa

  1. Ruta de gestión de snapshots (la ruta emite algún resultado en forma de objeto Snapshot)
  2. Snapshot manager (objeto con los resultados de la ruta de gestión)
  3. Scheduler (recibe el resultado de una orden)
  4. D-Bus Manager (recibe, si procede, los resultados un mensaje con los resultados de la operación).

Ruta de gestión de snapshots

Esta última ruta está formada por las clases snapshot manager, y el conjunto de storage manager. La primera, snapshot-manager, ofrece toda la funcionalidad relativa a la gestión de snapshots como la creación, eliminación o lectura de los mismos. Esta clase hace uso del conjunto storage manager que ofrece el nivel más bajo de funcionalidad a través de una API común. Los integrantes del módulo codifican esta API dependiendo de si el soporte de snapshots es un sistema de archivos (como LVM o ZFS), una aplicación externa (como xdelta3) o un modelo propio (como snapshot-core), para tratar de permitir el uso del sistema más eficiente según dónde se desplegase la aplicación, así como para obligarnos a separar ese componente crítico del resto de funcionalidad de la aplicación.

La clase Snapshot sirve de puente de comunicación entre Scheduler y la ruta de gestión que nos ocupa. La clase representa un Snapshot como resultado con capacidad para autoarchivarse dentro de una posible base de datos y con toda la información útil que fuese necesaria. Es el resultado de las operaciones de lectura, escritura o eliminación de la ruta de gestión.

La ruta de colaboraciones típica es la siguiente:

  1. Snapshot manager (la orden llega desde el scheduler)
  2. Storage manager (entre ellos, el más adecuado al medio)
  3. Snapshot manager (recibe la terminación del módulo que corresponda)
  4. Snapshot (se crea un objeto snapshot con información relevante asociada a la operación)
  5. Ruta de planificación y configuración

NOTA: En esta última se ha incluido el retorno inverso hasta la comunicación con la ruta de planificación. Hay que notar, que gran parte de estos retornos son implícitos debido al retorno de llamadas a función.

Rutas por casos de uso

Con todas las rutas especificadas, la elaboración de las colaboraciones por casos de uso es mucho más sencilla y clara.


Añadir una nueva carpeta a indexar

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (cambio en la configuración y órdenes)
3. ruta de gestión de snapshots (creación del snapshot)


Borrar todas las versiones de un archivo

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (cambio en la configuración y órdenes)
3. ruta de gestión de snapshots (Cambios a disco, bases de datos)


Borrar una versión de un archivo

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (cambio en la configuración y órdenes)
3. ruta de gestión de snapshots (cambios a disco, bases de datos)


Configurar exclusión

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (cambio en la configuración y órdenes para borrar el patrón; posible feedback al usuario)
3. ruta de gestión de snapshots (consultas a bases de datos, posiblemente borrar, devolver el feedback que sea necesario)


Configuración inicial

1. Ruta de comunicación con el usuario (mediante el asistente)
2. Ruta de planificación y configuración (cambio en la configuración y actualizar estado actual)
3. ruta de gestión de snapshots (si es necesario realizar trabajo o añadir algo)


Copiar al lado

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (cambio en la configuración y órdenes)
3. ruta de gestión de snapshots (lectura y posible reconstrucción del snapshot)


Eliminar una carpeta a indexar

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (cambio en la configuración y actualizar estado actual)
3. ruta de gestión de snapshots (escrituras a disco y base de datos)


Sincronizar

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (órdenes)
3. ruta de gestión de snapshots (registro en base de datos)


Exportar a un dispositivo externo

1. Ruta de comunicación con el usuario, posiblemente colaborando con HAL (en el backend, para detectar dispositivo)
2. Ruta de planificación y configuración (órdenes)
3. ruta de gestión de snapshots (registro en base de datos de dónde se encuentra la información; posible eliminación local para ahorrar espacio).


Importar de un dispositivo externo

1. Ruta de comunicación con el usuario, posiblemente colaborando con HAL (en el backend, para detectar dispositivo)
2. Ruta de planificación y configuración (órdenes)
3. ruta de gestión de snapshots (añadido a base de datos de la nueva información, comprobando inconsistencias; ver el caso de uso).


Guardar un backup

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (órdenes)
3. ruta de gestión de snapshots (escrituras a disco y base de datos).


Guardar versiones automáticamente

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (órdenes)
3. ruta de gestión de snapshots (posible actualización del estado).


Backup inicial

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (órdenes)
3. ruta de gestión de snapshots (creación de estructuras de datos pertinentes; primer backup).


Sobreescribir última versión

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (órdenes)
3. ruta de gestión de snapshots (lectura del snapshot, reconstrucción del mismo).


Buscar contenidos en backup

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (órdenes)
3. ruta de gestión de snapshots (búsqueda en base de datos).
3.a Alternativamente, además usar clases.


Almacenamiento extra

No cubierto inicialmente por no ser en absoluto crítico.


Interrupción del backup

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (órdenes)
3. ruta de gestión de snapshots (según queden o no tareas pendientes)


Aplicaciones de terceros

No cubierta inicialmente al depender de un estado de desarrollo bastante más avanzado de la aplicación y no ser crítico. En cualquier caso el componente dbus ofrece un api de cara a otras interfaces de usuario para la aplicación.


Ver versiones de carpetas

1. Ruta de comunicación con el usuario
2. Ruta de planificación y configuración (órdenes, recibir información)
3. ruta de gestión de snapshots (lectura de los listados de base de datos)


Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License