status.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. <?php
  2. // Copyright (C) 2014 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. /**
  19. * Monitor the backup
  20. *
  21. * @copyright Copyright (C) 2013 Combodo SARL
  22. * @license http://opensource.org/licenses/AGPL-3.0
  23. */
  24. if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
  25. require_once(__DIR__.'/../../approot.inc.php');
  26. require_once(APPROOT.'application/application.inc.php');
  27. require_once(APPROOT.'application/itopwebpage.class.inc.php');
  28. require_once(APPROOT.'application/startup.inc.php');
  29. require_once(APPROOT.'application/loginwebpage.class.inc.php');
  30. /////////////////////////////////////////////////////////////////////
  31. // Main program
  32. //
  33. LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
  34. //$sOperation = utils::ReadParam('operation', 'menu');
  35. //$oAppContext = new ApplicationContext();
  36. $oP = new iTopWebPage(Dict::S('bkp-status-title'));
  37. $oP->set_base(utils::GetAbsoluteUrlAppRoot().'pages/');
  38. try
  39. {
  40. $oP->add("<h1>".Dict::S('bkp-status-title')."</h1>");
  41. $sImgOk = '<img src="../images/validation_ok.png"> ';
  42. $sImgError = '<img src="../images/validation_error.png"> ';
  43. $oP->add("<fieldset>");
  44. $oP->add("<legend>".Dict::S('bkp-status-checks')."</legend>");
  45. // Availability of mysqldump
  46. //
  47. $sMySQLBinDir = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', '');
  48. $sMySQLBinDir = utils::ReadParam('mysql_bindir', $sMySQLBinDir, true);
  49. if (empty($sMySQLBinDir))
  50. {
  51. $sMySQLDump = 'mysqldump';
  52. }
  53. else
  54. {
  55. //echo 'Info - Found mysql_bindir: '.$sMySQLBinDir;
  56. $sMySQLDump = '"'.$sMySQLBinDir.'/mysqldump"';
  57. }
  58. $sCommand = "$sMySQLDump -V 2>&1";
  59. $aOutput = array();
  60. $iRetCode = 0;
  61. exec($sCommand, $aOutput, $iRetCode);
  62. if ($iRetCode == 0)
  63. {
  64. $sMySqlDump = $sImgOk.Dict::Format("bkp-mysqldump-ok", $aOutput[0]);
  65. }
  66. elseif ($iRetCode == 1)
  67. {
  68. $sMySqlDump = $sImgError.Dict::Format("bkp-mysqldump-notfound", implode(' ', $aOutput));
  69. }
  70. else
  71. {
  72. $sMySqlDump = $sImgError.Dict::Format("bkp-mysqldump-issue", $iRetCode);
  73. }
  74. foreach($aOutput as $sLine)
  75. {
  76. //echo 'Info - mysqldump -V said: '.$sLine;
  77. }
  78. $oP->p($sMySqlDump);
  79. // Destination directory
  80. //
  81. // Make sure the target directory exists and is writeable
  82. $sBackupDir = APPROOT.'data/backups/';
  83. SetupUtils::builddir($sBackupDir);
  84. if (!is_dir($sBackupDir))
  85. {
  86. $oP->p($sImgError.Dict::Format('bkp-missing-dir', $sBackupDir));
  87. }
  88. else
  89. {
  90. $oP->p(Dict::Format('bkp-free-disk-space', SetupUtils::HumanReadableSize(SetupUtils::CheckDiskSpace($sBackupDir)), $sBackupDir));
  91. if (!is_writable($sBackupDir))
  92. {
  93. $oP->p($sImgError.Dict::Format('bkp-dir-not-writeable', $sBackupDir));
  94. }
  95. }
  96. $sBackupDirAuto = $sBackupDir.'auto/';
  97. SetupUtils::builddir($sBackupDirAuto);
  98. $sBackupDirManual = $sBackupDir.'manual/';
  99. SetupUtils::builddir($sBackupDirManual);
  100. // Wrong format
  101. //
  102. $sBackupFile = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'file_name_format', BACKUP_DEFAULT_FORMAT);
  103. $oBackup = new DBBackupScheduled();
  104. $sZipName = $oBackup->MakeName($sBackupFile);
  105. if ($sZipName == '')
  106. {
  107. $oP->p($sImgError.Dict::Format('bkp-wrong-format-spec', $sBackupFile, BACKUP_DEFAULT_FORMAT));
  108. }
  109. else
  110. {
  111. $oP->p(Dict::Format('bkp-name-sample', $sZipName));
  112. }
  113. // Week Days
  114. //
  115. $aWeekDayToString = array(
  116. 1 => Dict::S('DayOfWeek-Monday'),
  117. 2 => Dict::S('DayOfWeek-Tuesday'),
  118. 3 => Dict::S('DayOfWeek-Wednesday'),
  119. 4 => Dict::S('DayOfWeek-Thursday'),
  120. 5 => Dict::S('DayOfWeek-Friday'),
  121. 6 => Dict::S('DayOfWeek-Saturday'),
  122. 7 => Dict::S('DayOfWeek-Sunday')
  123. );
  124. $aDayLabels = array();
  125. $oBackupExec = new BackupExec();
  126. foreach ($oBackupExec->InterpretWeekDays() as $iDay)
  127. {
  128. $aDayLabels[] = $aWeekDayToString[$iDay];
  129. }
  130. $sDays = implode(', ', $aDayLabels);
  131. $sBackupTime = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'time', '23:30');
  132. $oP->p(Dict::Format('bkp-week-days', $sDays, $sBackupTime));
  133. $iRetention = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'retention_count', 5);
  134. $oP->p(Dict::Format('bkp-retention', $iRetention));
  135. $oP->add("</fieldset>");
  136. // List of backups
  137. //
  138. $aFiles = $oBackup->ListFiles($sBackupDirAuto);
  139. $aFilesToDelete = array();
  140. while (count($aFiles) > $iRetention - 1)
  141. {
  142. $aFilesToDelete[] = array_shift($aFiles);
  143. }
  144. $oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment());
  145. if ($oRestoreMutex->TryLock())
  146. {
  147. $oRestoreMutex->Unlock();
  148. $sDisableRestore = '';
  149. }
  150. else
  151. {
  152. $sDisableRestore = 'disabled="disabled"';
  153. }
  154. // 1st table: list the backups made in the background
  155. //
  156. $aDetails = array();
  157. foreach ($oBackup->ListFiles($sBackupDirAuto) as $sBackupFile)
  158. {
  159. $sFileName = basename($sBackupFile);
  160. $sFilePath = 'auto/'.$sFileName;
  161. $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', array('operation' => 'download', 'file' => $sFilePath));
  162. $sName = "<a href=\"$sAjax\">".$sFileName.'</a>';
  163. $sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile));
  164. $sConfirmRestore = addslashes(Dict::Format('bkp-confirm-restore', $sFileName));
  165. $sFileEscaped = addslashes($sFilePath);
  166. $sRestoreBtn = '<button class="restore" onclick="LaunchRestoreNow(\''.$sFileEscaped.'\', \''.$sConfirmRestore.'\');" '.$sDisableRestore.'>'.Dict::S('bkp-button-restore-now').'</button>';
  167. if (in_array($sBackupFile, $aFilesToDelete))
  168. {
  169. $aDetails[] = array('file' => $sName.' <span class="next_to_delete" title="'.Dict::S('bkp-next-to-delete').'">*</span>', 'size' => $sSize, 'actions' => $sRestoreBtn);
  170. }
  171. else
  172. {
  173. $aDetails[] = array('file' => $sName, 'size' => $sSize, 'actions' => $sRestoreBtn);
  174. }
  175. }
  176. $aConfig = array(
  177. 'file' => array('label' => Dict::S('bkp-table-file'), 'description' => Dict::S('bkp-table-file+')),
  178. 'size' => array('label' => Dict::S('bkp-table-size'), 'description' => Dict::S('bkp-table-size+')),
  179. 'actions' => array('label' => Dict::S('bkp-table-actions'), 'description' => Dict::S('bkp-table-actions+')),
  180. );
  181. $oP->add("<fieldset>");
  182. $oP->add("<legend>".Dict::S('bkp-status-backups-auto')."</legend>");
  183. if (count($aDetails) > 0)
  184. {
  185. $oP->add('<div style="max-height:400px; overflow: auto;">');
  186. $oP->table($aConfig, array_reverse($aDetails));
  187. $oP->add('</div>');
  188. }
  189. else
  190. {
  191. $oP->p(Dict::S('bkp-status-backups-none'));
  192. }
  193. $oP->add("</fieldset>");
  194. // 2nd table: list the backups made manually
  195. //
  196. $aDetails = array();
  197. foreach ($oBackup->ListFiles($sBackupDirManual) as $sBackupFile)
  198. {
  199. $sFileName = basename($sBackupFile);
  200. $sFilePath = 'manual/'.$sFileName;
  201. $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', array('operation' => 'download', 'file' => $sFilePath));
  202. $sName = "<a href=\"$sAjax\">".$sFileName.'</a>';
  203. $sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile));
  204. $sConfirmRestore = addslashes(Dict::Format('bkp-confirm-restore', $sFileName));
  205. $sFileEscaped = addslashes($sFilePath);
  206. $sRestoreBtn = '<button class="restore" onclick="LaunchRestoreNow(\''.$sFileEscaped.'\', \''.$sConfirmRestore.'\');" '.$sDisableRestore.'>'.Dict::S('bkp-button-restore-now').'</button>';
  207. $aDetails[] = array('file' => $sName, 'size' => $sSize, 'actions' => $sRestoreBtn);
  208. }
  209. $aConfig = array(
  210. 'file' => array('label' => Dict::S('bkp-table-file'), 'description' => Dict::S('bkp-table-file+')),
  211. 'size' => array('label' => Dict::S('bkp-table-size'), 'description' => Dict::S('bkp-table-size+')),
  212. 'actions' => array('label' => Dict::S('bkp-table-actions'), 'description' => Dict::S('bkp-table-actions+')),
  213. );
  214. $oP->add("<fieldset>");
  215. $oP->add("<legend>".Dict::S('bkp-status-backups-manual')."</legend>");
  216. if (count($aDetails) > 0)
  217. {
  218. $oP->add('<div style="max-height:400px; overflow: auto;">');
  219. $oP->table($aConfig, array_reverse($aDetails));
  220. $oP->add('</div>');
  221. }
  222. else
  223. {
  224. $oP->p(Dict::S('bkp-status-backups-none'));
  225. }
  226. $oP->add("</fieldset>");
  227. // Ongoing operation ?
  228. //
  229. $oBackupMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
  230. if ($oBackupMutex->TryLock())
  231. {
  232. $oBackupMutex->Unlock();
  233. }
  234. else
  235. {
  236. $oP->p(Dict::S('bkp-backup-running'));
  237. }
  238. $oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment());
  239. if ($oRestoreMutex->TryLock())
  240. {
  241. $oRestoreMutex->Unlock();
  242. }
  243. else
  244. {
  245. $oP->p(Dict::S('bkp-restore-running'));
  246. }
  247. // Do backup now
  248. //
  249. $oBackupExec = new BackupExec();
  250. $oNext = $oBackupExec->GetNextOccurrence();
  251. $oP->p(Dict::Format('bkp-next-backup', $aWeekDayToString[$oNext->Format('N')], $oNext->Format('Y-m-d'), $oNext->Format('H:i')));
  252. $oP->p('<button onclick="LaunchBackupNow();">'.Dict::S('bkp-button-backup-now').'</button>');
  253. $oP->add('<div id="backup_success" class="header_message message_ok" style="display: none;"></div>');
  254. $oP->add('<div id="backup_errors" class="header_message message_error" style="display: none;"></div>');
  255. $oP->add('<input type="hidden" name="restore_token" id="restore_token"/>');
  256. $sConfirmBackup = addslashes(Dict::S('bkp-confirm-backup'));
  257. $sPleaseWaitBackup = addslashes(Dict::S('bkp-wait-backup'));
  258. $sPleaseWaitRestore = addslashes(Dict::S('bkp-wait-restore'));
  259. $sRestoreDone = addslashes(Dict::S('bkp-success-restore'));
  260. $sMySQLBinDir = addslashes(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
  261. $sDBHost = addslashes(MetaModel::GetConfig()->GetDBHost());
  262. $sDBUser = addslashes(MetaModel::GetConfig()->GetDBUser());
  263. $sDBPwd = addslashes(MetaModel::GetConfig()->GetDBPwd());
  264. $sDBName = addslashes(MetaModel::GetConfig()->GetDBName());
  265. $sDBSubName = addslashes(MetaModel::GetConfig()->GetDBSubName());
  266. $sEnvironment = addslashes(utils::GetCurrentEnvironment());
  267. $oP->add_script(
  268. <<<EOF
  269. function LaunchBackupNow()
  270. {
  271. $('#backup_success').hide();
  272. $('#backup_errors').hide();
  273. if (confirm('$sConfirmBackup'))
  274. {
  275. $.blockUI({ message: '<h1><img src="../images/indicator.gif" /> $sPleaseWaitBackup</h1>' });
  276. var oParams = {};
  277. oParams.operation = 'backup';
  278. $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){
  279. if (data.search(/error|exceptio|notice|warning/i) != -1)
  280. {
  281. $('#backup_errors').html(data);
  282. $('#backup_errors').show();
  283. }
  284. else
  285. {
  286. window.location.reload();
  287. }
  288. $.unblockUI();
  289. });
  290. }
  291. }
  292. function LaunchRestoreNow(sBackupFile, sConfirmationMessage)
  293. {
  294. if (confirm(sConfirmationMessage))
  295. {
  296. $.blockUI({ message: '<h1><img src="../images/indicator.gif" /> $sPleaseWaitRestore</h1>' });
  297. $('#backup_success').hide();
  298. $('#backup_errors').hide();
  299. var oParams = {};
  300. oParams.operation = 'restore_get_token';
  301. oParams.file = sBackupFile;
  302. $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){
  303. // Get the value of restore_token
  304. $('#backup_errors').append(data);
  305. var oParams = {};
  306. oParams.operation = 'restore_exec';
  307. oParams.token = $("#restore_token").val();
  308. oParams.mysql_bindir = '$sMySQLBinDir';
  309. oParams.db_host = '$sDBHost';
  310. oParams.db_user = '$sDBUser';
  311. oParams.db_pwd = '$sDBPwd';
  312. oParams.db_name = '$sDBName';
  313. oParams.db_subname = '$sDBSubName';
  314. oParams.environment = '$sEnvironment';
  315. if (oParams.token.length > 0)
  316. {
  317. $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){
  318. if (data.search(/error|exceptio|notice|warning/i) != -1)
  319. {
  320. $('#backup_success').hide();
  321. $('#backup_errors').html(data);
  322. $('#backup_errors').show();
  323. }
  324. else
  325. {
  326. $('#backup_errors').hide();
  327. $('#backup_success').html('$sRestoreDone');
  328. $('#backup_success').show();
  329. }
  330. $.unblockUI();
  331. });
  332. }
  333. else
  334. {
  335. $('button.restore').attr('disabled', 'disabled');
  336. $.unblockUI();
  337. }
  338. });
  339. }
  340. }
  341. EOF
  342. );
  343. }
  344. catch(Exception $e)
  345. {
  346. $oP->p('<b>'.$e->getMessage().'</b>');
  347. }
  348. $oP->output();
  349. ?>