Coconut MRI

When trying to construct a head model for MRI studies, I did some experiments with coconuts.

Here are some images using BrainVisa/Anatomist for display of the acquisitions from a coconut still half full of juice.

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

Posted in Misc. | 2 Comments

WordPress behind proxy does not detect updates

WordPress is used for a small website in my workplace. The server must use a proxy to connect to the web, and this is necessary for many plugins and for updates.

As explained in the documentation, the proxy is declared in wp-config.php

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

Since 3.8.1, the Dashboard does not show any update available (even if now 3.9.1 is out). No error message is displayed either.
I had to run wireshark to see what was happening :

  1. WordPress sends a POST request to https://api.wordpress.org/plugins/info/1.0
  2. Squid proxy answers Error 501 : “Unsupported Request Method and Protocol”

It seems that the proxy does not support HTTPS POST requests. So I had to find where the https URL was defined in WordPress. It turns out it is in wp-includes/update.php. But the URLs are defined as http, and the following code is used (in three different places) to convert it to https if ssl is available :

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

I thus commented those :

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

And I could update the server. Of course, I will have to do that again after each update…

Posted in TechTips | 3 Comments

Dying hard disk : getting my data back (GNU/Linux)

Five years ago I made backups of my data to three hard drives. When my desktop died, I used a small laptop where I could not copy the 1+Tb of data stored on the backups.

Last week, I tried to copy back all the data from my laptop and the old data from my Backups onto a new computer.
Of the three backup disks, one was completely dead (it did not even power up), the second one worked but contained only a partial backup (it was a smaller disk), and the last one was apparently unreadable.

When this disk was plugged in, ‘dmesg’ was printing ‘Read errors’ all over the place, and Linux did not detect any partition on the disk. I thought I had lost 1Tb of data…

Then I found this, this and this.

