diff --git a/data/grid5000/sites/grenoble/grenoble.json b/data/grid5000/sites/grenoble/grenoble.json
index 86b2006238946c4f99650ce8bc6be9efbb13d602..1783d09db05f87eb1fc4d7253c8c067a713fdeb8 100644
--- a/data/grid5000/sites/grenoble/grenoble.json
+++ b/data/grid5000/sites/grenoble/grenoble.json
@@ -54,9 +54,9 @@
       "network": "172.16.16.0/20"
     }
   },
-  "latitude": 45.1833,
+  "latitude": 45.19021,
   "location": "Grenoble, France",
-  "longitude": 5.7167,
+  "longitude": 5.76705,
   "name": "Grenoble",
   "production": true,
   "renater_ip": "192.168.4.15",
diff --git a/data/grid5000/sites/nancy/nancy.json b/data/grid5000/sites/nancy/nancy.json
index 7b3a8fcde7baae51a90ebbd003146b463531e32e..9e9c7826ed574c710524fc3a6c40b6f66ac299b4 100644
--- a/data/grid5000/sites/nancy/nancy.json
+++ b/data/grid5000/sites/nancy/nancy.json
@@ -55,9 +55,9 @@
     },
     "topo": "1501..1601"
   },
-  "latitude": 48.7,
+  "latitude": 48.66543,
   "location": "Nancy, France",
-  "longitude": 6.2,
+  "longitude": 6.15698,
   "name": "Nancy",
   "production": true,
   "renater_ip": "192.168.4.14",
diff --git a/input/grid5000/sites/grenoble/grenoble.yaml b/input/grid5000/sites/grenoble/grenoble.yaml
index 45bff80bafdf7c5ae49983b2af43a27bb180b2dd..5fff6086d924efea5fead450cef09ea654e2a560 100644
--- a/input/grid5000/sites/grenoble/grenoble.yaml
+++ b/input/grid5000/sites/grenoble/grenoble.yaml
@@ -4,8 +4,8 @@ name: Grenoble
 location: Grenoble, France
 web: http://www.grid5000.fr/mediawiki/index.php/Grenoble:Home
 description: Grid5000 Grenoble site
-latitude: 45.1833
-longitude: 5.7167
+latitude: 45.19021
+longitude: 5.76705
 email_contact: support-staff@lists.grid5000.fr
 sys_admin_contact: support-staff@lists.grid5000.fr
 security_contact: support-staff@lists.grid5000.fr
diff --git a/input/grid5000/sites/nancy/nancy.yaml b/input/grid5000/sites/nancy/nancy.yaml
index f276b6dec132d3e619f5671fc07a2665f1fc67ad..b1bd45b3213c176d049c3acd0add7e9794fa334c 100644
--- a/input/grid5000/sites/nancy/nancy.yaml
+++ b/input/grid5000/sites/nancy/nancy.yaml
@@ -4,8 +4,8 @@ name: Nancy
 location: Nancy, France
 web: http://www.grid5000.fr/mediawiki/index.php/Nancy:Home
 description: Grid5000 Nancy site
-latitude: 48.7000
-longitude: 6.2000
+latitude: 48.66543
+longitude: 6.15698
 email_contact: support-staff@lists.grid5000.fr
 sys_admin_contact: support-staff@lists.grid5000.fr
 security_contact: support-staff@lists.grid5000.fr
