Stage LP DI Madjid Bouchair SERFA, CDS

VOSpace : API Web REST en Python

Encadré par : André Schaaff

Au cours du mois d'avril, j'ai abordé plusieurs problématiques autour de l'API VOSpace :

Création et amélioration continue de la base MongoDB

Afin d'assurer une persistance des données du serveur VOSpace, la mise en place d'une base de donnée est indispensable.

Le SGBD choisi est MongoDB. MongoDB est un des SGBD orienté document ne nécéssitant pas de schéma de données prédéfini. Il est l'un des SGBD les plus utilisés au monde, a l'avantage de supporter un driver Python et d'offrir la représentation des documents dans un format proche du JSON (BSON), permettant une manipulation rapide et facile.

{
    "_id" : ObjectId("590b20603d945451fe67a2db"),
    "name" : "VOSPACE",
    "voviews" : {
        "provides" : {
            "default" : "ivo://ivoa.net/vospace/core#defaultview",
            "fits" : "ivo://ivoa.net/vospace/core#fits",
            "votable" : "ivo://ivoa.net/vospace/core#votable"
        },
        "accepts" : {
            "fits" : "ivo://ivoa.net/vospace/core#fits",
            "votable" : "ivo://ivoa.net/vospace/core#votable",
            "anyview" : "ivo://ivoa.net/vospace/core#anyview"
        }
    },
    "PROPERTIES" : {
        "type" : "ivo://ivoa.net/vospace/core#type",
        "groupread" : "ivo://ivoa.net/vospace/core#groupread",
        ...
        "btime" : "ivo://ivoa.net/vospace/core#btime",
        "format" : "ivo://ivoa.net/vospace/core#format",
        "availableSpace" : "ivo://ivoa.net/vospace/core#availableSpace"
    },
    "PROTOCOL" : {
        "get" : "ivo://ivoa.net/vospace/core#httpget",
        "post" : "ivo://ivoa.net/vospace/core#httppost",
        "delete" : "ivo://ivoa.net/vospace/core#httpdelete",
        "put" : "ivo://ivoa.net/vospace/core#httpput"
    },
    "voprotocols" : {
        "provides" : {
            "get" : "ivo://ivoa.net/vospace/core#httpget",
            "put" : "ivo://ivoa.net/vospace/core#httpput"
        },
        "accepts" : {
            "get" : "ivo://ivoa.net/vospace/core#httpget",
            "put" : "ivo://ivoa.net/vospace/core#httpput"
        }
    }
}

Le fait de ne pas avoir de schéma de données prédéfini offre la possibilité de changer la "structure" des documents à volonté sans mettre en péril l'intégralité de la base. Chaque document peut avoir une structure différente.

La documentation est simple à comprendre et complète, MongoDB a aussi une communauté importante, ce qui offre de multiples sources d'informations. Il possède aussi une documentation pour l'utilisation de la base avec un client Python :

https://docs.mongodb.com/getting-started/python/client/

En premier lieu, j'ai créé 2 "collections", la première servait de collection de test pour les insertions et requêtes depuis les méthodes et à stocker les metadonnées des nodes, la seconde 'arbre' était utilisé par la méthode scannant le filesystem qui l'actualisait avec le dictionnaire généré par le scan.

Au fil des modifications des méthodes et du besoin d'un document compréhensible par toutes les méthodes, les collections ont évolué. La première s'est vu transformé petit à petit en collection de stockage des "settings" du serveur, permetant ainsi de les isolés d'une collection plus active où le risque de suppression par erreur est plus important.

La seconde est consacré aux nodes et contient leurs représentations, les documents ont été révisé de nombreuses fois afin de permettre des recherches et des update de façon plus aisés. Chaque "VOSpace" (node mère) est un document composé de ses metadonnées (VOSpace + FileSystem) ainsi que de l'arborescence de ses nodes filles.

Exemple :