And here is how I got almost everything back :

  1. Plug in the dying disk (/dev/sdi)
  2. In dmesg, I had hundreds of lines such as :
    [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. Make an image of the disk, ignoring read errors (replaced by 0’s in the file). This took 5 days for 1Tb.
    sudo dd if=/dev/sdi of=/home/myself/backupdisk.bin conv=noerror,sync
  4. Repair the partition table using testdisk (sudo apt-get install testdisk)
    It found one Linux (Ext3fs) partition filling up the disk image, asked to rewrite the partition table and saved the result.

    testdisk /home/myself/backupdisk.bin
  5. Display the partition table in the disk image
    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. Set up the partition using the loopback device
    From the partition table, the partition begins at block 63 and units are 512 bytes. So the offset of the partition in the image is 32256 = 63*512. We can now set the /dev/loop0 partition as the first partition of the disk image

    sudo losetup -o 32256 /dev/loop0  /home/myself/backupdisk.bin
  7. Check and repair the partition
    Option -y is used to force fsck to correct errors (without it, it asked for confirmation thousands of times, such as “Free inodes count is wrong for group n°7230 (16384, should be=16378). Correct ?)

    sudo fsck  -y /dev/loop0
    
    #  This ends with :
    /dev/loop0: ***** FILE SYSTEM WAS MODIFIED *****
    /dev/loop0 : 968660/122109952 files (3.9% non contiguous), 234454083/244190000 blocks
  8. Mount the partition and access files
    sudo mkdir /mnt/backupdisk
    sudo mount /dev/loop0 /mnt/backupdisk
  9. Explore files and copy them from /mnt/backupdisk
  10. Unmount the partition et free the loopack device
    sudo umount /mnt/backupdisk
    sudo losetup -d /dev/loop0
Posted in TechTips | Leave a comment

Pineapple MRI

Around 2010, while developping MRI sequences for brain imaging, we used an pineapple to test the effect of some parameters on the images.

Here are some images rendered with Anatomist (from a Philips 3 Tesla scanner). Most are T1-weighted 3D sequences, the most beautiful ones are T2 2D multi-slices sequences

T2yellowred3-c T2red-c T2blackwhite2-c T2actif-ret-c T1blackwhite3-c T1blackwhite2-c T1blackwhite1-c

Posted in Misc. | Leave a comment

Ending tactical voting

[To be translated…]

Tactical voting

Before each election in France, the largest political parties call for “tactical voting” (‘vote utile’, litterally ‘useful vote’) in the first round of our two-round elections to avoid a risk, e.g. the left being removed for the second round (like in Presidential elections of 2002 which saw a right/far-right second round) or the election of the left with a right majority as the vote is split between multiple right-wind candidates. The good citizen should thus vote for the main party of its side of the political spectrum.
Lastly in the 2014 local elections, the same idea took the form of “republican withdrawal” mostly defended by the parti socialiste : if the far right might win in the second round against a right-wing and a left-wing candidate (with just 34% of votes), the least successful in the first round should withdraw and ask voters to vote for the other. This idea was not really successful as it was not accepted by UMP (main right-wing party), so PS withdrew in favor of UMP when they had the lowest score, not the other way around.
Thus, largest political parties are blackmailing citizens : if you don’t vote for us, you may get the worst (far-right). How strange for the very idea of democracy ! Voters wouldn’t be free to vote for the party they like the most, but should feel responsible enough to support the major parties as a lesser evil. So “voting against” instead of “voting for”. Where is this coming from ?

Voting system : the problem and a solution

In the current voting system (in France) if there are two left-wing candidates L1 and L2, a right-wing one R and a far-right one FR in the second round, if the results are 25.9%(L1), 26.1%(L2), 21%(R), 27%(FR), far-right will govern, despite the fact that 52% of voters wanted a left-wing candidate !
The solution is simple : changing the voting system !
There are numerous voting systems which don’t have this problem. I’ll present only one here : Instant run-off voting.

  1. Each voter ranks candidates from the best to the worst (it is not necessary to rank all candidates) :
    • a left-wing voter’s ballot could be L1, L2, R
    •  a right-winger who rejects the far-right could choose R, L2, L1
    • a right-winger who prefers the far-right to the left :  R, FR
  2. To count the results is rather simple : start by counting the first choice in all ballots. You get the same result as with the current system : 25.9%(L1), 26.1%(L2), 21%(R), 27%(FR)
  3. Remove the least popular candidate, in this example R with 21%. The ballots of the removed candidate are attributed to the other candidates according to the second choice of the voters (if there isn’t a second choice, just drop the ballot).
    We get a new result : 35.9% (L1), 30.1% (L2), 32% (FR). The total is not 100% because some voters for R did not choose a second candidate.
  4. L2 having the lowest result is removed, and the ballots are attributed to the next choice (if it is D, already eliminated, just get the next choice).
    The final score is thus :
    61% (L1) et 33% (FR).
    The winner is L1.

So, allowing voters to rank candidates allows a free expression of voters’opinion, without the fear of having cast a “wasted vote”, a vote without any influence on the choice of the winner because their preferred option lost the election.
It is of course possible to add a blank vote in the list, to express disatisfaction with all candidates, but to follow it by you ranking of the candidates, thus giving your voice for the “least bad”.

When will this come ?

If there are solutions, why are they not implemented ? In fact they have been in many countries (Australia, New Zealand, Ireland, India…).

The main beneficiaries of the current system are the largest parties (PS and UMP in France), so it would be suprising that their members risk changing what works for them : they want to keep their seats in the Parliament (where they are dominant thanks to the voting system)

And that’s how you convert the diversity of opinions in a two-party system !

As a conclusion, here is a relevant excerpt of  The Hitchiker’s Guide to the Galaxy by Douglas Adams :

On its world, the people are people. The leaders are lizards. The people hate the lizards and the lizards rule the people.”
“Odd,” said Arthur, “I thought you said it was a democracy.”
“I did,” said Ford. “It is.”
“So,” said Arthur,[…] “why don’t people get rid of the lizards?”
“It honestly doesn’t occur to them,” said Ford. “They’ve all got the vote, so they all pretty much assume that the government they’ve voted in more or less approximates to the government they want.”
“You mean they actually vote for the lizards?”
“Oh yes,” said Ford with a shrug, “of course.”
“But,” said Arthur,[…] “why?”
“Because if they didn’t vote for a lizard,” said Ford, “the wrong lizard might get in.

Posted in news, Politics & society | Leave a comment

Alpha web version of PyVotons!

At last, I put online a web version of PyVotons!.

It is alpha software, not easy to run, lacking a lot of features.

You should read the Documentation page before trying to install it.
You need Python and python-openssl, on a system running on GNU/Linux.

Here it is :

pyvotons-2014-04-15.tar.bz2

Posted in Development, news | Leave a comment

Backup all MySQL databases

Goal : Saving MySQL databases to a samba (windows share) server (Ubuntu Linux).

Relatively easy thing to do, but I will keep this here if I need to do it again someday… Here is the code of a small bash script used to do that (not optimal, I know). But first, to get things started  :

# Create a backup user to get read access to the database
mysql -u root -p
mysql> GRANT SELECT,RELOAD,LOCK TABLES ON *.* TO ‘backup_user’@’localhost’;

# Add the root user to the mysql group (as cron jobs run as root)
sudo usermod -a -G mysql root

# Allows read access to users of the mysql group for the directories of mysql data files
sudo find /var/lib/mysql -maxdepth 1 -type d -exec chmod g+r \{\} \;
# List all databases
mysql -u backup_user -B -e ‘show databases;’
# Backup one database
mysqlhotcopy -u backup_user databaseName /TheBackupDir/
# Backup all databases -> this does not work as some databases listed do not really exist !
for i in `mysql -u backup_user -B -e ‘show databases;’`; do   echo $i; mysqlhotcopy -u backup_user “$i” /TheBackupDir/ ; done

On the samba share, create a directory WebServerBackup and in this directory, an empty file autoBackup to allow the script to confirm that the right directory is mounted before backing the database to it.

The samba share used here does not need a username or password, but adding user=”myuser”,password=”mypassword” to the list of options when mounting the share should work nicely in other cases.

The /var/www dir will be saved as is (no old versions) in the same directory and kept syncrhonized by rsync. A log will be written in /var/log/backup.log

Here is the script itself :

#!/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

Finally, the script (executable, thanks to a sudo chmod a+x /usr/bin/backupWebsite.sh) is linked to from the directory /etc/cron.daily/  so it will launch automatically once a day. The .sh is removed in the link name as some versions of cron don’t execute .sh files.

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

 

Posted in TechTips | Leave a comment

Soon : alpha version of WebPyvotons

After a few month of slumber, project PyVotons has awaken. It is now moving fast towards an alpha release on this website in the next few weeks.

This version should include account creation, poll creation, voter’s listings, voting page and finally poll’s result display.

Technical details : PyVotons contains a UI that runs in the client web browser (python compiled to javascript by Pyjamas) and a standalone HTTPS serveur (also using python) connected to a MySQL database to store user and poll data.

It should be the first usable web version of PyVotons but there will still be a lot on the TODO list :

  • include CSS support and styles, use new icons
  • code cleanup and documentation
  • better error management
  • translation support
  • user groups
  • security improvements (especially against DOS attacks)
  • e-mailing support for account creation and voting opportunities
  • support for other database egines (e.g. sqllite)
  • server-side should be able to run under Apache (CGI, app server…)

 

Posted in Development, news | Leave a comment

Bibtex, latex, elsarticle and nodots : meaningless error message

Just in case this could help someone else, here is a small record about why using latex and bibtex with the Elsevier article class and bibliography style model4-names.bst may lead to some headaches…

First, just using
\documentclass{elsarticle}
and
\bibliographystyle{model4-names}
\bibliography{myBiblio}
does not display the name of authors and the date, but just a number [1].

Solution : \documentclass[authoryear]{elsarticle} (from the sample file at http://support.river-valley.com/wiki/index.php?title=Model-wise_bibliographic_style_files)

The problem with this solution is that the author’s names are full of dots in the reference section (Smith, S.J., for example, instead of Smith SJ).
The solution provided here is to use the numcompress.sty package like this : \usepackage[nodots]{numcompress}

There I got a puzzling error and could not find a solution (this is why I publish this) :

(./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

?

In MyBiblio.bib file, some @article items were not well-defined. I had to change pages = {185,190} to a correct pages = {185–190}, and remove a pages={3} as it seems to confuse the numcompress package.

Posted in TechTips | 4 Comments

DroboFS : a php status page.

I recently bought a DroboFS for the storage needs of my lab. It is a nice piece of hardware but I was extremely surprised to see that the only way to configure it is the Drobo Dashboard, a Windows/Mac binary that must be plugged in the same network as the DroboFS (it sends broadcast packets to locate the DroboFS and it is impossible just to give it the IP address).

Why no web interface, such as the one on the Iomega StorCenter Ix2, a much cheaper network drive ?

A recent project to develop a webdashboard (by a user, not the company) exists, but to this day there is very little code available.

Based on his code, I wrote a PHP page (dirty code as I used PHP only once in the last 10 years) that displays the data I was able to get from the share configuration and status data of the Drobo.

edit:anonymized screenshots



I still cannot add users, allow them to change their own password, add a share and change user rights, but that’s a start !

Here is the source code in the hope it might be useful to someone else.

Known bug : if you did not define any users in your DroboFS the status display will fail.

To use it : activate DroboApps on your DroboFS (with Dashboard), install the Apache droboapp, create the file droboStatus.php in the /DroboApps/apache/www directory and fill it with the code that follows. To see the result, just go to http://droboIP:8080/droboStatus.php with your browser.

 

<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>
Posted in TechTips | Leave a comment