diff --git a/scripts/mesos/mesocentre.schema.json b/scripts/mesos/mesocentre.schema.json
new file mode 100644
index 0000000000000000000000000000000000000000..87ed1263f01b72ad9a7b097a341e95a34fe4073a
--- /dev/null
+++ b/scripts/mesos/mesocentre.schema.json
@@ -0,0 +1,222 @@
+{
+  "title": "mesocentre",
+  "type": "object",
+  "required": ["name","contactName","location"],
+  "properties": {
+    "name": {
+      "type": "string",
+      "description": "Nom usuel"
+    },
+    "institutesName": {
+      "type": "array",
+      "items": {
+        "type": "string"
+      },
+      "description": "Tutelles du mésocentre"
+    },
+    "url": {
+      "type": "string",
+      "description": "Adresse Web de l'infrastructure"
+    },
+    "financersName": {
+      "type" : "array",
+      "items": {
+        "type": "string"
+      },
+      "description": "Nom des organismes ou projet financeurs"
+    },
+    "location": {
+      "type": "string",
+      "enum": ["Auvergne-Rhône-Alpes",
+        "Bourgogne-Franche-Comté",
+        "Bretagne",
+        "Centre-Val de Loire",
+        "Corse",
+        "Grand Est",
+        "Guadeloupe",
+        "Guyane",
+        "Hauts-de-France",
+        "ÃŽle-de-France",
+        "Martinique",
+        "Mayotte",
+        "Normandie",
+        "Nouvelle-Aquitaine",
+        "Occitanie",
+        "Pays de la Loire",
+        "Provence-Alpes-Côte d'Azur",
+        "La Réunion",
+        "Etranger"],
+      "description": "Localisation géographique (Région)"
+    },
+    "GPSCoordinates": {
+      "type": "array",
+      "items": {
+        "type": "number"
+      },
+      "minItems": 2,
+      "maxItems": 2,
+      "description": "Coordonnées GPS - latitude, longitude - du centre (pour la création de la carte des mésocentres)"
+    },
+    "contactName": {
+      "type": "string",
+      "description": "Nom du contact principal"
+    },
+    "contactAddress": {
+      "type": "string",
+      "description": "Adresse email du contact"
+    },
+    "totalCoreNumber": {
+      "type": "number",
+      "description": "Nombre de coeurs total"
+    },
+    "totalStorage": {
+      "type": "number",
+      "description": "Capacité disque totale en To"
+    },
+    "distributedInfra": {
+      "type": "array",
+      "items": {
+        "type": "string",
+        "enum": ["EGI","grid5000"]
+      },
+      "description": "Nom des infrastructures distribuées auxquelles appartient le mésocentre"
+    },
+    "serviceName": {
+      "type": "array",
+      "items": {
+        "type": "string"
+      },        
+      "description": "Services proposés par le mésocentre"
+    },
+    "etptNumber": {
+      "type": "number",
+      "description": "Nombre de ETPT travaillant pour l'administration et le support"
+    },
+    "accessPolicy": {
+      "type": "array",
+      "items": {
+        "type": "string"
+      },      
+      "description": "Politique d'accès : qui a accès au mésocentre, de quelle manière ?"
+    },
+    "fullDescription": {
+      "type": "string",
+      "description": "Longue description"
+    },
+    "clusterList" : {
+      "type": "array",
+      "items": {"$ref": "#/definitions/clusterItem"}
+    }, 
+    "storageList" : {
+      "type": "array",
+      "items": {"$ref": "#/definitions/storageTypeItem"}
+    }
+  },
+  "definitions":{
+    "clusterItem": {
+      "type": "object",
+      "properties": {
+        "clusterName": {
+          "type": "string",
+          "description": "Nom du cluster"
+        },
+        "clusterCoreNumber":{
+          "type": "number",
+          "description": "Nombre de coeurs dans ce cluster"
+        },
+        "nodeType" : {
+          "type": "array",
+          "items": {"$ref": "#/definitions/nodeTypeItem"}
+        },
+        "storageType": {
+          "type": "array",
+          "items": {"$ref": "#/definitions/storageTypeItem" }
+        },
+        "networkType": {
+          "type": "string",
+          "enum": ["infiniband","ethernet","omni-path"],
+          "description": "Type d'interconnexion"
+        },
+        "networkBandwidth": {
+          "type": "number",
+          "description": "Bande passante réseau en Gb/s"
+        },
+        "networkTopology": {
+          "type": "string",
+          "enum": ["fat tree","tore","dragonfly","hypercube"],
+          "description": "Topologie réseau"
+        },
+        "jobschedulerName": {
+          "type": "string",
+            "enum": ["slurm","oar","grid engine","lsf","pbs","openstack"],
+          "description": "Nom du gestionnaire de jobs"
+        },
+        "vendorName": {
+          "type": "string",
+            "enum": ["IBM", "NEC", "Cray", "Atos", "HPE", "SGI", "Dell", "Lenovo", "Sun Microsystems", "NVIDIA"],
+          "description": "Nom du vendeur"
+        }
+      }
+    },
+    "nodeTypeItem": {
+      "type": "object",
+      "properties": {
+        "GPUType": {
+          "type": "string",
+          "description": "Type de GPU (si le noeud en possède)"
+        },
+        "GPUNumber": {
+          "type": "number",
+          "description": "Nombre de GPU par noeud de ce type"
+        },
+        "CPUType": {
+          "type": "string",
+          "description": "Type de CPU"
+        },
+        "coreNumber": {
+          "type": "number",
+          "description": "Nombre de coeurs par noeud de ce type"
+        },
+        "cpuNumber": {
+          "type": "number",
+          "description": "Nombre de CPUs (sockets) par noeud de ce type"
+        },
+        "memory": {
+          "type": "number",
+          "description": "RAM en Go par noeud de ce type"
+        },
+        "localDisk": {
+          "type": "number",
+          "description": "Capacité en To"
+        },
+        "nodeNumber": {
+          "type": "number",
+          "description" : "Nombre de noeuds de ce type"
+        }
+      }
+    },
+    "storageTypeItem": {
+      "type": "object",
+      "properties": {
+        "typeName": {
+          "type": "string",
+          "enum": ["scratch","home","data"],
+          "description": "Type de stockage"
+        },
+	"name": {
+          "type": "string",
+          "description": "Nom du stockage"
+        },
+        "filesystemType":{
+          "type": "string",
+            "enum": ["BeeGFS","Spectrum Scale","LUSTRE","ext4","iRODS","NFS","CEPH"],
+          "description": "Nom du système de fichier"
+        },
+        "size": {
+          "type": "number",
+          "description": "Capacité de ce type de stockage en To"
+        }
+      }
+    }
+  }
+}
diff --git a/scripts/mesos/mesos.rb b/scripts/mesos/mesos.rb
new file mode 100755
index 0000000000000000000000000000000000000000..e0fe61a7043e17ebf5f63e2529755a6fd9c5c392
--- /dev/null
+++ b/scripts/mesos/mesos.rb
@@ -0,0 +1,185 @@
+#!/usr/bin/ruby
+# coding: utf-8
+
+$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../../lib')))
+require 'refrepo'
+require 'json-schema'
+mesos_schema = JSON::load(IO::read('mesocentre.schema.json'))
+
+module Enumerable
+  def sum
+    collect(0) { |a,b| a + b }
+  end
+end
+
+d = load_data_hierarchy
+d['sites'].each_pair do |site, ds|
+  o = {
+    'name' => "Grid'5000 - #{site.capitalize}"
+  }
+  o['url'] = "https://www.grid5000.fr/w/#{site.capitalize}:Home"
+  o['institutesName'] = case site
+                        when 'grenoble' then ['CNRS', 'Inria', 'Université Grenoble Alpes']
+                        when 'lille' then ['CNRS', 'Inria', 'Université de Lille']
+                        when 'lyon' then ['CNRS', 'Inria', 'ENS Lyon']
+                        when 'nancy' then ['CNRS', 'Inria', 'Université de Lorraine']
+                        when 'nantes' then ['Inria', 'Université de Lorraine']
+                        when 'rennes' then ['CNRS', 'Inria', 'Université Rennes 1']
+                        when 'sophia' then ['CNRS', 'Inria', 'Université Côte d\'Azur']
+                        when 'luxembourg' then ['Université du Luxembourg']
+                        end
+#  o['financersName'] = [] # FIXME
+  o['fullDescription'] = "Grid'5000 est une infrastructure distribuée pour la recherche expérimentale dans tous les domaines de l'informatique, et en particulier pour le Cloud, le HPC, l'IA et le Big Data. Voir https://www.grid5000.fr/"
+   o['serviceName'] = [
+     'Infrastructure hautement reconfigurable et contrôlable: déploiement bare-metal d\'images systèmes, accès root sur les noeuds, reconfiguration réseau pour l\'isolation',
+     'Système de monitoring pour diverses métriques (en particulier pour la consommation énergétique)',
+     'Description très complète et traçabilité du matériel, pour une recherche reproductible'
+   ]
+  o['accessPolicy'] = [
+    'Ouvert à la communauté de recherche en informatique, en particulier dans les domaines du Cloud, du HPC de l\'IA et du Big Data',
+    'Ouvert aux chercheurs et ingénieurs d\'autres domaines scientifiques pour des travaux requiérant les services spécifiques offerts par Grid\'5000',
+    'Ouvert aux entreprises moyennant paiement à l\'usage'
+  ]
+  o['location'] = case site
+                  when 'grenoble' then 'Auvergne-Rhône-Alpes'
+                  when 'lille' then 'Hauts-de-France'
+                  when 'lyon' then 'Auvergne-Rhône-Alpes'
+                  when 'nancy' then 'Grand Est'
+                  when 'nantes' then 'Pays de la Loire'
+                  when 'rennes' then 'Bretagne'
+                  when 'sophia' then 'Provence-Alpes-Côte d\'Azur'
+                  when 'luxembourg' then 'Etranger'
+                  else raise
+                  end
+  o['GPSCoordinates'] = [ds['latitude'], ds['longitude']]
+  o['contactName'], o['contactAddress'] = case site
+                                           when 'grenoble' then ['Olivier Richard', 'olivier.richard@imag.fr']
+                                           when 'lille' then ['Nouredine Melab', 'nouredine.melab@univ-lille.fr']
+                                           when 'lyon' then ['Laurent Lefevre', 'laurent.lefevre@inria.fr']
+                                           when 'nancy' then ['Lucas Nussbaum', 'lucas.nussbaum@loria.fr']
+                                           when 'nantes' then ['Adrien Lèbre', 'adrien.lebre@inria.fr']
+                                           when 'rennes' then ['Anne-Cécile Orgerie', 'anne-cecile.orgerie@irisa.fr']
+                                           when 'sophia' then ['Fabrice Huet', 'fabrice.huet@unice.fr']
+                                           when 'luxembourg' then ['Sébastien Varrette', 'sebastien.varrette@uni.lu']
+                                           end
+  o['distributedInfra'] = [ 'grid5000' ]
+  o['clusterList'] = []
+  ds['clusters'].each_pair do |cluster, dc|
+    oc = {}
+    oc['clusterName'] = cluster
+    nodes = dc['nodes'].values.select { |n| not n['status'] == 'retired' }
+    next if nodes.empty?
+    fn = nodes.first
+    oc['vendorName'] = case fn['chassis']['manufacturer']
+                       when 'Dell Inc.' then 'Dell' # normalize according to schema
+                       when 'HP' then 'HPE'
+                       else fn['chassis']['manufacturer']
+                       end
+
+    oc['jobschedulerName'] = 'oar'
+    oc['clusterCoreNumber'] = nodes.length * fn['architecture']['nb_cores']
+    oc['nodeType'] = [
+      {
+        "CPUType" => "#{fn['processor']['model']} #{fn['processor']['version']}",
+        "coreNumber" => fn['architecture']['nb_cores'],
+        "cpuNumber" => fn['architecture']['nb_procs'], # FIXME check name of property
+        "memory" => (fn['main_memory']['ram_size'].to_f / 1024**3).to_i,
+        "nodeNumber" => nodes.length,
+        "localDisk" => (fn['storage_devices'].map { |sd| sd['size'] }.sum.to_f / 1024**4).round(2)
+      }
+    ]
+    if not fn['network_adapters'].select { |na| na['interface'] == 'InfiniBand' }.empty?
+      oc['networkType'] = 'infiniband'
+    elsif not fn['network_adapters'].select { |na| na['interface'] == 'Omni-Path' }.empty?
+      oc['networkType'] = 'omni-path'
+    else
+      oc['networkType'] = 'ethernet'
+    end
+    oc['networkBandwidth'] = fn['network_adapters'].select { |e| e['mountable'] }.map { |e| e['rate'] }.max / 1000**3
+    if (fn['gpu_devices'] || {}).values.length > 0
+      gpus = fn['gpu_devices'].values
+      oc['nodeType'].first['GPUType'] = "#{gpus.first['vendor']} #{gpus.first['model']}"
+      oc['nodeType'].first['GPUNumber'] = gpus.length
+    end
+    o['clusterList'] << oc
+  end
+  o['storageList'] = []
+  homesize = case site # size in To on 02/03/2020
+             when 'grenoble' then 28
+             when 'lille' then 6
+             when 'lyon' then 14
+             when 'nancy' then 91
+             when 'nantes' then 11
+             when 'rennes' then 10
+             when 'sophia' then 41
+             when 'luxembourg' then 10
+             end
+  o['storageList'] << {
+    'typeName' => 'home',
+    'name' => 'homes',
+    'filesystemType' => 'NFS',
+    'size' => homesize
+  }
+  # additional storage spaces
+  if site == 'lille'
+    o['storageList'] << {
+      'typeName' => 'data',
+      'name' => 'storage1',
+      'filesystemType' => 'NFS',
+      'size' => 90
+    }
+    o['storageList'] << {
+      'typeName' => 'data',
+      'name' => 'storage2',
+      'filesystemType' => 'NFS',
+      'size' => 90
+    }
+  elsif site == 'lyon'
+    o['storageList'] << {
+      'typeName' => 'data',
+      'name' => 'storage1',
+      'filesystemType' => 'NFS',
+      'size' => 75
+    }
+  elsif site == 'rennes'
+    o['storageList'] << {
+      'typeName' => 'data',
+      'name' => 'storage1',
+      'filesystemType' => 'NFS',
+      'size' => 100
+    }
+  elsif site == 'nancy'
+    o['storageList'] << {
+      'typeName' => 'data',
+      'name' => 'talc-data',
+      'filesystemType' => 'NFS',
+      'size' => 71 + 58 + 58
+    }
+    o['storageList'] << {
+      'typeName' => 'data',
+      'name' => 'talc-data2',
+      'filesystemType' => 'NFS',
+      'size' => 200
+    }
+  elsif site == 'sophia'
+    o['storageList'] << {
+      'typeName' => 'data',
+      'name' => 'storage1',
+      'filesystemType' => 'NFS',
+      'size' => 30
+    }
+  elsif site == 'luxembourg'
+    o['storageList'] << {
+      'typeName' => 'data',
+      'name' => 'storage1',
+      'filesystemType' => 'NFS',
+      'size' => 40
+    }
+  end
+  o['totalCoreNumber'] = o['clusterList'].map { |c| c['clusterCoreNumber'] }.sum
+  o['totalStorage'] = (o['clusterList'].map { |c| c['nodeType'].map { |n| n['localDisk'] * n['nodeNumber'] }.sum }.sum +
+    o['storageList'].map { |s| s['size'] }.sum).round(2)
+  JSON::Validator.validate!(mesos_schema, o)
+  File::open("grid5000-#{site}.json", "w") { |fd| fd.puts JSON::pretty_generate(o) }
+end
+