IRM de noix de coco

Au cours de la construction d’un modèle de tête pout l’IRM, j’ai fait des essais avec des noix de coco.

Voici quelques images produites avec BrainVisa/Anatomist des acquisitions d’une noix de coco à moitié pleine de liquide.

coco1-c coco2-c coco3-c coco4-c coco5-c

Publié dans Divers | 2 commentaires

WordPress ne détecte pas les mises à jour derrière un proxy

WordPress est utilisé pour un petit site web de mon boulot. Le serveur web qui l’héberge doit utiliser un proxy pour accéder au web, ce qui est nécessaire pour certains plugins et pour les mises à jour.

Le proxy est déclaré (comme spécifié dans la documentation) dans le fichier wp-config.php

define('WP_PROXY_HOST', 'myCache.fr');
define('WP_PROXY_PORT', '3128');

Depuis la version 3.8.1, le tableau de bord n’affiche aucune mise à jour disponible, alors que la version 3.9.1 est sortie. Idem pour les plugins. Aucun message d’erreur n’est affiché, y compris en forçant la vérification des mises à jour, et en activant les messages de debug de WordPress.
J’ai dû utiliser wireshark pour comprendre ce qu’il se passe :

  1. WordPress envoit une requête POST vers https://api.wordpress.org/plugins/info/1.0
  2. Le serveur proxy Squid répond avec une erreur 501 : « Unsupported Request Method and Protocol »

Il semble que le proxy ne supporte pas les requêtes POST en https. J’ai donc recherché où était définie l’URL https dans le code de WordPress. Elle se trouve dans wp-includes/update.php. Mais les URLs sont définies en http, et le code suivant transforme le protocole en https si ssl est disponible (à trois endroits différents):

if ( $ssl = wp_http_supports( array( 'ssl' ) ) )
                $url = set_url_scheme( $url, 'https' );

J’ai donc commenté ces lignes :

/**if ( $ssl = wp_http_supports( array( 'ssl' ) ) )
 $url = set_url_scheme( $url, 'https' );*/

La mise à jour a été détectée et elle s’est installée correctement. Bien sûr, je vais devoir refaire cette modification après chaque mise à jour…

Publié dans Notes Info | 3 commentaires

Disque dur mourant : récupérer mes données (GNU/Linux)

I y a 5 ans j’ai sauvegardé mes données vers 3 disques durs externes. Quand mon PC fixe est mort, j’ai utilisé un petit portable sur lequel je ne pouvais pas copier le Téraoctet de données de mes sauvegardes.

La semaine dernière, j’ai essayé de récupérer les données du portable et les anciennes sauvegardes sur un nouveau PC.
Sur les 3 disques de sauvegarde, un était complètement mort (il ne tourne plus quand on le branche), le second fonctionnait mais ne contenait qu’une sauvegarde partielle (disque plus petit que les autres), et le dernier n’était apparemment pas lisible.

Au branchement du disque, ‘dmesg’ affiche des dizaines d’erreurs de lecture, et Linux ne détecte aucune partition sur le disque. J’ai cru avoir perdu 1To de données…

Puis j’ai trouvé ça, ça et ça.

Et voilà comment j’ai presque tout récupéré :

  1. Brancher le disque mourant (/dev/sdi)
  2. Dans dmesg, il y avait des centaines de lignes comme ceci :
    [200135.764454] Buffer I/O error on device sdi, logical block 9464
    [200160.490454] sd 9:0:0:0: [sdi] Unhandled sense code
    [200160.490457] sd 9:0:0:0: [sdi]  
    [200160.490458] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
    [200160.490460] sd 9:0:0:0: [sdi]  
    [200160.490461] Sense Key : Medium Error [current] 
    [200160.490463] sd 9:0:0:0: [sdi]  
    [200160.490465] Add. Sense: Data phase error
    [200160.490466] sd 9:0:0:0: [sdi] CDB: 
    [200160.490467] Read(10): 28 00 00 01 27 c0 00 00 08 00
    [200160.490472] end_request: I/O error, dev sdi, sector 75712
  3. Faire une image du disque, en ignorant les erreurs de lecture (remplacées par des 0 dans le fichier). Ceci a pris 5 jours pour le disque de 1To.
    sudo dd if=/dev/sdi of=/home/myself/backupdisk.bin conv=noerror,sync
  4. Réparer la table de partition en utilisant testdisk (sudo apt-get install testdisk)
    I a trouvé une partition Linux (Ext3fs) remplissant l’image de disque, a proposé de réécrire la table de partition et a sauvé le résultat.

    testdisk /home/myself/backupdisk.bin
  5. Afficher la table de partition de l’image du disque
    sudo fdisk -lu /home/myself/backupdisk.bin
    Disk /home/myself/backupdisk.bin: 1000.2 GB, 1000204886016 bytes
    255 têtes, 63 secteurs/piste, 121601 cylindres, total 1953525168 secteurs
    Unités = secteurs de 1 * 512 = 512 octets
    Taille de secteur (logique / physique) : 512 octets / 512 octets
    taille d'E/S (minimale / optimale) : 512 octets / 512 octets
    Identifiant de disque : 0x00000000
    Périphérique Amorçage  Début         Fin      Blocs    Id. Système
    /home/myself/backupdisk.bin1   *          63  1953520064   976760001   83  Linux
  6. Définir la partition avec le périphérique loopback (« rebouclage »)
    D’après la table de partition, la partition commence au bloc 63 and l’unité est de 512 octets. Donc le début de la partition dans l’imageest à 32256 = 63*512 octets. On peut maintenant définir la partition /dev/loop0 pour qu’elle pointe vers la première partition de l’image du disque

    sudo losetup -o 32256 /dev/loop0  /home/myself/backupdisk.bin
  7. Vérifier et réparer la partition
    L’option -y est utilisée pour forcer fsck à corriger les erreurs sans poser de question (sinon il demande des milliers de fois de confirmer, par exemple « Le décompte des i-noeuds libres est erroné pour le groupe n°7448 (16384, décompté=16334). Corriger ? »)

    sudo fsck  -y /dev/loop0
    
    #  Ca se termine par:
    /dev/loop0: ***** LE SYSTÈME DE FICHIERS A ÉTÉ MODIFIÉ *****
    /dev/loop0 : 968660/122109952 fichiers (3.9% non contigus), 234454083/244190000 blocs
  8. Monter la partition
    sudo mkdir /mnt/backupdisk
    sudo mount /dev/loop0 /mnt/backupdisk
  9. Accéder aux fichiers et les copier depuis /mnt/backupdisk
  10. Démonter la partition et détacher le périphérique loopback
    sudo umount /mnt/backupdisk
    sudo losetup -d /dev/loop0
