Différences entre les versions de « SAUVEGARDES AUTOMATIQUES SUR UN NAS »
(44 versions intermédiaires par 2 utilisateurs non affichées) | |||
Ligne 5 : | Ligne 5 : | ||
| famille = Entretien et dépannages | | famille = Entretien et dépannages | ||
| date-create = 25/06/2018 | | date-create = 25/06/2018 | ||
| date-update = | | date-update = 04/10/2018 | ||
| auteur = fgtoul | | auteur = fgtoul | ||
}} | }} | ||
Ligne 11 : | Ligne 11 : | ||
Les différents matériels de '''GCE Electronics''' contiennent des données qui nous sont précieuses. | Les différents matériels de '''GCE Electronics''' contiennent des données qui nous sont précieuses. | ||
Que ce soit votre matériel, ou celui-de vos clients, vous pouvez effectuer des sauvegardes, même à distance (à condition de connaître l'adresse distante dudit matériel et que celui-ci soit accessible). | |||
Outre la configuration que nous avons passé des jours à peaufiner (voire des semaines), nous trouvons divers formulaires XML tout aussi précieux | Outre la configuration que nous avons passé des jours à peaufiner (voire des semaines), nous trouvons divers formulaires XML tout aussi précieux | ||
* status.xml, | :* status.xml, | ||
* io.xml, | :* io.xml, | ||
* analog.xml, | :* analog.xml, | ||
* globalstatus.xml, | :* globalstatus.xml, | ||
et bien d'autres encore. | et bien d'autres encore. Ces fichiers contiennent nos mesures de la température du salon ou des chambres, la quantité de pluie tombée hier, le PH de l'eau de la piscine, mais surtout nos consommations d'énergie, d'eau ou de gaz que nous aimerions conserver à des fins statistiques. | ||
Ces fichiers contiennent nos mesures de la température du salon ou des chambres, la quantité de pluie tombée hier, le PH de l'eau de la piscine, mais surtout nos consommations d'énergie, d'eau ou de gaz que nous aimerions conserver à des fins statistiques. Les amateurs de | |||
Les amateurs de météorologie trouveront d'autres raisons tout aussi importantes pour sauvegarder les mesures faites par leurs sondes (c'est d'ailleurs suite à la demande d'un passionné de météo que j'ai développé le script et rédigé ce wiki.) | |||
Nous allons planifier une application '''Php''' sur un serveur. Elle effectuera les sauvegardes à un rythme régulier sur le disque du serveur. | Nous allons planifier une application '''Php''' sur un serveur. Elle effectuera les sauvegardes à un rythme régulier sur le disque du serveur. | ||
Dans notre tutoriel, nos exemples et captures d'écrans s'appuieront sur un NAS Synology, mais il sera facile d'adapter le script pour qu'il fonctionne sur un autre type de serveur. | Dans notre tutoriel, nos exemples et captures d'écrans s'appuieront sur un NAS Synology, mais il sera facile d'adapter le script pour qu'il fonctionne sur un autre type de serveur. | ||
Le script permet, pour chaque matériel, de générer des jeux de sauvegarde qui auront une durée de vie programmée. | Le script permet, pour chaque matériel, de générer des jeux de sauvegarde qui auront une durée de vie programmée. | ||
Dans ce tutoriel, le déroulement du script sera appelé "cycle de sauvegarde", les données sauvegardées durant ce cycle seront nommées "Jeu de sauvegarde". | |||
Dans ce tutoriel, le déroulement du script sera appelé "'''cycle de sauvegarde'''", les données sauvegardées durant ce cycle seront nommées "'''Jeu de sauvegarde'''". | |||
Ces jeux de sauvegardes seront conservés selon un nombre de cycles défini par paramètre. | Ces jeux de sauvegardes seront conservés selon un nombre de cycles défini par paramètre. | ||
Ainsi, une sauvegarde planifiée de manière hebdomadaire, avec une rétention de 3 cycles, sera préservée pendant 3 semaines. Les données seront écrasées lors du quatrième cycle. | Ainsi, une sauvegarde planifiée de manière hebdomadaire, avec une rétention de 3 cycles, sera préservée pendant 3 semaines. Les données seront écrasées lors du quatrième cycle. | ||
'''Important :''' Un jeu de sauvegarde est préservé lorsqu'il est complet. Si un problème survient pendant le cycle, le jeu sera remplacé lors du cycle suivant. | '''Important :''' Un jeu de sauvegarde est préservé lorsqu'il est <u>complet</u>. Si un problème survient pendant le cycle, le jeu sera remplacé lors du cycle suivant. | ||
Il est possible de déclarer que les jeux de sauvegarde incomplets doivent malgré tout être préservés. Dans ce cas il est conseillé de valoriser le paramètre de rétention à zéro (rétention illimitée). | Il est possible de déclarer que les jeux de sauvegarde incomplets doivent malgré tout être préservés. Dans ce cas il est conseillé de valoriser le paramètre de rétention à zéro (rétention illimitée). | ||
Remarque : cet article concerne les matériels antérieurs à l'IPX800 V5. | |||
- IPX800 V3 | |||
- IPX800 V4 | |||
- Ecodevice RT 2 | |||
- Ecodevice | |||
''Remerciements à @seraphinou pour le temps passé lors des bêta-tests.'' | |||
==Préparation du serveur== | ==Préparation du serveur== | ||
Ligne 43 : | Ligne 60 : | ||
Attribuez tous les droits au SYSTEM afin que PHP puisse placer les fichiers dans ce dossier. | Attribuez tous les droits au SYSTEM afin que PHP puisse placer les fichiers dans ce dossier. | ||
[[Fichier: | [[Fichier:Sauvegardes_droits2.png]] | ||
===Création du dossier de l'application Web=== | ===Création du dossier de l'application Web=== | ||
Ligne 56 : | Ligne 73 : | ||
===Téléchargement=== | ===Téléchargement=== | ||
Téléchargez et | |||
[https://wiki.gce-electronics.com/images/3/36/SaveIPX4v3.zip Téléchargez] le code du fichier SaveIPX4v3.php | |||
Voici le code source du script que vous pouvez coller dans un fichier que vous nommerez SaveIPX4v3.php : | |||
<div class="mw-collapsible mw-collapsed" data-expandtext="{{int:show}}" data-collapsetext="{{int:hide}}" style="width:730px;"> | |||
''' Voir le code''' | |||
<div class="mw-collapsible-content"> | |||
<source> | |||
<?php | |||
/////////////////////////////////////////////////////////// | |||
// PhpSaveIPX : script php // | |||
// permettant la sauvegarde automatique // | |||
// des données et de la configuration // | |||
// des matériels GCE // | |||
// (IPX800, EcoDevice, EcoDevice RT2,... ) // | |||
// @fgtoul 2018 pour la communauté // | |||
/////////////////////////////////////////////////////////// | |||
// Tableau $materiel avec les IPX800 V3, IPX800 V4, EDRT2 | |||
// structure d'une table à n matériels | |||
// $materiel[0..n][0]: nom du matériel | |||
// $materiel[0..n][1]: Type de matériel (IPX800V3, IPX800V4, EDRT2, ED), | |||
// $materiel[0..n][2]: adresse IP locale ou distante, | |||
// $materiel[0..n][3]: port TCP, | |||
// $materiel[0..n][4]: securisation interface administrateur(true/false), | |||
// $materiel[0..n][5]: utilisateur administrateur, | |||
// $materiel[0..n][6]: password administrateur, | |||
// $materiel[0..n][7]: nombre de cycles de sauvegarde à conserver. Illimité si 0 | |||
//remarque : si un seul matériel, ne laisser qu'une ligne et supprimer la virgule qui la termine | |||
// si plusieurs matériels, chaque ligne doit se terminer par une virgule, sauf la dernière | |||
$materiel=array | |||
( | |||
array('IPX800_1','IPX800V4','192.168.0.112','80',false,'admin','',3), | |||
array('IPX800_2','IPX800V4','192.168.0.112','80',false,'admin','',3), | |||
array('PISCINE','IPX800V3','192.168.0.43','80',false,'admin','',5), | |||
array('EDRT','EDRT2','192.168.0.163','80',false,'admin','',3), | |||
array('ED','ED','192.168.0.124','80',false,'admin','',3) | |||
); | |||
//sous-dossier temporaire pour download des fichiers sur le NAS | |||
// ce sous-dossier sera créé dans le répertoire web de cette application | |||
$dwnload="sauvegardesGCE"; | |||
// dossier de destination des sauvegardes (les sous-dossiers y seront créés). | |||
// ce dossier doit exister au préalable et doit permettre à tout le monde de lire/écrire/exécuter -> chmod(0777) | |||
$destination="/volume1/test_savIPX"; //dossiers de destination des sauvegardes. renseigner le chemin absolu (complet) | |||
// par défaut, un jeu de sauvegarde réputé incomplet (ayant rencontré des erreurs pendant son déroulement) ne sera pas préservé | |||
// et sera remplacé lors du cycle suivant. | |||
// si vous souhaitez préserver les jeux incomplets, passez le paramètre ci-dessous à true. | |||
// Dans ce cas, il est conseillé de régler la rétention à 0 pour tous les matériels dans la table $materiel | |||
$preserveIncomplets=false; //true ; false; | |||
//Notifications | |||
$NotificationsMail=true; //true : envoie d'un mail à la fin du traitement | |||
$NotificationsLimitees=true; //si activés, les mails seront envoyés uniquement lorsque le script se terminera avec des erreurs. | |||
$NotificationsDestinataire="destinataire@gmail.com"; | |||
//******************************************************************************************************************** | |||
//******************************************************************************************************************** | |||
//******************************************************************************************************************** | |||
// ne rien modifier sous cette ligne | |||
//******************************************************************************************************************** | |||
// Tableau $fichiersXML contient le nom (sans extension) des fichiers XML à sauvegarder, par type de matériel | |||
// structure d'une table à n types de matériels | |||
// $fichiersXML[0..n][0] : chemin | |||
// $fichiersXML[0..n][1] : nom du fichier | |||
// $fichiersXML[0..n][2] : extension du fichier | |||
$fichiersXML=array | |||
( | |||
'IPX800V4'=>array('/admin/','status', 'xml', | |||
'/admin/','io', 'xml', | |||
'/admin/','graph', 'xml', | |||
'/admin/','analog','xml' | |||
), | |||
'IPX800V3'=>array('/','status', 'xml', | |||
'/','globalstatus', 'xml' | |||
), | |||
'EDRT2'=>array('/admin/','status','xml' | |||
), | |||
'ED'=>array('/protect/download/','xdata','csv' | |||
) | |||
); | |||
// Tableau $fichiersConfig contient le nom (sans extension) des fichiers de configuration à sauvegarder, par type de matériel | |||
// structure d'une table à n types de matériels | |||
// $fichiersConfig[0..n][0] : /chemin/ | |||
// $fichiersConfig[0..n][1] : type | |||
// $fichiersConfig[0..n][2] : nom du fichier | |||
// $fichiersConfig[0..n][3] : extension | |||
$fichiersConfig=array | |||
( | |||
'IPX800V4'=>array('/admin/download/','config','gce'), | |||
'IPX800V3'=>array('/protect/download/','config','gce'), | |||
'EDRT2'=>array('/admin/download/','config','gce'), | |||
'ED'=>array('/protect/download/','config','gce') | |||
); | |||
//gestionnaire d'erreurs | |||
$erreurs=false; //gestionnaire d'erreurs => global script | |||
$detailsExecutionErreurs=''; | |||
$avertissements=0;//gestionnaire d'avertissements => global script | |||
set_time_limit(0); | |||
//façonnage des données d'éxécution (contenu web) | |||
$detailsExecution=''; | |||
//////////////////////////////////////////////////////////////////////////////// | |||
//initialisation à 0 des cycles pour tous les types de matériels | |||
// permet l'initialisation en cas de nouveau matériel dans la table $materiels | |||
$cycles=array(); | |||
for ($i = 0; $i < count($materiel); $i++) { | |||
//$detailsExecution .= $materiel[$i][0] . "\r\n"; | |||
$cycles[$materiel[$i][0]] = 0; | |||
} | |||
// Cycles : liste lue à partir du fichier CyclesOUT.txt | |||
if (is_readable ( './CyclesOUT.txt' )) | |||
{ | |||
$cycles = unserialize ( urldecode ( file_get_contents ( './CyclesOUT.txt' ) ) ); | |||
} | |||
//fseek($cyclesOut, 0); // On remet le curseur au début du fichier | |||
//fclose($cyclesOut); | |||
//ouverture du journal des sauvegardes | |||
$journal=fopen('./Journal.txt', 'a+'); | |||
fputs($journal, '//////////////////////////////////////////'. "\n"); | |||
fputs($journal, "cycle du " . date("d/m/Y H:i:s") . "\n"); | |||
fputs($journal, '//////////////////////////////////////////'. "\n"); | |||
//création du sous-dossier pour download des fichiers | |||
if(!is_dir('./' . $dwnload )){ | |||
if (!mkdir('./' . $dwnload ,0777,true)){ | |||
fputs($journal, '***Création sous-dossier download ' . $dwnload . " : ECHEC \n"); | |||
fputs($journal, "***Abandon du programme \n"); | |||
$detailsExecutionErreurs .= 'ERREUR : Création sous-dossier download ' . $dwnload . ' : ECHEC'. "\r\n"; | |||
$detailsExecutionErreurs .= "Abandon du programme. \r\n"; | |||
$erreurs=true; | |||
goto fin; | |||
} else { | |||
fputs($journal, ' Création sous-dossier download "' . $dwnload . '" : OK'. "\n"); | |||
} | |||
} else { | |||
fputs($journal, ' Sous-dossier download "' . $dwnload . '" déjà existant.'. "\n"); | |||
} | |||
//////////////////////////////////////////// | |||
//boucle principale // | |||
//////////////////////////////////////////// | |||
$d= date("Ymd"); | |||
//traitement pour chaque matériel référencé | |||
for ($i = 0; $i < count($materiel); $i++) { | |||
$nommat=$materiel[$i][0]; | |||
$typmat=$materiel[$i][1]; | |||
$ipmat=$materiel[$i][2]; | |||
$portmat=$materiel[$i][3]; | |||
$securemat=$materiel[$i][4]; | |||
$usermat=$materiel[$i][5]; | |||
$pwdmat=$materiel[$i][6]; | |||
$retention=$materiel[$i][7]; | |||
if ($securemat==true){ | |||
$materiel_secure=$usermat . ':' . $pwdmat . '@'; | |||
} else { | |||
$materiel_secure=''; | |||
} | |||
$nberreurs=0; //gestionnaire d'erreurs par matériel | |||
//entête matériel dans le journal | |||
fputs($journal, '============== ' . $nommat . '(' . $typmat . ') =============='. "\n"); | |||
//création du sous-dossier destination pour ce matériel | |||
$do=$destination . '/' . $nommat; | |||
$er = "Création sous-dossier '" . $do . "'"; | |||
if(!is_dir($do)){ | |||
if (!mkdir($do,0777,true)){ | |||
fputs($journal, " ***" . $er. " : ECHEC \n"); | |||
$detailsExecutionErreurs .= "ERREUR : " . $er . " : ECHEC \r\n"; | |||
fputs($journal, " ***Sauvegarde ignorée. \n"); | |||
$detailsExecutionErreurs .= "Sauvegarde ignorée \r\n"; | |||
$erreurs=true; | |||
goto MaterielSuivant; | |||
} else { | |||
fputs($journal, " " . $er . "' : OK". "\n"); | |||
} | |||
} else { | |||
fputs($journal, " Sous-dossier '" . $do . "' déjà existant.". "\n"); | |||
} | |||
//détermination jeu de sauvegarde | |||
$jeu=$cycles[$nommat]; | |||
$jeu += 1; | |||
if ($jeu > $retention && $retention>0) { | |||
$jeu=1; | |||
} | |||
//création du sous-dossier destination selon jeu de sauvegarde | |||
$do=$destination . '/' . $nommat . '/' . $jeu; | |||
$er='Création du dossier de destination "' . $do; | |||
if(!is_dir($do)){ | |||
//création dossier destination Jeu de ce matériel | |||
if (!mkdir($do ,0777,true)){ | |||
//échec création | |||
fputs($journal,' ***' . $er . '" en échec.' . "\n"); | |||
fputs($journal,' ***sauvegarde ignorée.'. "\n"); | |||
$detailsExecutionErreurs .= 'ERREUR : ' . $er . '" en échec.' . "\r\n"; | |||
$detailsExecutionErreurs .= "sauvegarde ignorée. \r\n"; | |||
$erreurs=true; | |||
goto MaterielSuivant; | |||
} else { | |||
//création réussie | |||
fputs($journal,' création du dossier de destination "' . $do . '" réussie.'. "\n"); | |||
} | |||
} else { | |||
//dossier destination Jeu de ce matériel existe déjà | |||
fputs($journal," Sous-dossier de destination '" . $do . "' déjà existant.". "\n"); | |||
//vidage dossier destination. "\n"); | |||
//$ret=vidage($destination . '/' . $nommat . '/' . $jeu); | |||
$repertoire = opendir($destination . '/' . $nommat . '/' . $jeu); | |||
fputs($journal, " Vidage du dossier '" . $destination . '/' . $nommat . '/' . $jeu . "'". "\n"); | |||
$old = getcwd(); // Save the current directory | |||
chdir($destination . '/' . $nommat . '/' . $jeu); | |||
while (false !== ($fichier = readdir($repertoire))) | |||
{ | |||
$chemin = $destination . '/' . $nommat . '/' . $jeu ."/".$fichier; // On définit le chemin du fichier à effacer. | |||
// Si le fichier n'est pas un répertoire… | |||
if ($fichier != ".." AND $fichier != "." AND !is_dir($fichier)) | |||
{ | |||
if (!unlink($chemin)){; // On efface. | |||
fputs($journal, " - Suppression du fichier " . $fichier . "' en échec\n"); | |||
$avertissements += 1 ; //ne remet pas en cause l'intégrité du jeu de sauvegarde | |||
$detailsExecutionErreurs .= "Avertissement : Suppression du fichier " . $fichier . "' en échec \r\n"; | |||
} else { | |||
fputs($journal, " - Suppression du fichier " . $fichier . "' réussie \n"); | |||
} | |||
} | |||
} | |||
fputs($journal, " Vidage du dossier '" . $destination . "/" . $nommat . "/" . $jeu . "' terminé" . "\n"); | |||
closedir($repertoire); | |||
chdir($old); // Restore the old working directory | |||
} | |||
//création dossier $nomat pour download | |||
$do=$dwnload . '/' . $nommat; | |||
$er='Création sous-dossier ' . $do ; | |||
if(!is_dir($do)){ | |||
if (!mkdir($do,0777,true)){ | |||
fputs($journal, ' ***' . $do . ' : ECHEC'. "\n"); | |||
fputs($journal, " ***Sauvegarde ignorée.\n"); | |||
$detailsExecutionErreurs .= "ERREUR : " . $er . " : ECHEC \r\n"; | |||
$detailsExecutionErreurs .= "Sauvegarde ignorée"; | |||
$erreurs=true; | |||
goto MaterielSuivant; | |||
} else { | |||
fputs($journal, ' Création sous-dossier "' . $do . '" : OK'. "\n"); | |||
} | |||
} else { | |||
fputs($journal, ' Sous-dossier "' . $do . '" déjà existant.'. "\n"); | |||
$detailsExecutionErreurs .="Avertissement : le dossier " . $do . " existe déjà \r\n"; | |||
} | |||
// Boucle download des datas (boucle fichiers data) | |||
for ($j = 0; $j < count($fichiersXML[$typmat]); $j+=3) { | |||
$cheminMat=$fichiersXML[$typmat][$j]; //lecture chemin vers fichiers xml | |||
if (substr($cheminMat, 0,1)!=="/"){$cheminMat="/".$cheminMat;} | |||
if (substr($cheminMat, -1)!=='/'){$cheminMat=$cheminMat . '/';} | |||
$dwnFic=false; //gestionnaire d'erreur au niveau du fichier | |||
$fic=$fichiersXML[$typmat][$j+1]; | |||
$ext=$fichiersXML[$typmat][$j+2]; | |||
$RenameSource = getcwd() . "/" . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext; | |||
$RenameDest = $destination . '/' . $nommat . '/' . $jeu . '/' . $fic . '_' . $d . "." . $ext; | |||
//dwnload du fichier source | |||
//echo $fic . "\r\n" . $RenameSource . "\r\n" . $RenameDest . "\r\n" . $fichiersXML[$typmat][$j] . "\r\n"; | |||
$ficcible=fopen ('./' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext, 'w+'); | |||
$url = curl_init('http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . "." . $ext); | |||
curl_setopt($url, CURLOPT_TIMEOUT, 50); | |||
curl_setopt($url, CURLOPT_FILE, $ficcible); | |||
curl_setopt($url, CURLOPT_FOLLOWLOCATION, true); | |||
if (curl_exec($url)){ | |||
fputs($journal, ' Download du fichier http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . "." . $ext . ' vers ' . $RenameSource . ' réussi.'. "\n"); | |||
$dwnFic=true; | |||
} else { | |||
fputs($journal, ' *Download du fichier http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . "." . $ext . ' vers ' . $RenameSource . ' en échec'. "\n"); | |||
$detailsExecutionErreurs .= ' ERREUR : Download du fichier http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . "." . $ext . ' vers ' . $RenameSource . ' en échec'. "\r\n"; | |||
$erreurs=true; | |||
$nberreurs += 1; | |||
} | |||
curl_close($url); | |||
fclose($ficcible); | |||
if (filesize ( './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext )<100){ | |||
echo "le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext . " semble corrompu ! <br>"; | |||
$detailsExecutionErreurs .= "le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext . " semble corrompu ! \r\n"; | |||
fputs($journal, " *le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext . " semble corrompu ! \n"); | |||
} | |||
//déplacement du fichier vers sa destination finale si dwnl ok | |||
if ($dwnFic==true){ | |||
if (!rename($RenameSource, $RenameDest)){ | |||
// le déplacement a échoué | |||
fputs($journal,' *Déplacement du fichier ' . $renameSource . ' vers ' . $RenameDest . ' en échec.'. "\n"); | |||
$detailsExecutionErreurs .= " ERREUR : Déplacement du fichier " . $renameSource . " vers " . $RenameDest . " en échec. \r\n"; | |||
$erreurs=true; | |||
$nberreurs += 1; | |||
} else { | |||
// le déplacement a échoué | |||
fputs($journal,' Déplacement du fichier ' . $RenameSource . ' vers ' . $RenameDest . ' réussi.'. "\n"); | |||
} | |||
} else { | |||
//supprime fichier créé à blanc si download échoué | |||
//unlink('./' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext); | |||
} | |||
} | |||
// sauvegarde du fichier de configuration (fichier .gce) | |||
$cheminMat=$fichiersConfig[$typmat][0]; //lecture chemin vers fichiers config | |||
if (substr($cheminMat, 0,1)!=="/"){$cheminMat="/".$cheminMat;} | |||
if (substr($cheminMat, -1)!=='/'){$cheminMat=$cheminMat . '/';} | |||
$dwnFic=false; //gestionnaire d'erreur au niveau du fichier | |||
$fic=$fichiersConfig[$typmat][1]; | |||
$extension=$fichiersConfig[$typmat][2]; | |||
$RenameSource=getcwd() . "/" . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension; | |||
$RenameDest=$destination . '/' . $nommat . '/' . $jeu . '/' . $fic . '_' . $d . '.' . $extension; | |||
//upload du fichier source | |||
$ficcible=fopen ('./' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension, 'w+'); | |||
$url = curl_init('http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . '.' . $extension); | |||
curl_setopt($url, CURLOPT_TIMEOUT, 50); | |||
curl_setopt($url, CURLOPT_FILE, $ficcible); | |||
curl_setopt($url, CURLOPT_FOLLOWLOCATION, true); | |||
if (curl_exec($url)){ | |||
fputs($journal, ' Download du fichier http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . '.' . $extension . " vers " . $RenameSource . " réussi\n"); | |||
$dwnFic=true; | |||
} else { | |||
fputs($journal, ' *Download du fichier ' . 'http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . '.' . $extension . ' vers ' . $RenameSource . " en échec \n"); | |||
$detailsExecutionErreurs .= ' ERREUR : Download du fichier http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . '.' . $extension . ' vers ' . $RenameSource . " en échec\r\n"; | |||
$erreurs=true; | |||
$nberreurs += 1; | |||
} | |||
curl_close($url); | |||
fclose($ficcible); | |||
if (filesize ( './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension )<100){ | |||
echo "le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension . " semble corrompu ! <br>"; | |||
$detailsExecutionErreurs .= "le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension . " semble corrompu ! \r\n"; | |||
fputs($journal, " *le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension . " semble corrompu ! \n"); | |||
} | |||
if ($dwnFic==true){ | |||
//déplacement du fichier vers sa destination finale | |||
if (!rename($RenameSource, $RenameDest)){ | |||
// le déplacement a échoué | |||
fputs($journal,' *Déplacement du fichier ' . $RenameSource . " vers " . $RenameDest . "en échec.\n"); | |||
$detailsExecutionErreurs .= ' ERREUR : Déplacement du fichier ' . $RenameSource . " vers " . $RenameDest . "en échec.\r\n"; | |||
$erreurs=true; | |||
$nberreurs += 1; | |||
} else { | |||
// le déplacement a échoué | |||
fputs($journal,' Déplacement du fichier ' . $RenameSource . " vers " . $RenameDest . " réussi. \n"); | |||
} | |||
} else { | |||
//supprime fichier créé à blanc si download échoué | |||
unlink('./' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension); | |||
} | |||
MaterielSuivant: | |||
//incrément du jeu de sauvegarde si sauvegarde complète | |||
if ($nberreurs==0 || $preserveIncomplets==true){ | |||
$cycles[$nommat]=$jeu; | |||
} | |||
if ($nberreurs==0 ){ | |||
$ficOK=fopen ($destination . '/' . $nommat . '/' . $jeu . '/Sauvegarde_complete.txt', 'w+'); | |||
fclose($ficOK); | |||
} else { | |||
$ficOK=fopen ($destination . '/' . $nommat . '/' . $jeu . '/Sauvegarde_partielle.txt', 'w+'); | |||
fclose($ficOK); | |||
} | |||
// matériel suivant | |||
} | |||
fin: | |||
// écriture cyclesOut.txt | |||
file_put_contents ( './CyclesOUT.txt' , urlencode ( serialize ( $cycles ) ) ); | |||
// conclusion cycle de sauvegarde dans le $journal | |||
fputs($journal,' -------------------------------------------------------'. "\n"); | |||
if ($erreurs==true){ | |||
fputs($journal,' Le cycle de sauvegarde a rencontré des problèmes. Lisez ce qui précède.'. "\n"); | |||
} else if ($avertissements>0) { | |||
fputs($journal,' Cycle de sauvegarde terminé avec des avertissements. Lisez ce qui précède.'. "\n"); | |||
} else { | |||
fputs($journal,' Le cycle de sauvegarde terminé normalement.'. "\n"); | |||
} | |||
fputs($journal,' -------------------------------------------------------'. "\n"); | |||
fclose($journal); | |||
// façonnage des détails d'exécution | |||
$detailsExecutionErreurs = "===================================== \r\nRESUME DU CYCLE DE SAUVEGARDE DU JOUR : \r\n===================================== \r\n" . $detailsExecutionErreurs; | |||
$detailsExecutionErreurs .= "===================================== \r\nJEUX DE SAUVEGARDE VALIDES DU JOUR : \r\n===================================== \r\n"; | |||
for ($i = 0; $i < count($materiel); $i++) | |||
{ | |||
$detailsExecutionErreurs .= 'Materiel : ' . $materiel[$i][0] . " : Jeu "; | |||
echo 'Materiel : ' . $materiel[$i][0] . " : Jeu "; | |||
$detailsExecutionErreurs .= $cycles[$materiel[$i][0]] . "\r\n"; | |||
echo $cycles[$materiel[$i][0]] . "<br>"; | |||
} | |||
// en cas d'erreur, le synology envoie une notification par mail | |||
if ($NotificationsMail==true && ($erreurs==true || $NotificationsLimitees==false)) | |||
{ | |||
//trigger_error("Au moins une erreur est survenue. Merci de consulter le journal de l'application.", E_ERROR); | |||
echo "Traitement terminé avec des erreurs. Veuillez consulter le journal de l'application." ; | |||
mail($NotificationsDestinataire, "Sauvegarde Automatique GCE", "Au moins une erreur est survenue. Veuillez consulter le journal de l application.\r\n" . $detailsExecutionErreurs); | |||
} | |||
?> | |||
</source> | |||
Copiez et collez le code dans un fichier PHP. | |||
</div></div> | |||
===Adaptation du code=== | ===Adaptation du code=== | ||
Ligne 64 : | Ligne 523 : | ||
Il va être nécessaire de valoriser quelques paramètres dans le script. | Il va être nécessaire de valoriser quelques paramètres dans le script. | ||
==== | ====Liste du matériel==== | ||
Renseignez la liste de | Renseignez la liste de matériels pour lesquels vous voulez activer la sauvegarde. | ||
Dans le code, vous trouverez une table nommée '''$materiel''' | Dans le code, vous trouverez une table nommée '''$materiel''' | ||
Ligne 76 : | Ligne 535 : | ||
* adresse IP locale ou distante, | * adresse IP locale ou distante, | ||
* port TCP, | * port TCP, | ||
* | * sécurisation interface administrateur : doit prendre la valeur true si le menu Administrateur est protégé par mot de passe, ou false si non protégé | ||
* utilisateur administrateur (respectez les majuscules/minuscules), | * utilisateur administrateur (respectez les majuscules/minuscules), | ||
* password administrateur (respectez les majuscules/minuscules), | * password administrateur (respectez les majuscules/minuscules), | ||
Ligne 143 : | Ligne 602 : | ||
'''$NotificationsMail''': Renseignez la valeur '''true''' si vous souhaitez que l'application envoie un mail à la fin du traitement. | '''$NotificationsMail''': Renseignez la valeur '''true''' si vous souhaitez que l'application envoie un mail à la fin du traitement. | ||
'''$NotificationsLimitees''': Renseignez '''true'''pour que les mails soient envoyés uniquement lorsque le script se termine avec des erreurs. | '''$NotificationsLimitees''': Renseignez '''true''' pour que les mails soient envoyés uniquement lorsque le script se termine avec des erreurs.<br> | ||
Renseignez false pour un envoi systématique. | |||
'''$NotificationsDestinataire''': renseignez l'adresse mail du destinataire des notifications. | '''$NotificationsDestinataire''': renseignez l'adresse mail du destinataire des notifications. | ||
Ligne 154 : | Ligne 615 : | ||
</source> | </source> | ||
L'envoi des mails repose sur la | L'envoi des mails repose sur la capacité du serveur à envoyer lui-même des mails. | ||
Dans le cas d'un Synology, cela passe donc par la fonctionnalité "Notifications". | |||
Si vous ne l'avez déjà fait, activez les notifications dans le panneau de configuration, et renseignez les accès à votre fournisseur. | Si vous ne l'avez déjà fait, activez les notifications dans le panneau de configuration, et renseignez les accès à votre fournisseur. | ||
Ligne 178 : | Ligne 641 : | ||
dans mon cas, c’est | dans mon cas, c’est | ||
<nowiki>curl "http://192.168.0.9/gce/SaveIPX4.php"</nowiki> | <code><nowiki>curl "http://192.168.0.9/gce/SaveIPX4.php"</nowiki></code> | ||
Validez après avoir renseigné le calendrier d’exécution (onglet Programmer) | Validez après avoir renseigné le calendrier d’exécution (onglet Programmer) | ||
Ligne 193 : | Ligne 656 : | ||
[[Fichier:sauvegardes_curl.png|500px]] | [[Fichier:sauvegardes_curl.png|500px]] | ||
Information de @Mistoukwak : | |||
pour info si ça peut servir, quand on a une redirection http vers https sur le serveur web du NAS, il faut lancer le script dans le cron avec le paramètre --insecure associé au curl. Comme on est en local on ne risque rien. | |||
==Les fichiers sauvegardés== | ==Les fichiers sauvegardés== | ||
Ligne 201 : | Ligne 667 : | ||
* un fichier journal.txt est complété à la fin de chaque cycle. Il contient tout l'historique des sauvegardes. | * un fichier journal.txt est complété à la fin de chaque cycle. Il contient tout l'historique des sauvegardes. | ||
exemple de journal pour la sauvegarde | |||
exemple de journal pour la sauvegarde pour 2 IPX800 V4 , une IPX800 V3, un EDRT2 et un EcoDevice : | |||
<source> | <source> | ||
////////////////////////////////////////// | ////////////////////////////////////////// | ||
Ligne 298 : | Ligne 765 : | ||
Il contient également le numéro du jeu de sauvegarde pour chaque matériel. | Il contient également le numéro du jeu de sauvegarde pour chaque matériel. | ||
[[Fichier:sauvegardes_mail.png]] | [[Fichier:sauvegardes_mail.png|800px|border]] | ||
==Les différents messages== | ==Les différents messages== | ||
* Un download en échec signifie que le matériel était injoignable au moment de la sauvegarde | * Un download en échec signifie que le matériel était injoignable au moment de la sauvegarde | ||
* un fichier est désigné comme '''corrompu''' lorsque sa taille est inférieure à 500 octets. | * un fichier est désigné comme '''corrompu''' lorsque sa taille est inférieure à 500 octets. | ||
::* Si ce message est précédé d'une notification de téléchargement réussi : La fonction Curl a pu atteindre le matériel (d'où le message "Download réussi") mais les données n'ont pas pu être récupérées. Dans la plupart des cas, il s'agira d'un problème de mot de passe modifié ou nouvellement mis en place sur ledit matériel. | |||
::* Si ce message est précédé d'une notification de téléchargement en échec : Le matériel n'était pas joignable au moment de la sauvegarde. La récupération des données n'a pas pu se faire. | |||
* l'échec à la création de certains dossiers comme $destination ou $dwnload provoqueront une fin anormale du script.<br>Vérifiez les droits Lecture/Ecriture attribués au SYSTEM sur ces dossiers. | |||
* l'échec à la création de certains dossiers comme $destination ou $dwnload provoqueront une fin anormale du script. | |||
Vérifiez les droits Lecture/Ecriture attribués au SYSTEM | |||
* l'échec de création d'un sous-dossier pour un matériel n'entraîne pas la fin du programme. La sauvegarde du matériel est ignorée, le programme passe au matériel suivant. | * l'échec de création d'un sous-dossier pour un matériel n'entraîne pas la fin du programme. La sauvegarde du matériel est ignorée, le programme passe au matériel suivant. |
Version actuelle datée du 16 février 2024 à 08:57
| |||
---|---|---|---|
Nom | Sauvegardes | ||
Famille | Entretien et dépannages | ||
Wiki créé le | 25/06/2018 | ||
Wiki mis à jour le | 04/10/2018 | ||
Auteur | fgtoul |
Présentation
Les différents matériels de GCE Electronics contiennent des données qui nous sont précieuses.
Que ce soit votre matériel, ou celui-de vos clients, vous pouvez effectuer des sauvegardes, même à distance (à condition de connaître l'adresse distante dudit matériel et que celui-ci soit accessible).
Outre la configuration que nous avons passé des jours à peaufiner (voire des semaines), nous trouvons divers formulaires XML tout aussi précieux
- status.xml,
- io.xml,
- analog.xml,
- globalstatus.xml,
et bien d'autres encore. Ces fichiers contiennent nos mesures de la température du salon ou des chambres, la quantité de pluie tombée hier, le PH de l'eau de la piscine, mais surtout nos consommations d'énergie, d'eau ou de gaz que nous aimerions conserver à des fins statistiques.
Les amateurs de météorologie trouveront d'autres raisons tout aussi importantes pour sauvegarder les mesures faites par leurs sondes (c'est d'ailleurs suite à la demande d'un passionné de météo que j'ai développé le script et rédigé ce wiki.)
Nous allons planifier une application Php sur un serveur. Elle effectuera les sauvegardes à un rythme régulier sur le disque du serveur.
Dans notre tutoriel, nos exemples et captures d'écrans s'appuieront sur un NAS Synology, mais il sera facile d'adapter le script pour qu'il fonctionne sur un autre type de serveur.
Le script permet, pour chaque matériel, de générer des jeux de sauvegarde qui auront une durée de vie programmée.
Dans ce tutoriel, le déroulement du script sera appelé "cycle de sauvegarde", les données sauvegardées durant ce cycle seront nommées "Jeu de sauvegarde".
Ces jeux de sauvegardes seront conservés selon un nombre de cycles défini par paramètre.
Ainsi, une sauvegarde planifiée de manière hebdomadaire, avec une rétention de 3 cycles, sera préservée pendant 3 semaines. Les données seront écrasées lors du quatrième cycle.
Important : Un jeu de sauvegarde est préservé lorsqu'il est complet. Si un problème survient pendant le cycle, le jeu sera remplacé lors du cycle suivant. Il est possible de déclarer que les jeux de sauvegarde incomplets doivent malgré tout être préservés. Dans ce cas il est conseillé de valoriser le paramètre de rétention à zéro (rétention illimitée).
Remarque : cet article concerne les matériels antérieurs à l'IPX800 V5. - IPX800 V3 - IPX800 V4 - Ecodevice RT 2 - Ecodevice
Remerciements à @seraphinou pour le temps passé lors des bêta-tests.
Préparation du serveur
Création du dossier de sauvegarde
Sur le disque du serveur, créons un dossier qui recevra les différentes sauvegardes de nos matériels.
Sur mon Synology, j'ai créé le dossier test_savIPX
Dans les propriétés du dossier, ajoutez le SYSTEM comme ayant droit.
Attribuez tous les droits au SYSTEM afin que PHP puisse placer les fichiers dans ce dossier.
Création du dossier de l'application Web
Dans l'arborescence du serveur web, créez un dossier qui contiendra l'application.
Sur mon Synology, j'ai créé le dossier gce ayant le chemin /volume1/web/gce
Dans les propriétés de ce dossier, vérifiez que le SYSTEM a également tous les droits d'accès (lecture et écriture).
Le script PHP
Téléchargement
Téléchargez le code du fichier SaveIPX4v3.php
Voici le code source du script que vous pouvez coller dans un fichier que vous nommerez SaveIPX4v3.php :
Voir le code
<?php
///////////////////////////////////////////////////////////
// PhpSaveIPX : script php //
// permettant la sauvegarde automatique //
// des données et de la configuration //
// des matériels GCE //
// (IPX800, EcoDevice, EcoDevice RT2,... ) //
// @fgtoul 2018 pour la communauté //
///////////////////////////////////////////////////////////
// Tableau $materiel avec les IPX800 V3, IPX800 V4, EDRT2
// structure d'une table à n matériels
// $materiel[0..n][0]: nom du matériel
// $materiel[0..n][1]: Type de matériel (IPX800V3, IPX800V4, EDRT2, ED),
// $materiel[0..n][2]: adresse IP locale ou distante,
// $materiel[0..n][3]: port TCP,
// $materiel[0..n][4]: securisation interface administrateur(true/false),
// $materiel[0..n][5]: utilisateur administrateur,
// $materiel[0..n][6]: password administrateur,
// $materiel[0..n][7]: nombre de cycles de sauvegarde à conserver. Illimité si 0
//remarque : si un seul matériel, ne laisser qu'une ligne et supprimer la virgule qui la termine
// si plusieurs matériels, chaque ligne doit se terminer par une virgule, sauf la dernière
$materiel=array
(
array('IPX800_1','IPX800V4','192.168.0.112','80',false,'admin','',3),
array('IPX800_2','IPX800V4','192.168.0.112','80',false,'admin','',3),
array('PISCINE','IPX800V3','192.168.0.43','80',false,'admin','',5),
array('EDRT','EDRT2','192.168.0.163','80',false,'admin','',3),
array('ED','ED','192.168.0.124','80',false,'admin','',3)
);
//sous-dossier temporaire pour download des fichiers sur le NAS
// ce sous-dossier sera créé dans le répertoire web de cette application
$dwnload="sauvegardesGCE";
// dossier de destination des sauvegardes (les sous-dossiers y seront créés).
// ce dossier doit exister au préalable et doit permettre à tout le monde de lire/écrire/exécuter -> chmod(0777)
$destination="/volume1/test_savIPX"; //dossiers de destination des sauvegardes. renseigner le chemin absolu (complet)
// par défaut, un jeu de sauvegarde réputé incomplet (ayant rencontré des erreurs pendant son déroulement) ne sera pas préservé
// et sera remplacé lors du cycle suivant.
// si vous souhaitez préserver les jeux incomplets, passez le paramètre ci-dessous à true.
// Dans ce cas, il est conseillé de régler la rétention à 0 pour tous les matériels dans la table $materiel
$preserveIncomplets=false; //true ; false;
//Notifications
$NotificationsMail=true; //true : envoie d'un mail à la fin du traitement
$NotificationsLimitees=true; //si activés, les mails seront envoyés uniquement lorsque le script se terminera avec des erreurs.
$NotificationsDestinataire="destinataire@gmail.com";
//********************************************************************************************************************
//********************************************************************************************************************
//********************************************************************************************************************
// ne rien modifier sous cette ligne
//********************************************************************************************************************
// Tableau $fichiersXML contient le nom (sans extension) des fichiers XML à sauvegarder, par type de matériel
// structure d'une table à n types de matériels
// $fichiersXML[0..n][0] : chemin
// $fichiersXML[0..n][1] : nom du fichier
// $fichiersXML[0..n][2] : extension du fichier
$fichiersXML=array
(
'IPX800V4'=>array('/admin/','status', 'xml',
'/admin/','io', 'xml',
'/admin/','graph', 'xml',
'/admin/','analog','xml'
),
'IPX800V3'=>array('/','status', 'xml',
'/','globalstatus', 'xml'
),
'EDRT2'=>array('/admin/','status','xml'
),
'ED'=>array('/protect/download/','xdata','csv'
)
);
// Tableau $fichiersConfig contient le nom (sans extension) des fichiers de configuration à sauvegarder, par type de matériel
// structure d'une table à n types de matériels
// $fichiersConfig[0..n][0] : /chemin/
// $fichiersConfig[0..n][1] : type
// $fichiersConfig[0..n][2] : nom du fichier
// $fichiersConfig[0..n][3] : extension
$fichiersConfig=array
(
'IPX800V4'=>array('/admin/download/','config','gce'),
'IPX800V3'=>array('/protect/download/','config','gce'),
'EDRT2'=>array('/admin/download/','config','gce'),
'ED'=>array('/protect/download/','config','gce')
);
//gestionnaire d'erreurs
$erreurs=false; //gestionnaire d'erreurs => global script
$detailsExecutionErreurs='';
$avertissements=0;//gestionnaire d'avertissements => global script
set_time_limit(0);
//façonnage des données d'éxécution (contenu web)
$detailsExecution='';
////////////////////////////////////////////////////////////////////////////////
//initialisation à 0 des cycles pour tous les types de matériels
// permet l'initialisation en cas de nouveau matériel dans la table $materiels
$cycles=array();
for ($i = 0; $i < count($materiel); $i++) {
//$detailsExecution .= $materiel[$i][0] . "\r\n";
$cycles[$materiel[$i][0]] = 0;
}
// Cycles : liste lue à partir du fichier CyclesOUT.txt
if (is_readable ( './CyclesOUT.txt' ))
{
$cycles = unserialize ( urldecode ( file_get_contents ( './CyclesOUT.txt' ) ) );
}
//fseek($cyclesOut, 0); // On remet le curseur au début du fichier
//fclose($cyclesOut);
//ouverture du journal des sauvegardes
$journal=fopen('./Journal.txt', 'a+');
fputs($journal, '//////////////////////////////////////////'. "\n");
fputs($journal, "cycle du " . date("d/m/Y H:i:s") . "\n");
fputs($journal, '//////////////////////////////////////////'. "\n");
//création du sous-dossier pour download des fichiers
if(!is_dir('./' . $dwnload )){
if (!mkdir('./' . $dwnload ,0777,true)){
fputs($journal, '***Création sous-dossier download ' . $dwnload . " : ECHEC \n");
fputs($journal, "***Abandon du programme \n");
$detailsExecutionErreurs .= 'ERREUR : Création sous-dossier download ' . $dwnload . ' : ECHEC'. "\r\n";
$detailsExecutionErreurs .= "Abandon du programme. \r\n";
$erreurs=true;
goto fin;
} else {
fputs($journal, ' Création sous-dossier download "' . $dwnload . '" : OK'. "\n");
}
} else {
fputs($journal, ' Sous-dossier download "' . $dwnload . '" déjà existant.'. "\n");
}
////////////////////////////////////////////
//boucle principale //
////////////////////////////////////////////
$d= date("Ymd");
//traitement pour chaque matériel référencé
for ($i = 0; $i < count($materiel); $i++) {
$nommat=$materiel[$i][0];
$typmat=$materiel[$i][1];
$ipmat=$materiel[$i][2];
$portmat=$materiel[$i][3];
$securemat=$materiel[$i][4];
$usermat=$materiel[$i][5];
$pwdmat=$materiel[$i][6];
$retention=$materiel[$i][7];
if ($securemat==true){
$materiel_secure=$usermat . ':' . $pwdmat . '@';
} else {
$materiel_secure='';
}
$nberreurs=0; //gestionnaire d'erreurs par matériel
//entête matériel dans le journal
fputs($journal, '============== ' . $nommat . '(' . $typmat . ') =============='. "\n");
//création du sous-dossier destination pour ce matériel
$do=$destination . '/' . $nommat;
$er = "Création sous-dossier '" . $do . "'";
if(!is_dir($do)){
if (!mkdir($do,0777,true)){
fputs($journal, " ***" . $er. " : ECHEC \n");
$detailsExecutionErreurs .= "ERREUR : " . $er . " : ECHEC \r\n";
fputs($journal, " ***Sauvegarde ignorée. \n");
$detailsExecutionErreurs .= "Sauvegarde ignorée \r\n";
$erreurs=true;
goto MaterielSuivant;
} else {
fputs($journal, " " . $er . "' : OK". "\n");
}
} else {
fputs($journal, " Sous-dossier '" . $do . "' déjà existant.". "\n");
}
//détermination jeu de sauvegarde
$jeu=$cycles[$nommat];
$jeu += 1;
if ($jeu > $retention && $retention>0) {
$jeu=1;
}
//création du sous-dossier destination selon jeu de sauvegarde
$do=$destination . '/' . $nommat . '/' . $jeu;
$er='Création du dossier de destination "' . $do;
if(!is_dir($do)){
//création dossier destination Jeu de ce matériel
if (!mkdir($do ,0777,true)){
//échec création
fputs($journal,' ***' . $er . '" en échec.' . "\n");
fputs($journal,' ***sauvegarde ignorée.'. "\n");
$detailsExecutionErreurs .= 'ERREUR : ' . $er . '" en échec.' . "\r\n";
$detailsExecutionErreurs .= "sauvegarde ignorée. \r\n";
$erreurs=true;
goto MaterielSuivant;
} else {
//création réussie
fputs($journal,' création du dossier de destination "' . $do . '" réussie.'. "\n");
}
} else {
//dossier destination Jeu de ce matériel existe déjà
fputs($journal," Sous-dossier de destination '" . $do . "' déjà existant.". "\n");
//vidage dossier destination. "\n");
//$ret=vidage($destination . '/' . $nommat . '/' . $jeu);
$repertoire = opendir($destination . '/' . $nommat . '/' . $jeu);
fputs($journal, " Vidage du dossier '" . $destination . '/' . $nommat . '/' . $jeu . "'". "\n");
$old = getcwd(); // Save the current directory
chdir($destination . '/' . $nommat . '/' . $jeu);
while (false !== ($fichier = readdir($repertoire)))
{
$chemin = $destination . '/' . $nommat . '/' . $jeu ."/".$fichier; // On définit le chemin du fichier à effacer.
// Si le fichier n'est pas un répertoire…
if ($fichier != ".." AND $fichier != "." AND !is_dir($fichier))
{
if (!unlink($chemin)){; // On efface.
fputs($journal, " - Suppression du fichier " . $fichier . "' en échec\n");
$avertissements += 1 ; //ne remet pas en cause l'intégrité du jeu de sauvegarde
$detailsExecutionErreurs .= "Avertissement : Suppression du fichier " . $fichier . "' en échec \r\n";
} else {
fputs($journal, " - Suppression du fichier " . $fichier . "' réussie \n");
}
}
}
fputs($journal, " Vidage du dossier '" . $destination . "/" . $nommat . "/" . $jeu . "' terminé" . "\n");
closedir($repertoire);
chdir($old); // Restore the old working directory
}
//création dossier $nomat pour download
$do=$dwnload . '/' . $nommat;
$er='Création sous-dossier ' . $do ;
if(!is_dir($do)){
if (!mkdir($do,0777,true)){
fputs($journal, ' ***' . $do . ' : ECHEC'. "\n");
fputs($journal, " ***Sauvegarde ignorée.\n");
$detailsExecutionErreurs .= "ERREUR : " . $er . " : ECHEC \r\n";
$detailsExecutionErreurs .= "Sauvegarde ignorée";
$erreurs=true;
goto MaterielSuivant;
} else {
fputs($journal, ' Création sous-dossier "' . $do . '" : OK'. "\n");
}
} else {
fputs($journal, ' Sous-dossier "' . $do . '" déjà existant.'. "\n");
$detailsExecutionErreurs .="Avertissement : le dossier " . $do . " existe déjà \r\n";
}
// Boucle download des datas (boucle fichiers data)
for ($j = 0; $j < count($fichiersXML[$typmat]); $j+=3) {
$cheminMat=$fichiersXML[$typmat][$j]; //lecture chemin vers fichiers xml
if (substr($cheminMat, 0,1)!=="/"){$cheminMat="/".$cheminMat;}
if (substr($cheminMat, -1)!=='/'){$cheminMat=$cheminMat . '/';}
$dwnFic=false; //gestionnaire d'erreur au niveau du fichier
$fic=$fichiersXML[$typmat][$j+1];
$ext=$fichiersXML[$typmat][$j+2];
$RenameSource = getcwd() . "/" . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext;
$RenameDest = $destination . '/' . $nommat . '/' . $jeu . '/' . $fic . '_' . $d . "." . $ext;
//dwnload du fichier source
//echo $fic . "\r\n" . $RenameSource . "\r\n" . $RenameDest . "\r\n" . $fichiersXML[$typmat][$j] . "\r\n";
$ficcible=fopen ('./' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext, 'w+');
$url = curl_init('http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . "." . $ext);
curl_setopt($url, CURLOPT_TIMEOUT, 50);
curl_setopt($url, CURLOPT_FILE, $ficcible);
curl_setopt($url, CURLOPT_FOLLOWLOCATION, true);
if (curl_exec($url)){
fputs($journal, ' Download du fichier http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . "." . $ext . ' vers ' . $RenameSource . ' réussi.'. "\n");
$dwnFic=true;
} else {
fputs($journal, ' *Download du fichier http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . "." . $ext . ' vers ' . $RenameSource . ' en échec'. "\n");
$detailsExecutionErreurs .= ' ERREUR : Download du fichier http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . "." . $ext . ' vers ' . $RenameSource . ' en échec'. "\r\n";
$erreurs=true;
$nberreurs += 1;
}
curl_close($url);
fclose($ficcible);
if (filesize ( './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext )<100){
echo "le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext . " semble corrompu ! <br>";
$detailsExecutionErreurs .= "le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext . " semble corrompu ! \r\n";
fputs($journal, " *le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext . " semble corrompu ! \n");
}
//déplacement du fichier vers sa destination finale si dwnl ok
if ($dwnFic==true){
if (!rename($RenameSource, $RenameDest)){
// le déplacement a échoué
fputs($journal,' *Déplacement du fichier ' . $renameSource . ' vers ' . $RenameDest . ' en échec.'. "\n");
$detailsExecutionErreurs .= " ERREUR : Déplacement du fichier " . $renameSource . " vers " . $RenameDest . " en échec. \r\n";
$erreurs=true;
$nberreurs += 1;
} else {
// le déplacement a échoué
fputs($journal,' Déplacement du fichier ' . $RenameSource . ' vers ' . $RenameDest . ' réussi.'. "\n");
}
} else {
//supprime fichier créé à blanc si download échoué
//unlink('./' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . "." . $ext);
}
}
// sauvegarde du fichier de configuration (fichier .gce)
$cheminMat=$fichiersConfig[$typmat][0]; //lecture chemin vers fichiers config
if (substr($cheminMat, 0,1)!=="/"){$cheminMat="/".$cheminMat;}
if (substr($cheminMat, -1)!=='/'){$cheminMat=$cheminMat . '/';}
$dwnFic=false; //gestionnaire d'erreur au niveau du fichier
$fic=$fichiersConfig[$typmat][1];
$extension=$fichiersConfig[$typmat][2];
$RenameSource=getcwd() . "/" . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension;
$RenameDest=$destination . '/' . $nommat . '/' . $jeu . '/' . $fic . '_' . $d . '.' . $extension;
//upload du fichier source
$ficcible=fopen ('./' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension, 'w+');
$url = curl_init('http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . '.' . $extension);
curl_setopt($url, CURLOPT_TIMEOUT, 50);
curl_setopt($url, CURLOPT_FILE, $ficcible);
curl_setopt($url, CURLOPT_FOLLOWLOCATION, true);
if (curl_exec($url)){
fputs($journal, ' Download du fichier http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . '.' . $extension . " vers " . $RenameSource . " réussi\n");
$dwnFic=true;
} else {
fputs($journal, ' *Download du fichier ' . 'http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . '.' . $extension . ' vers ' . $RenameSource . " en échec \n");
$detailsExecutionErreurs .= ' ERREUR : Download du fichier http://' . $materiel_secure . $ipmat . ':' . $portmat . $cheminMat . $fic . '.' . $extension . ' vers ' . $RenameSource . " en échec\r\n";
$erreurs=true;
$nberreurs += 1;
}
curl_close($url);
fclose($ficcible);
if (filesize ( './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension )<100){
echo "le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension . " semble corrompu ! <br>";
$detailsExecutionErreurs .= "le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension . " semble corrompu ! \r\n";
fputs($journal, " *le fichier " . './' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension . " semble corrompu ! \n");
}
if ($dwnFic==true){
//déplacement du fichier vers sa destination finale
if (!rename($RenameSource, $RenameDest)){
// le déplacement a échoué
fputs($journal,' *Déplacement du fichier ' . $RenameSource . " vers " . $RenameDest . "en échec.\n");
$detailsExecutionErreurs .= ' ERREUR : Déplacement du fichier ' . $RenameSource . " vers " . $RenameDest . "en échec.\r\n";
$erreurs=true;
$nberreurs += 1;
} else {
// le déplacement a échoué
fputs($journal,' Déplacement du fichier ' . $RenameSource . " vers " . $RenameDest . " réussi. \n");
}
} else {
//supprime fichier créé à blanc si download échoué
unlink('./' . $dwnload . '/' . $nommat . '/' . $fic . '_' . $d . '.' . $extension);
}
MaterielSuivant:
//incrément du jeu de sauvegarde si sauvegarde complète
if ($nberreurs==0 || $preserveIncomplets==true){
$cycles[$nommat]=$jeu;
}
if ($nberreurs==0 ){
$ficOK=fopen ($destination . '/' . $nommat . '/' . $jeu . '/Sauvegarde_complete.txt', 'w+');
fclose($ficOK);
} else {
$ficOK=fopen ($destination . '/' . $nommat . '/' . $jeu . '/Sauvegarde_partielle.txt', 'w+');
fclose($ficOK);
}
// matériel suivant
}
fin:
// écriture cyclesOut.txt
file_put_contents ( './CyclesOUT.txt' , urlencode ( serialize ( $cycles ) ) );
// conclusion cycle de sauvegarde dans le $journal
fputs($journal,' -------------------------------------------------------'. "\n");
if ($erreurs==true){
fputs($journal,' Le cycle de sauvegarde a rencontré des problèmes. Lisez ce qui précède.'. "\n");
} else if ($avertissements>0) {
fputs($journal,' Cycle de sauvegarde terminé avec des avertissements. Lisez ce qui précède.'. "\n");
} else {
fputs($journal,' Le cycle de sauvegarde terminé normalement.'. "\n");
}
fputs($journal,' -------------------------------------------------------'. "\n");
fclose($journal);
// façonnage des détails d'exécution
$detailsExecutionErreurs = "===================================== \r\nRESUME DU CYCLE DE SAUVEGARDE DU JOUR : \r\n===================================== \r\n" . $detailsExecutionErreurs;
$detailsExecutionErreurs .= "===================================== \r\nJEUX DE SAUVEGARDE VALIDES DU JOUR : \r\n===================================== \r\n";
for ($i = 0; $i < count($materiel); $i++)
{
$detailsExecutionErreurs .= 'Materiel : ' . $materiel[$i][0] . " : Jeu ";
echo 'Materiel : ' . $materiel[$i][0] . " : Jeu ";
$detailsExecutionErreurs .= $cycles[$materiel[$i][0]] . "\r\n";
echo $cycles[$materiel[$i][0]] . "<br>";
}
// en cas d'erreur, le synology envoie une notification par mail
if ($NotificationsMail==true && ($erreurs==true || $NotificationsLimitees==false))
{
//trigger_error("Au moins une erreur est survenue. Merci de consulter le journal de l'application.", E_ERROR);
echo "Traitement terminé avec des erreurs. Veuillez consulter le journal de l'application." ;
mail($NotificationsDestinataire, "Sauvegarde Automatique GCE", "Au moins une erreur est survenue. Veuillez consulter le journal de l application.\r\n" . $detailsExecutionErreurs);
}
?>
Copiez et collez le code dans un fichier PHP.
Adaptation du code
Il va être nécessaire de valoriser quelques paramètres dans le script.
Liste du matériel
Renseignez la liste de matériels pour lesquels vous voulez activer la sauvegarde.
Dans le code, vous trouverez une table nommée $materiel
Créez une ligne par matériel en respectant l'ordre des données. Chaque ligne est composée de 8 valeurs séparées par une virgule
- nom du matériel : ce nom sera utilisé lors de la création d'un sous-dossier qui contiendra les jeux de sauvegarde pour ce matériel. Ce sous-dossier sera créé dans le dossier que vous avez créé précédemment sur le disque (/volume1/test_savIPX dans mon cas)
- Type de matériel (IPX800V3, IPX800V4, EDRT2, ED) (en majuscules),
- adresse IP locale ou distante,
- port TCP,
- sécurisation interface administrateur : doit prendre la valeur true si le menu Administrateur est protégé par mot de passe, ou false si non protégé
- utilisateur administrateur (respectez les majuscules/minuscules),
- password administrateur (respectez les majuscules/minuscules),
- nombre de cycles de sauvegarde à conserver. Comme vu plus haut, cela correspond au nombre de cycles au cours desquels le jeu sera préservé. 0 = rétention illimitée.
Séparez chaque ligne de matériel par une virgule. La dernière ligne ne devra pas être suivie par une virgule.
Exemple de table avec un seul matériel :
$materiel=array
(
array('IPX800_1','IPX800V4','192.168.0.112','80',false,'admin','',3)
);
Exemple de table avec deux matériels :
$materiel=array
(
array('IPX800','IPX800V4','192.168.0.112','80',false,'admin','',3),
array('EDRT','EDRT2','192.168.0.44','80',true,'admin','jhgFdgt1n',7)
);
Dossier web pour les données temporaires
Renseignez le paramètre $dwnload avec le nom du sous-dossier temporaire où seront téléchargés les fichiers à partir des matériels.
Ce sous-dossier sera automatiquement créé dans le répertoire web de cette application exemple :
$dwnload="sauvegardesGCE";
Dossier de destination des sauvegardes
Précédemment, vous avez créé un dossier sur le disque du serveur afin de stocker les différents jeux de sauvegarde.
Dans ce dossier, le script créera un sous-dossier par méteriel.
Renseignez le paramètre $destination avec le chemin absolu du dossier que vous avez créé
(/volume1/test_savIPX dans mon cas)
exemple :
$destination="/volume1/test_savIPX";
Traitement des jeux incomplets
Par défaut, un jeu de sauvegarde réputé incomplet (ayant rencontré des erreurs pendant son déroulement) ne sera pas préservé et sera remplacé lors du cycle suivant.
Si vous souhaitez préserver les jeux incomplets, valorisez le paramètre ci-dessous à true. Dans ce cas, il est conseillé de régler la rétention à 0 pour tous les matériels dans la table $materiel
exemple :
$preserveIncomplets=false;
Les notifications
$NotificationsMail: Renseignez la valeur true si vous souhaitez que l'application envoie un mail à la fin du traitement.
$NotificationsLimitees: Renseignez true pour que les mails soient envoyés uniquement lorsque le script se termine avec des erreurs.
Renseignez false pour un envoi systématique.
$NotificationsDestinataire: renseignez l'adresse mail du destinataire des notifications.
exemple :
$NotificationsMail=true; //true : envoie d'un mail à la fin du traitement
$NotificationsLimitees=true; //si activés, les mails seront envoyés uniquement lorsque le script se terminera avec des erreurs.
$NotificationsDestinataire="destinataire@gmail.com";
L'envoi des mails repose sur la capacité du serveur à envoyer lui-même des mails.
Dans le cas d'un Synology, cela passe donc par la fonctionnalité "Notifications". Si vous ne l'avez déjà fait, activez les notifications dans le panneau de configuration, et renseignez les accès à votre fournisseur.
Le planificateur de tâches
Créez une tâche planifiée sur votre serveur afin que le script php soit exécuté à intervalle régulier.
Sur le Synology, dans le panneau de configuration, ajoutez une tâche au planificateur
l’utilisateur est root, le mot de passe est le même que pour admin sur le synology
Vous pouvez envoyer une alerte mail lorsque la sauvegarde se passe mal.
renseignez Curl et l’url de votre page web entre guillemets
dans mon cas, c’est
curl "http://192.168.0.9/gce/SaveIPX4.php"
Validez après avoir renseigné le calendrier d’exécution (onglet Programmer)
Sélectionnez la tâche dans la liste et cliquez sur le bouton Paramètres.
Activez le journal d’exécution.
Remarque : la fonction Curl doit être installée sur votre serveur.
Sur le Synology, dans les paramètres de la Web Station, vérifiez les paramètres PHP (toutes les versions si vous en avez installé plusieurs)
Information de @Mistoukwak : pour info si ça peut servir, quand on a une redirection http vers https sur le serveur web du NAS, il faut lancer le script dans le cron avec le paramètre --insecure associé au curl. Comme on est en local on ne risque rien.
Les fichiers sauvegardés
- Tous les jeux de sauvegardes sont datés.
- un fichier journal.txt est complété à la fin de chaque cycle. Il contient tout l'historique des sauvegardes.
exemple de journal pour la sauvegarde pour 2 IPX800 V4 , une IPX800 V3, un EDRT2 et un EcoDevice :
//////////////////////////////////////////
cycle du 25/06/2018 12:36:32
//////////////////////////////////////////
Sous-dossier download "sauvegardesGCE" déjà existant.
============== IPX800_1(IPX800V4) ==============
Sous-dossier '/volume1/test_savIPX/IPX800_1' déjà existant.
Sous-dossier de destination '/volume1/test_savIPX/IPX800_1/3' déjà existant.
Vidage du dossier '/volume1/test_savIPX/IPX800_1/3'
- Suppression du fichier status_20180623.xml' réussie
- Suppression du fichier io_20180623.xml' réussie
- Suppression du fichier graph_20180623.xml' réussie
- Suppression du fichier analog_20180623.xml' réussie
- Suppression du fichier config_20180623.gce' réussie
- Suppression du fichier Sauvegarde_complete.txt' réussie
Vidage du dossier '/volume1/test_savIPX/IPX800_1/3' terminé
Sous-dossier "sauvegardesGCE/IPX800_1" déjà existant.
Download du fichier http://192.168.0.112:80/admin/status.xml vers /volume1/web/gce/sauvegardesGCE/IPX800_1/status_20180625.xml réussi.
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/IPX800_1/status_20180625.xml vers /volume1/test_savIPX/IPX800_1/3/status_20180625.xml réussi.
Download du fichier http://192.168.0.112:80/admin/io.xml vers /volume1/web/gce/sauvegardesGCE/IPX800_1/io_20180625.xml réussi.
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/IPX800_1/io_20180625.xml vers /volume1/test_savIPX/IPX800_1/3/io_20180625.xml réussi.
Download du fichier http://192.168.0.112:80/admin/graph.xml vers /volume1/web/gce/sauvegardesGCE/IPX800_1/graph_20180625.xml réussi.
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/IPX800_1/graph_20180625.xml vers /volume1/test_savIPX/IPX800_1/3/graph_20180625.xml réussi.
Download du fichier http://192.168.0.112:80/admin/analog.xml vers /volume1/web/gce/sauvegardesGCE/IPX800_1/analog_20180625.xml réussi.
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/IPX800_1/analog_20180625.xml vers /volume1/test_savIPX/IPX800_1/3/analog_20180625.xml réussi.
Download du fichier http://192.168.0.112:80/admin/download/config.gce vers /volume1/web/gce/sauvegardesGCE/IPX800_1/config_20180625.gce réussi
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/IPX800_1/config_20180625.gce vers /volume1/test_savIPX/IPX800_1/3/config_20180625.gce réussi.
============== IPX800_2(IPX800V4) ==============
Sous-dossier '/volume1/test_savIPX/IPX800_2' déjà existant.
Sous-dossier de destination '/volume1/test_savIPX/IPX800_2/3' déjà existant.
Vidage du dossier '/volume1/test_savIPX/IPX800_2/3'
- Suppression du fichier status_20180623.xml' réussie
- Suppression du fichier io_20180623.xml' réussie
- Suppression du fichier graph_20180623.xml' réussie
- Suppression du fichier analog_20180623.xml' réussie
- Suppression du fichier config_20180623.gce' réussie
- Suppression du fichier Sauvegarde_complete.txt' réussie
Vidage du dossier '/volume1/test_savIPX/IPX800_2/3' terminé
Sous-dossier "sauvegardesGCE/IPX800_2" déjà existant.
Download du fichier http://192.168.0.112:80/admin/status.xml vers /volume1/web/gce/sauvegardesGCE/IPX800_2/status_20180625.xml réussi.
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/IPX800_2/status_20180625.xml vers /volume1/test_savIPX/IPX800_2/3/status_20180625.xml réussi.
Download du fichier http://192.168.0.112:80/admin/io.xml vers /volume1/web/gce/sauvegardesGCE/IPX800_2/io_20180625.xml réussi.
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/IPX800_2/io_20180625.xml vers /volume1/test_savIPX/IPX800_2/3/io_20180625.xml réussi.
Download du fichier http://192.168.0.112:80/admin/graph.xml vers /volume1/web/gce/sauvegardesGCE/IPX800_2/graph_20180625.xml réussi.
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/IPX800_2/graph_20180625.xml vers /volume1/test_savIPX/IPX800_2/3/graph_20180625.xml réussi.
Download du fichier http://192.168.0.112:80/admin/analog.xml vers /volume1/web/gce/sauvegardesGCE/IPX800_2/analog_20180625.xml réussi.
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/IPX800_2/analog_20180625.xml vers /volume1/test_savIPX/IPX800_2/3/analog_20180625.xml réussi.
Download du fichier http://192.168.0.112:80/admin/download/config.gce vers /volume1/web/gce/sauvegardesGCE/IPX800_2/config_20180625.gce réussi
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/IPX800_2/config_20180625.gce vers /volume1/test_savIPX/IPX800_2/3/config_20180625.gce réussi.
============== PISCINE(IPX800V3) ==============
Sous-dossier '/volume1/test_savIPX/PISCINE' déjà existant.
Sous-dossier de destination '/volume1/test_savIPX/PISCINE/5' déjà existant.
Vidage du dossier '/volume1/test_savIPX/PISCINE/5'
- Suppression du fichier Sauvegarde_partielle.txt' réussie
Vidage du dossier '/volume1/test_savIPX/PISCINE/5' terminé
Sous-dossier "sauvegardesGCE/PISCINE" déjà existant.
Download du fichier http://192.168.0.43:80/status.xml vers /volume1/web/gce/sauvegardesGCE/PISCINE/status_20180625.xml réussi.
*le fichier ./sauvegardesGCE/PISCINE/status_20180625.xml semble corrompu !
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/PISCINE/status_20180625.xml vers /volume1/test_savIPX/PISCINE/5/status_20180625.xml réussi.
Download du fichier http://192.168.0.43:80/globalstatus.xml vers /volume1/web/gce/sauvegardesGCE/PISCINE/globalstatus_20180625.xml réussi.
*le fichier ./sauvegardesGCE/PISCINE/globalstatus_20180625.xml semble corrompu !
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/PISCINE/globalstatus_20180625.xml vers /volume1/test_savIPX/PISCINE/5/globalstatus_20180625.xml réussi.
Download du fichier http://192.168.0.43:80/protect/download/config.gce vers /volume1/web/gce/sauvegardesGCE/PISCINE/config_20180625.gce réussi
*le fichier ./sauvegardesGCE/PISCINE/config_20180625.gce semble corrompu !
Déplacement du fichier /volume1/web/gce/sauvegardesGCE/PISCINE/config_20180625.gce vers /volume1/test_savIPX/PISCINE/5/config_20180625.gce réussi.
============== EDRT(EDRT2) ==============
Sous-dossier '/volume1/test_savIPX/EDRT' déjà existant.
Sous-dossier de destination '/volume1/test_savIPX/EDRT/7' déjà existant.
Vidage du dossier '/volume1/test_savIPX/EDRT/7'
- Suppression du fichier Sauvegarde_partielle.txt' réussie
Vidage du dossier '/volume1/test_savIPX/EDRT/7' terminé
Sous-dossier "sauvegardesGCE/EDRT" déjà existant.
*Download du fichier http://192.168.0.44:80/admin/status.xml vers /volume1/web/gce/sauvegardesGCE/EDRT/status_20180625.xml en échec
*le fichier ./sauvegardesGCE/EDRT/status_20180625.xml semble corrompu !
*Download du fichier http://192.168.0.44:80/admin/download/config.gce vers /volume1/web/gce/sauvegardesGCE/EDRT/config_20180625.gce en échec
*le fichier ./sauvegardesGCE/EDRT/config_20180625.gce semble corrompu !
============== ED(ED) ==============
Sous-dossier '/volume1/test_savIPX/ED' déjà existant.
Sous-dossier de destination '/volume1/test_savIPX/ED/7' déjà existant.
Vidage du dossier '/volume1/test_savIPX/ED/7'
- Suppression du fichier Sauvegarde_partielle.txt' réussie
Vidage du dossier '/volume1/test_savIPX/ED/7' terminé
Sous-dossier "sauvegardesGCE/ED" déjà existant.
*Download du fichier http://192.168.0.44:80/status.xml vers /volume1/web/gce/sauvegardesGCE/ED/status_20180625.xml en échec
*le fichier ./sauvegardesGCE/ED/status_20180625.xml semble corrompu !
*Download du fichier http://192.168.0.44:80/protect/download/config.gce vers /volume1/web/gce/sauvegardesGCE/ED/config_20180625.gce en échec
*le fichier ./sauvegardesGCE/ED/config_20180625.gce semble corrompu !
-------------------------------------------------------
Le cycle de sauvegarde a rencontré des problèmes. Lisez ce qui précède.
-------------------------------------------------------
Les notifications
le mail envoyé à la fin du traitement contient un résumé des sauvegardes. Il contient également le numéro du jeu de sauvegarde pour chaque matériel.
Les différents messages
- Un download en échec signifie que le matériel était injoignable au moment de la sauvegarde
- un fichier est désigné comme corrompu lorsque sa taille est inférieure à 500 octets.
- Si ce message est précédé d'une notification de téléchargement réussi : La fonction Curl a pu atteindre le matériel (d'où le message "Download réussi") mais les données n'ont pas pu être récupérées. Dans la plupart des cas, il s'agira d'un problème de mot de passe modifié ou nouvellement mis en place sur ledit matériel.
- Si ce message est précédé d'une notification de téléchargement en échec : Le matériel n'était pas joignable au moment de la sauvegarde. La récupération des données n'a pas pu se faire.
- l'échec à la création de certains dossiers comme $destination ou $dwnload provoqueront une fin anormale du script.
Vérifiez les droits Lecture/Ecriture attribués au SYSTEM sur ces dossiers. - l'échec de création d'un sous-dossier pour un matériel n'entraîne pas la fin du programme. La sauvegarde du matériel est ignorée, le programme passe au matériel suivant.