{
    "_id" : ObjectId("590b207d3d9454521983bdc9"),
    "size" : "12.79ko",
    "properties" : {
        "type" : {
            "type" : "ContainerNode"
        },
        "language" : {
            "Farsi" : "ivo://ivoa.net/vospace/core#language"
        },
        "title" : {
            "IVOA" : "ivo://ivoa.net/vospace/core#title"
        },
        "btime" : {
            "Creation date" : "2017-05-04 14:33:38"
        },
        "ctime" : {
            "metadata modified" : "2017-05-04 14:37:17"
        },
        "mtime" : {
            "Modified" : "2017-05-04 14:33:38"
        },
        "contributor" : {
            "Foo" : "ivo://ivoa.net/vospace/core#contributor"
        },
        "description" : {
            "Yet another node" : "ivo://ivoa.net/vospace/core#description"
        }
    },
    "provides" : {
        "fits" : "ivo://ivoa.net/vospace/core#fits",
        "default" : "ivo://ivoa.net/vospace/core#defaultview"
    },

    "subnode : 1" : {
        "path" : "./VOTest/VOSpace/nodes/myresult1/view_accept.txt",
        "node" : "view_accept.txt",
        "accepts" : [],
        "ownerId" : "1004",
        "properties" : {
            "type" : {
                "type" : "StructuredDataNode"
            },
            "btime" : {
                "Creation date" : "2017-05-04 14:33:38"
            },
            "ctime" : {
                "Metadata modified" : "2017-05-04 14:37:17"
            },
            "mtime" : {
                "Modified" : "2017-05-04 14:33:38"
            }
        },
        "provides" : [],
        "size" : "35o"
    },
    "subnode : 0" : {
        "path" : "./VOTest/VOSpace/nodes/myresult1/myresult1.txt",
        "node" : "myresult1.txt",
        "accepts" : [],
        "ownerId" : "1004",
        "properties" : {
            "type" : {
                "type" : "StructuredDataNode"
            },
            "btime" : {
                "Creation date" : "2017-05-04 14:33:38"
            },
            "ctime" : {
                "metadata modified" : "2017-05-04 14:37:17"
            },
            "mtime" : {
                "Modified" : "2017-05-04 14:33:38"
            }
        },
        "provides" : [],
        "size" : "10o"
    },
    "subnode : 2" : {
        "path" : "./VOTest/VOSpace/nodes/myresult1/Capability",
        "node" : "Capability",
        "subnode : 0" : {
            "path" : "./VOTest/VOSpace/nodes/myresult1/Capability/myresult1_capability.txt",
            "node" : "myresult1_capability.txt",
            "accepts" : [],
            "ownerId" : "1004",
            "properties" : {
                "type" : {
                    "type" : "StructuredDataNode"
                },
                "btime" : {
                    "Creation date" : "2017-05-04 14:33:38"
                },
                "ctime" : {
                    "metadata modified" : "2017-05-04 14:37:17"
                },
                "mtime" : {
                    "Modified" : "2017-05-04 14:33:38"
                }
            },
            "provides" : [],
            "size" : "12o"
        },
        "accepts" : [],
        "ownerId" : "1004",
        "properties" : {
            "type" : {
                "type" : "ContainerNode"
            },
            "btime" : {
                "Creation date" : "2017-05-04 14:33:38"
            },
            "ctime" : {
                "metadata modified" : "2017-05-04 14:37:17"
            },
            "mtime" : {
                "Modified" : "2017-05-04 14:33:38"
            }
        },
        "provides" : [],
        "size" : "12o"
    },
    "node" : "myresult1",
    "accepts" : {
        "fits" : "ivo://ivoa.net/vospace/core#fits",
        "anyview" : "ivo://ivoa.net/vospace/core#anyview"
    },
    "ownerId" : "1004",
    "path" : "./VOTest/VOSpace/nodes/myresult1"
}

Comme il est possible de constaté sur ce document exemple, une fois récupéré, son traitement est très simple car il s'apparente à celui d'un dictionnaire Python. La structure du document est encore amené à évolué avec les prochaines modifications à venir sur les méthodes pour notament facilité la recherche dans les sous-nodes.

Création d'une routine de démarrage du serveur vérifiant la BDD

Afin d'assurer un fonctionnement optimal de l'API et du serveur, une méthode lancée dès le démarrage du serveur a été créé. Tout d'abord elle consulte la BDD, si celle-ci est lancée mais vide, alors la méthode scan le filesystem afin d'updater la collection 'arbre'. Elle met à jour les metadonnées du service.

Modification des méthodes afin d'intégrer les vérifications et mises à jour de la BDD

L'ajout de la base de donnée permet la vérification de l'existance de la node ainsi que de ses metadonnées. Mais elle entraine aussi la modification de plusieurs méthodes afin de mettre en place ces vérifications avant toute action sur les nodes afin d'assurer que les données sont correctements sauvegardées. La structure des collections ainsi que la représentation des documents a entrainé beaucoup de tests et de modification dans la façon dont les méthodes fonctionnaient.

Par exemple une méthode comme createNode(path) vérifie la non-existence de la node dans la BDD puis sur le filesystem avant toute tentative de création. Si la node n'est présente ni dans la BDD ni sur le file system, la création est lancée. Cependant si la node existe, une exception doit être levée. Il faudra par la suite traiter le cas où la node existe dans la BDD sans exister sur le file system et vice versa. Qui doit être privilégié ?

Lecture de la "documentation" de la librairie UWS présente au CDS

Une librairie UWS 4.1 et Beta 4.2 a été développé au CDS par Grégory Mantelet. Afin de ne pas refaire un travail déjà fait, j'ai décidé d'utiliser cette librairie pour les méthodes asynchrones du serveur.

http://cdsportal.u-strasbg.fr/uwstuto/

La documentation étant presque inexistante, j'ai utilisé la page de démonstration pour voir le fonctionnement basique de la librairie. J'ai aussi consulté la javadoc et contacté directement l'auteur par mail.

logo_cds logo_serfa