Publié dans Notes Info | Laisser un commentaire

IRM d’Ananas

Vers 2010, au cours d’un développement de séquences IRM pour le cerveau, nous avons utilisé un ananas pour tester les effets de certains paramètres sur les images.

Voici quelques-unes de ces images visualisées avec Anatomist (à partir d’une IRM Philips 3 Tesla). La plupart sont pondérées des séquences 3D pondérées en T1, les plus belles (plus haute résolution) sont des T2 multi-coupes.T2yellowred3-c T2red-c T2blackwhite2-c T2actif-ret-c T1blackwhite3-c T1blackwhite2-c T1blackwhite1-c

 

Publié dans Divers | Laisser un commentaire

Pour en finir avec le vote utile

Vote utile

A chaque élection en France, les grands partis incitent les électeurs à « voter utile » dès le premier tour, pour ne pas risquer, par exemple, l’élimination de la gauche du deuxième tour (élection présidentielle de 2002 par exemple), ou l’élection de la gauche par division du vote de droite.
Dernièrement aux municipales de 2014, une autre variante de la même idée a été affichée partout, en particulier par le parti socialiste : le « désistement républicain« . En cas de présence de l’extrême droite au second tour, les partis républicains devraient se désister en faveur de celui d’entre eux qui a obtenu le plus de voix au premier tour. Ce principe a par ailleurs été fortement remis en cause par le refus de l’UMP de l’appliquer, ce qui a signifié que le PS s’est désisté en faveur de l’UMP, mais sans la réciproque.
On arrive ainsi à un chantage envers les électeurs : si vous ne votez pas pour nous, vous risquez d’obtenir le pire (l’extrême droite). Quel paradoxe pour l’idée même de démocratie ! Les électeurs ne seraient donc pas libres de voter pour le parti qui leur convient le mieux, mais devraient par sens des responsabilités soutenir les plus gros partis, donc « voter contre » plutôt que « voter pour ». Mais d’où vient ce paradoxe ?

Mode de scrutin : le problème et une solution

Dans notre système de vote actuel s’il y a deux listes de gauche G1 et G2, une liste de droite D et une liste d’extrême droite ED au second tour, si elles obtiennent respectivement 25.9%(G1), 26.1%(G2), 21%(D), 27%(ED) des voix, c’est l’extrême droite qui gouvernera même si 52% des électeurs voulaient des élus de gauche !
La solution est simple : changer de mode de scrutin !
Il existe de nombreux modes de scrutins qui n’ont pas ce problème.
J’en présenterai un ici : le vote unique transférable.

  1. Chaque électeur classe dans son bulletin les candidats selon son ordre de préférence (il n’est pas obligatoire de lister tous les candidats) :
    • le bulletin d’un électeur de gauche pourrait par exemple contenir G1, G2, D
    • le bulletin d’un électeur de droite qui refuse l’extrême droite pourrait être D, G2, G1
    • le bulletin d’un électeur de droite qui préfère l’extrême-droite à la gauche D, ED
  2. Pour compter les résultats, c’est assez simple : on commence par compter le premier choix de tous les bulletins. On obtient alors le même résultat qu’avec le type de scrutin actuel : 25.9%(G1), 26.1%(G2), 21%(D), 27%(ED)
  3. On élimine alors le candidat le plus mal placé, ici D avec 21%. On reprend alors les bulletins du candidat éliminé, et on répartit les bulletins vers les candidats restants en lisant le second choix des électeurs de D (s’il n’y en a pas, on élimine le bulletin).
    On a alors un nouveau résultat : 35.9% (G1), 30.1% (G2), 32% (ED). Le total ne fait pas 100% car certains électeurs de D n’avaient pas fait de second choix.
  4. On élimine alors le parti G2 minoritaire, et on procède de la même façon : les voix comptées pour G2 sont redistribuées en fonction du choix suivant dans les bulletins (si le choix suivant est D qui a déjà été éliminé, on passe au suivant dans la liste).
    On obtient alors le résultat suivant :
    61% (G1) et 33% (ED).
    Le parti vainqueur est donc le parti G1.

Ainsi, en permettant aux électeurs de préciser leur ordre de préférence, on peut leur permettre de s’exprimer en toute liberté, sans craindre que leur vote soit inutile, c’est-à-dire non compté car leur parti préféré a été éliminé.
Il est bien sûr possible de rajouter le vote blanc dans la liste, et ainsi d’exprimer qu’aucun candidat ne nous convient vraiment, tout en précisant dans les choix suivants l’ordre de préférence pour ne pas « laisser passer » un parti qu’on refuse plus que les autres.

Pour quand ce changement ?

Pourquoi donc, s’il y a des solutions, ne sont-elles pas mises en place ? En fait, ces solutions sont déjà en place dans plusieurs pays (Australie, Nouvelle Zélande, Irlande…). Les principaux bénéficiaires de ce système sont les grands partis (PS et UMP en France),  et il serait donc surprenant que leurs représentants, voulant garder leur place ultra majoritaire à l’Assemblée Nationale et au Sénat, se risquent à proposer un changement. Et c’est ainsi qu’on transforme la diversité des opinions en quasi bipartisme !

 

En conclusion, un extrait approprié du « Guide du voyageur galactique » de Douglas Adams :
Dans ce monde, le peuple est composé de gens, et les chefs sont des lézards. Le peuple déteste les lézards et les lézards dirigent le peuple »
« Bizarre » dit Arthur, « J’ai cru que tu disais que c’était une démocratie. »
« C’est ce que j’ai dit » dit Ford. « C’en est une »
« Alors, » dit Arthur […] « pourquoi le peuple ne se débarrasse pas des lézards ? »
« Honnêtement ça ne leur vient pas à l’esprit, » dit Ford. « Ils ont le droit de vote, alors ils supposent plus ou moins que le gouvernement qu’ils ont élu est approximativement le gouvernement qu’ils veulent. »
« Tu veux dire qu’ils votent réellement pour les lézards ? »
« Oh oui, » dit Ford en haussant les épaules, « bien sûr. »
« Mais, » dit Arthur […] « pourquoi ? »
« Parce que s’ils ne votaient pas pour un lézard, » dit Ford, « le mauvais lézard pourrait être élu ! »

Publié dans news, Politique & société | Laisser un commentaire

Version alpha de PyVotons! (web)

Enfin je mets en ligne la version web de PyVotons!.

C’est un logiciel en phase alpha, difficile à installer, et dont beaucoup de fonctions ne sont pas encore implémentées.

Vous devriez lire la page Documentation avant de tenter de l’installer.
Il vous faut Python et python-openssl, sur un système GNU/Linux.

Voici le logiciel :

pyvotons-2014-04-15.tar.bz2

Publié dans Development, news | Laisser un commentaire

Sauvegarde de bases MySQL

Objectif : Sauver les bases de données MySQL vers un « partage windows » (ici un serveur Samba) depuis un serveur Ubuntu Linux.

On conservera une sauvegarde par jour avec trente jours d’historique.
C’est quelque chose de relativement simple mais que je garde sous la main ici au cas où je devrai le refaire… Voici le code d’un petit script bash script fait rapidement (pas optimal, je sais). Mais pour commencer, il faut :

# Créer un utilisateur de la base de données avec accès en lecture pour les sauvegardes
mysql -u root -p
mysql> GRANT SELECT,RELOAD,LOCK TABLES ON *.* TO ‘backup_user’@’localhost’;

# Ajouter root au groupe mysql (car cron lance les tâches en tant que root)
sudo usermod -a -G mysql root

# Autoriser l’accès en lecture aux fichiers de mysql pour les utilisateurs du groupe mysql
sudo find /var/lib/mysql -maxdepth 1 -type d -exec chmod g+r \{\} \;
# Lister les bases de données
mysql -u backup_user -B -e ‘show databases;’
# En sauvegarder une (pour tester)
mysqlhotcopy -u backup_user databaseName /TheBackupDir/
# Sauver toutes les bases -> Ca ne marche pas car certaines bases listées ne sont pas accessibles
for i in `mysql -u backup_user -B -e ‘show databases;’`; do echo $i; mysqlhotcopy -u backup_user « $i » /TheBackupDir/ ; done

# Sur le partage samba, créer un répertoire WebServerBackup et ajouter un fichier vide autoBackup pour que le script puisse détecter qu’il a accès au bon répertoire, et qu’il a été monté correctement avant de lancer la sauvegarde.

Le partage windows utilisé ici n’a pas besoin de login et de mot de passe, mais dans le cas contraire, il suffit d’ajouter user= »myuser »,password= »mypassword » à la liste des options de la commande « mount ».

Le répertoire /var/www dir sera sauvé (sans conserver d’anciennes versions) dans le même répertoire, et sera mis à jour avec rsync. Un log sera écrit dans /var/log/backup.log

Voici le script :

#!/bin/bash
echo >> /var/log/backup.log
echo >> /var/log/backup.log
day=$(date +%d-%m-%Y_%Hh%M)
# List of databases to backup. Unfortunately, 
#   mysql -u backup_user -B -e 'show databases;'
#   lists system databases that cannot be saved. Using manual list instead in the for loop
echo "-------------------------------------------------" >> /var/log/backup.log
echo "Beginning WebServer Backup -> Launching backupWebsite.sh" >> /var/log/backup.log
date -R >> /var/log/backup.log
mount -t cifs -o iocharset=utf8,file_mode=0777,dir_mode=0777 //MySambaServer/MySambaShare  /backupDisk
if [ -f "/backupDisk/WebServerBackup/autoBackup" ]
then
  rsync -rltp --del --stats  /var/www /backupDisk/WebServerBackup/www >> /var/log/backup.log # --del
  mkdir "/backupDisk/WebServerBackup/mysql_$day"
  # Backup all databases
  for i in 'mybase' 'wordpress' 'mysql'; 
    do 
      echo $i; mysqlhotcopy -u backup_user "$i" "/backupDisk/WebServerBackup/mysql_$day" >> /var/log/backup.log ;
    done
  # Remove the old database backups (more than 30 days)
  find /backupDisk/WebServerBackup -maxdepth 1 -type d -name "mysql_*" -mtime +30 -exec rm -rf \{\} \;
else
  echo "Echec du montage ! ***Pas de sauvegarde du serveur web !***" >> /var/log/backup.log
fi

umount /backupDisk

echo "WebServer BACKUP FINISHED (in //MySambaServer/MySambaShare/WebServerBackup )" >> /var/log/backup.log
date -R >> /var/log/backup.log
echo "**************************************************" >> /var/log/backup.log

Enfin, on met un lien vers ce script (rendu exécutable par un sudo chmod a+x /usr/bin/backupWebsite.sh) dans le dossier /etc/cron.daily/ pour qu’il se lance une fois par jour, mais sans l’extension .sh car certaines versions de cron ignorent les fichiers en .sh :

sudo ln -s /usr/bin/backupWebsite.sh /etc/cron.daily/backupWebsite

Publié dans Notes Info | Laisser un commentaire

Bientôt une version alpha de WebPyVotons!

Après plusieurs mois d’inactivité, le projet PyVotons! a progressé rapidement ces dernières semaines. Une version alpha devrait être publiée rapidement sur ce site.

Cette version devrait permettre de créer un compte, créer un scrutin, inscrire des électeurs à ce scrutin, leur permettre de voter puis afficher les résultats.

Techniquement, PyVotons est composé d’une interface graphique qui s’exécutera dans le navigateur de la machine cliente (python traduit en javascript par Pyjamas) et d’un serveur HTTPS écrit en python qui utilise MySQL pour stocker les données utilisateur.

Il restera cependant beaucoup de travail à faire :

  • présentation avec CSS et nouvelles icônes
  • documentation et nettoyage du code
  • meilleure gestion des erreurs
  • support des traductions
  • support des groupes d’utilisateurs
  • sécurisation (en particulier contre les DOS)
  • envoie d’e-mail de confirmation d’inscription et d’appel aux votes
  • support d’autres bases de données que MySQL (sqllite par exemple), support de serveurs d’application python…).

 

Publié dans Development, news | Laisser un commentaire

Bibtex, latex, elsarticle et nodots : message d’erreur incompréhensible

Au cas où cela pourrait aider quelqu’un, voici la solution d’un problème que j’ai eu en utilisant latex et bibtex avec la classe d’article Elsevier et le style de bibliographie model4-names.bst…

Tout d’abord, utiliser seulement
\documentclass{elsarticle}
et
\bibliographystyle{model4-names}
\bibliography{myBiblio}
n’affiche pas le nom d’auteur et la date, mais juste un numéro [1].

Solution : \documentclass[authoryear]{elsarticle} (d’après le fichier exemple disponible sur http://support.river-valley.com/wiki/index.php?title=Model-wise_bibliographic_style_files)

Le problème de ce style, c’est que les noms d’auteurs sont affichés dans la bibliographie avec beaucoup de points (Smith, S.J., au lieu de Smith SJ).
La solution donnée est d’utiliser le package numcompress.sty de cette façon : \usepackage[nodots]{numcompress}

J’ai rencontré avec ceci une erreur incompréhensible, et je n’ai pas trouvé la solution sur le web (c’est pour cela que je la publie ici) :

(./MyBiblio.bbl [12]
Runaway argument?
467, 469\@nil \else \ifx \next \@@@au \bibauthor {467, 469}\else 467,\ETC.
! Paragraph ended before \@@bibpages was complete.
<to be read again>
\par
l.514

?

Dans le fichier MyBiblio.bib, quelques @article n’étaient pas définis correctement, en particulier la propriété « page ». J’ai dû remplacer pages = {185,190} par la version correcte pages = {185–190}, et supprimer pages={3} pour un autre article, parce que numcompress.py causait l’erreur ci-dessus.

 

Publié dans Notes Info | 4 commentaires

DroboFS : une page d’informations avec PHP

J’ai acheté récemment un disque réseau DroboFS pour le stockage de données de mon labo. C’est du bon matériel, mais j’ai été très surpris de voir que la seule façon de le configurer est le Drobo Dashboard, un logiciel Windows/Mac qui doit être lancé sur un ordinateur branché au même sous-réseau que le DroboFS (il envoie des paquets broadcast pour localiser le DroboFS, et il est impossible de lui préciser l’adresse IP du DroboFS). Dans mon cas, le DroboFS est branché sur un autre sous-réseau (et dans un autre bâtiment), donc l’autodétection ne fonctionne pas.

Pourquoi n’y a-t-il pas d’interface web, comme sur le Iomega StorCenter Ix2, un disque réseau beaucoup plus bas-de-gamme ? Cela permettrait de l’administrer à distance depuis n’importe quel système !

Un projet récent pour développer un webdashboard existe, mais c’est un projet d’un utilisateur qui à ce jour ne comporte que très peu de code fonctionnel.

En me basant sur ce code, j’ai écrit une page PHP (très mal codée, car je n’ai utilisé PHP qu’une fois ces 10 dernières années) qui affiche les données que j’ai pu récupérer (configuration des dossiers partagés et statut du DroboFS).

edit : captures d’écran anonymisées

Je ne peux toujours pas ajouter des utilisateurs, leur permettre de changer leur mot de passe, ajouter un dossier partager ou changer les droits d’accès, mais c’est un début !

Voici le code source, en espérant qu’il pourra servir à d’autres.

Bug connu : si vous n’avez pas défini d’utilisateur, le script plantera.

Instructions pour l’utiliser : activer les DroboApps sur votre DroboFS (avec Dashboard donc), installer la droboapp Apache, et placer le fichier ci-dessous, nommé droboStatus.php, dans le répertoire /DroboApps/apache/www de votre Drobo. Pour voir le résultat, il suffit de se connecter à http://droboIP:8080/droboStatus.php

 

 

<html>
 <head>
   <title>PHP Test</title>
 </head>
 <body>
  <?php
/*
 droboStatus;php : a simple php page to display the status of a DroboFS
 Copyright (C) 2011 Manik Bhattacharjee - manik-listes@altern.org 

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/***************************** A short (and incomplete) XML parsing function **********************************************/
  function parseBasicXML(&$sourcexml, $markOpen = "DRIShareConfig") {
    //echo "<br>Parsing...<br>";
    $root = array();
    $index = 0;
        // Searching for markups
      while (preg_match("/([^<]*)<([\s\w\/]+)>/", $sourcexml, $matches, PREG_OFFSET_CAPTURE) ) {
          //echo "WHILE ";
          $index = $index + 1;
          // For each markup
          $content = $matches[1][0];
          $markup = $matches[2][0];
          //echo strlen($sourcexml);
          //echo "$markup<br/>";
          // Remove this part from the string
          $sourcexml = substr($sourcexml, strlen($markup) + 1 +  $matches[2][1]);
          //echo "Match : $markup\n";

            // 3 Cases : <Username>, </Username>, and <Username />
            // Entering : call the function recursively, it will return a new node
            if (preg_match("/^\s*(\w+)\s*$/", $markup, $mark)){
              //echo "New mark ".$mark[1]." <br>\n";
              $root[$mark[1].$index] = parseBasicXML($sourcexml, $mark[1]);
            }
            // Open/Closed markup (e.g. <br /> : add it as a child with no value
            elseif (preg_match("/^\s*(\w+)\s*\/$/", $markup, $mark)){
                  $root[$mark[1].$index] = "novalue";
                  //echo "OpenClosed mark ".$mark[1]."<br>";
            }
            // Closed Markup : if it really is our markOpen markup, return ! Otherwise, there is something very wrong !
            elseif (preg_match("/^\s*\/\s*(\w+)\s*$/", $markup, $mark)){
              if ($mark[1] === $markOpen){
                //echo "Closing mark ".$mark[1]." with content $content<br>";
                $root["content"] = $content;
                return $root;
              }else{
                echo "Unexpected input $lt;/".$mark[1].", whereas $markOpen was expected.";
              }

          }else{
            echo "Unidentified markup : $markup";
          }
      //echo "<br>";
    }
    return $root;
  }
/********************************END XML Parsing function********************************************************************/

/***************************** Sub Functions for Drobo configuration Analysis****************************************************************/
  function yesOrNo($val){
    if($val == 0){ return "No";}else{return "Yes";}
  }

  function userRights($r){
    if ($r == 0){ return "<font color=\"orange\">Read only</font>";}elseif($r == 1){ return "<font color=\"green\">Read/Write</font>";}else{return "Unknown rights !";}
  }

  function displayPassword($val){ return '***';}

  function displayUser($us){
    echo "<tr><td><font color = \"blue\"><b>".$us['Username1']['content']."</b></font></td><td>".yesOrNo($us['ValidPassword3']['content'])."</td><td>".yesOrNo($us['EncryptedPassword4']['content'])."</td><td><font color=\"gray\">".displayPassword($us['Password2']['content'])."</font></td></tr>\n";
  }

  function displayShare($sh){
// echo '<table border="1"><tr><td><b>ShareName<b></td><td>UserName</td><td>Rights</td><td>ShareState</td><td>TimeMachineEnabled</td><td>ShareMaxTMSizeGB</td><td>OldShareName</td></tr>'."\n";
    echo "<tr bgcolor=\"#aaaaaa\"><td><font color = \"blue\"><b>".$sh['ShareName1']['content']."</b></font></td><td></td><td></td><td>".$sh['ShareState2']['content']."</td><td>".yesOrNo($sh['TimeMachineEnabled3']['content'])."</td><td>".$sh['ShareMaxTMSizeGB4']['content']."</td><td>".$sh['OldShareName6']."</td></tr>\n";
    $su = $sh['ShareUsers5'];
    $index = 1;
    while($su["ShareUser$index"]){
      echo '<tr><td></td><td><font color = "blue">'.$su["ShareUser$index"]['ShareUsername1']['content'].'</font></td><td>'.userRights($su["ShareUser$index"]['ShareUserAccess2']['content']).'</td><td></td><td></td><td></td><td></td></tr>'."\n";
      $index = $index+1;
    }
    //echo "</table>\n";
  }
/**************************************End of Sub Functions for Drobo configuration Analysis**************************************************************/

/********************************** Drobo Configuration Analysis and display ******************************************************************/
  function displayNASconf($nc){

    // USERS
    echo "<h2><center>Users</center></h2>\n";
    echo '<center><table border="1"><tr><td><b>UserName</b></td><td>Valid Password</td><td>Encrypted Password</td><td width = "50">Password</td></tr>'."\n";
    $users = $nc["DRIShareConfig1"]["UserList2"];
    $index = 1;
    while($users["User$index"]){
      displayUser($users["User$index"]);
      $index = $index+1;
    }
    echo "</table></center>\n<br/><br/><br/><br/>\n";

    // SHARES
    echo "<h2><center>Shares</center></h2>\n";
    $shares = $nc["DRIShareConfig1"]['Shares3'];
    $index = 1;
    echo '<center><table border="1"><tr><td><b>ShareName<b></td><td>UserName</td><td>Rights</td><td>ShareState</td><td>TimeMachineEnabled</td><td>ShareMaxTMSizeGB</td><td>OldShareName</td></tr>'."\n";
    while($shares["Share$index"]){
      displayShare($shares["Share$index"]);
      echo "\n";
      $index = $index+1;
    }
    echo "</table></center>\n<br/><br/><br/><br/>\n";
  }

/*********************************** End of Drobo Configuration Analysis and display ************************************************/

/******************************** Sub Functions for Drobo Status Analysis *******************************************************************/
  function getCurrentStatus(){
    //echo '<form><textarea cols="120" rows="40">';
    $cfgServer = "localhost";
    $cfgPort    = 5000;
    $cfgTimeOut = 10;

    $drobofs = fsockopen($cfgServer, $cfgPort, $errno, $errstr, $cfgTimeOut);
    $statusxml = "";
    if (!$drobofs) {
      echo "Connexion failed : $errstr\n";
    } else {
      // read lines until end of "</ESATMUpdate>" is seen
      while (!feof($drobofs)) {
        $line = fgets($drobofs, 128);
        $statusxml = $statusxml.$line; 
        if (strpos($line, "</ESATMUpdate>") === 0){ break;}
      }
    }
    if ($drobofs) {
      fclose($drobofs);
    }

    return parseBasicXML($statusxml);
  }

  function simpleString($val){
    if ($val === 'novalue'){ return "undefined";}else{return $val['content'];}
  }

  function simpleMask($val){
    return base_convert($val['content'], 10, 2).'b';
  }

  // From http://code.google.com/p/drobowebdashboard/wiki/ESATMUpdate#ESATMUpdate/mStatus : should analyze the bitmask...
  function mStatus($val){
    //$ret = $val['content'].' = '.base_convert($val['content'], 10, 2).'b -> ';
    $ret = '';
    switch ($val['content']){
      case 32768:
        return $ret.'<font style="BACKGROUND-COLOR: green" color="white">Everything ok, or undergoing relayout</font>';
      case 32772:
        return $ret.'<font style="BACKGROUND-COLOR: yellow" color="black">Yellow capacity warning, replace first drive</font>';
      case 32774:
        return $ret.'<font style="BACKGROUND-COLOR: red" color="white">Red capacity warning, replace first drive</font>';
      case 32784:
        return $ret.'<font style="BACKGROUND-COLOR: red" color="white">Bad drive in 3rd bay</font>';
      case 33344:
        return $ret.'<font style="BACKGROUND-COLOR: orange" color="black">Rebuilding</font>';
    }
    $ret = $val['content'].' = '.base_convert($val['content'], 10, 2).'b -> ';
    return $ret.'<font color="red">UNKNOWN STATUS - please see http://code.google.com/p/drobowebdashboard/wiki/ESATMUpdate#ESATMUpdate/mStatus</font>';
  }

  function capacity($val){
    // Cannot just divide by 1024*1024*1024 because drobo's php does not support large numbers (above 2 Gb) : just remove the last digits for display
    $gb = substr($val['content'],0,-9);
    $tb = substr($val['content'],0,-12);
    return $gb.' Gb / '.$tb.' Tb.';
    // return $val['content'].' bytes / '.$gb.' Gb / '.$tb.' Tb.';
  }

  function emailConfig($val){
    if ($val['content'] == 0){ return "Email alerts <b>not</b> enabled ";} elseif($val['content'] == 1) { return "Email alerts enabled ";} else {return 'Email alert : <b>unknown</b> status';}
  }

  function mFirmwareFeatureStates($val){
    if($val['content'] == 6){ return '<b>Single redundancy</b>'; } elseif($val['content'] == 7){return '<b>Double redundancy</b>';}else{return '<b>Redundancy status unknown !</b>';}
  }

  function slotmStatus($val){
    //$ret = $val['content'].' = '.base_convert($val['content'], 10, 2).'b -> ';
    $ret="";
    switch ($val['content']){
      case 1:
        return $ret.'<font color="white" style="BACKGROUND-COLOR: red">Solid red light (add/upgrade drive immediately)</font>';
      case 2:
        return $ret.'<font color="black" style="BACKGROUND-COLOR: yellow">Solid yellow light (add/upgrade drive soon)</font>';
      case 3:
        return $ret.'<font color="white" style="BACKGROUND-COLOR: green">Solid green light (everything ok)</font>';
      case 4:
        return $ret.'<font color="yellow" style="BACKGROUND-COLOR: green">Blinking green/yellow light (relayout)</font>';
      case 128:
        return $ret.'<font color="gray">No light (empty slot)</font>';
      case 134:
        return $ret.'<font color="black" style="BACKGROUND-COLOR: red">Blinking red light (defective drive, replace immediately)</font>';
    }
    return $ret.'<font color="red">UNKNOWN STATUS - please see http://code.google.com/p/drobowebdashboard/wiki/ESATMUpdate#ESATMUpdate/mSlotsExp/n/mStatus</font>';
  }

/****************************** End of sub Functions for Drobo Status Analysis *********************************************/

/*********************************** Drobo Status Analysis and display ************************************************/

  function displayCurrentStatus($cs, $fullOrNot = 1){
    $params = array('mESAUpdateSignature1' => array('?', 'simpleString'),
             'mESAUpdateVersion2' => array('?', 'simpleString'),
             'mESAUpdateSize3' => array('?', 'simpleString'),
             'mESAID4' => array('? - serial number', 'simpleString'),
             'mSerial5' => array('Serial number of the device ', 'simpleString'),
             'mName6' => array('Name of the device', 'simpleString'),
             'mVersion7' => array('Firmware version', 'simpleString'),
             'mReleaseDate8' => array('Release date of the firmware ', 'simpleString'),
             'mArch9' => array('Hardware architecture', 'simpleString'),
             'mFirmwareFeatures10' => array('?', 'simpleString'),
             'mFirmwareTestFeatures11' => array('?', 'simpleString'),
             'mFirmwareTestState12' => array('?', 'simpleString'),
             'mFirmwareTestValue13' => array('?', 'simpleString'),
             'mStatus14' => array('Overall status of the device', 'mStatus'),
             'mRelayoutCount15' => array('? (Speculation: number of relayouts in the device\'s history)', 'simpleString'),
             'mTotalCapacityProtected16' => array('Total capacity', 'capacity'),
             'mUsedCapacityProtected17' => array('Used capacity', 'capacity'),
             'mFreeCapacityProtected18' => array('Free capacity', 'capacity'),
             'mTotalCapacityUnprotected19' => array('?', 'capacity'),
             'mUsedCapacityOS20' => array('?', 'capacity'),
             'mYellowThreshold21' => array('Threshold of yellow warning about capacity, in 100th of a percent', 'simpleString'),
             'mRedThreshold22' => array('Threshold of red warning about capacity, in 100th of a percent', 'simpleString'),
             'mUseUnprotectedCapacity23' => array('?', 'capacity'),
             'mRealTimeIntegrityChecking24' => array('?', 'simpleString'),
             'mStoredFirmwareTestState25' => array('?', 'simpleString'),
             'mStoredFirmwareTestValue26' => array('?', 'simpleString'),
             'mDiskPackID27' => array('?', 'simpleString'),
             'mDroboName28' => array('?', 'simpleString'),
             'mConnectionType29' => array('?', 'simpleString'),
             'mSlotCountExp30' => array('Maximum number of drive bays (slots)', 'simpleString'), 
             'mSlotsExp31' => array('Container tag for slots status', 'simpleString'),  // SUBFUNCTION
             'mLUNUpdates32' => array('Container tag for?', 'simpleString'), //SUBFUNCTION
             'mFirmwareFeatureStates33' => array('?', 'mFirmwareFeatureStates'),
             'mLUNCount34' => array('?', 'simpleString'),
             'mMaxLUNs35' => array('?', 'simpleString'),
             'mSledName36' => array('?', 'simpleString'),
             'mSledVersion37' => array('?', 'simpleString'),
             'mShareCount38' => array('?', 'simpleString'),
             'mShareInfo39' => array('?', 'simpleString'),
             'mSledStatus40' => array('?', 'simpleString'),
             'mSledSerial41' => array('?', 'simpleString'),
             'mDiskPackStatus42' => array('?', 'simpleString'),
             'DNASStatus43' => array('?', 'simpleString'),
             'DNASConfigVersion44' => array('?', 'simpleString'),
             'DNASDroboAppsShared45' => array('? (Speculation: indicates whether DroboApps are enabled)', 'simpleString'),
             'DNASDiskPackId46' => array('? (Speculation: unique ID for the disk pack) ', 'simpleString'),
             'DNASFeatureTable47' => array('?', 'simpleString'),
             'DNASEmailConfigEnabled48' => array('Indicates the state of email alerts', 'emailConfig'),
             'content' => array('?', 'simpleString')
);
    $cs = $cs['ESATMUpdate1'];

    //Display slots
    $slots = $cs['mSlotsExp31'];
    $index = 0;
    if ($fullOrNot){
      echo "<center><table border = \"1\">\n<tr><td><b>Slot number</b></td><td><b>Status</b></td><td><b>Capacity</b></td><td>Make</td><td>Model</td><td>ESAID</td></tr>\n";
    }else{
      echo "<center><table border = \"1\">\n<tr><td><b>Slot number</b></td><td><b>Status</b></td><td><b>Capacity</b></td></tr>\n";
    }
    while($slots['n'.$index.($index+1)]){
      $slot = $slots['n'.$index.($index+1)];
      if ($fullOrNot){
        echo '<tr><td>'.simpleString($slot[mSlotNumber1]).'</td><td>'.slotmStatus($slot[mStatus2]).'</td><td>'.capacity($slot[mPhysicalCapacity6]).'</td><td>'.simpleString($slot[mMake4]).'</td><td>'.simpleString($slot[mModel5]).'</td><td>'.simpleString($slot[mESAID3])."</td></tr>\n";
      }else{
        echo '<tr><td>'.simpleString($slot[mSlotNumber1]).'</td><td>'.slotmStatus($slot[mStatus2]).'</td><td>'.capacity($slot[mPhysicalCapacity6])."</td></tr>\n";
      }
      $index = $index+1;
    }
    echo "</table></center>\n";

    if ($fullOrNot){
      // Display all parameters
      echo "<br><table border = \"1\">\n<tr><td>VarName</td><td>Comment</td><td>Value</td></tr>\n";
      foreach ($cs as $key => $val) {
        if (!$params[$key][1]){
          echo "<br><br><br>UNKNOWN param for $key !<br><br><br>";
        }
        echo '<tr>';
        echo "<td>$key</td><td>".$params[$key][0].'</td><td>'.$params[$key][1]($val).'</td>';
        echo "</tr>\n";
      }
      echo "</table>\n";

    }else{ // Just display the comment and value for the most usefull items

      echo "<br><center><table border = \"1\">\n";
      $keys = array('mName6', 'mStatus14', 'mTotalCapacityProtected16', 'mUsedCapacityProtected17', 'mFreeCapacityProtected18', 'mVersion7', 'DNASEmailConfigEnabled48');
      foreach ($keys as $key){
        echo '<tr><td>'.$params[$key][0].'</td><td>'.$params[$key][1]($cs[$key])."</td></tr>\n";
      }
      echo "</table></center>\n";
    }

  }

/*********************************** End of Drobo Status Analysis and display ************************************************/

/****************************** THE PAGE CONTENT **********************************************************************/

  // Parameter : full details or not ?
  if($_GET["full"]){ $full = 1;} else {$full = 0;}
  if ($full == 0){echo '<h3><a href="droboStatus.php?full=1">Display full details</a></h3>';}else{echo '<h3><a href="droboStatus.php">Display summary</a></h3>';}

  // Get the shares configuration
  exec("cat /mnt/DroboFS/System/DNAS/configs/shares.conf", $output);
  // Make one string from it
  $output = implode(" ", $output);
  // Parse and display it
  $nasconf = parseBasicXML($output);
  displayNASconf($nasconf);

  // Get the status from port 5000 and display it
  echo '<h2><center>Drobo Current Status</center></h2>';
  displayCurrentStatus(getCurrentStatus(),$full);

  ?> 
 </body>
</html>
Publié dans Notes Info | Laisser un commentaire