status.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. if (MetaModel::GetConfig()->Get('demo_mode'))
  42. {
  43. $oP->add("<div class=\"header_message message_info\">iTop is in <b>demonstration mode</b>: the feature is disabled.</div>");
  44. }
  45. $sImgOk = '<img src="../images/validation_ok.png"> ';
  46. $sImgError = '<img src="../images/validation_error.png"> ';
  47. $oP->add("<fieldset>");
  48. $oP->add("<legend>".Dict::S('bkp-status-checks')."</legend>");
  49. // Availability of mysqldump
  50. //
  51. $sMySQLBinDir = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', '');
  52. $sMySQLBinDir = utils::ReadParam('mysql_bindir', $sMySQLBinDir, true);
  53. if (empty($sMySQLBinDir))
  54. {
  55. $sMySQLDump = 'mysqldump';
  56. }
  57. else
  58. {
  59. //echo 'Info - Found mysql_bindir: '.$sMySQLBinDir;
  60. $sMySQLDump = '"'.$sMySQLBinDir.'/mysqldump"';
  61. }
  62. $sCommand = "$sMySQLDump -V 2>&1";
  63. $aOutput = array();
  64. $iRetCode = 0;
  65. exec($sCommand, $aOutput, $iRetCode);
  66. if ($iRetCode == 0)
  67. {
  68. $sMySqlDump = $sImgOk.Dict::Format("bkp-mysqldump-ok", $aOutput[0]);
  69. }
  70. elseif ($iRetCode == 1)
  71. {
  72. $sMySqlDump = $sImgError.Dict::Format("bkp-mysqldump-notfound", implode(' ', $aOutput));
  73. }
  74. else
  75. {
  76. $sMySqlDump = $sImgError.Dict::Format("bkp-mysqldump-issue", $iRetCode);
  77. }
  78. foreach($aOutput as $sLine)
  79. {
  80. //echo 'Info - mysqldump -V said: '.$sLine;
  81. }
  82. $oP->p($sMySqlDump);
  83. // Destination directory
  84. //
  85. // Make sure the target directory exists and is writeable
  86. $sBackupDir = APPROOT.'data/backups/';
  87. SetupUtils::builddir($sBackupDir);
  88. if (!is_dir($sBackupDir))
  89. {
  90. $oP->p($sImgError.Dict::Format('bkp-missing-dir', $sBackupDir));
  91. }
  92. else
  93. {
  94. $oP->p(Dict::Format('bkp-free-disk-space', SetupUtils::HumanReadableSize(SetupUtils::CheckDiskSpace($sBackupDir)), $sBackupDir));
  95. if (!is_writable($sBackupDir))
  96. {
  97. $oP->p($sImgError.Dict::Format('bkp-dir-not-writeable', $sBackupDir));
  98. }
  99. }
  100. $sBackupDirAuto = $sBackupDir.'auto/';
  101. SetupUtils::builddir($sBackupDirAuto);
  102. $sBackupDirManual = $sBackupDir.'manual/';
  103. SetupUtils::builddir($sBackupDirManual);
  104. // Wrong format
  105. //
  106. $sBackupFile = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'file_name_format', BACKUP_DEFAULT_FORMAT);
  107. $oBackup = new DBBackupScheduled();
  108. $sZipName = $oBackup->MakeName($sBackupFile);
  109. if ($sZipName == '')
  110. {
  111. $oP->p($sImgError.Dict::Format('bkp-wrong-format-spec', $sBackupFile, BACKUP_DEFAULT_FORMAT));
  112. }
  113. else
  114. {
  115. $oP->p(Dict::Format('bkp-name-sample', $sZipName));
  116. }
  117. // Week Days
  118. //
  119. $aWeekDayToString = array(
  120. 1 => Dict::S('DayOfWeek-Monday'),
  121. 2 => Dict::S('DayOfWeek-Tuesday'),
  122. 3 => Dict::S('DayOfWeek-Wednesday'),
  123. 4 => Dict::S('DayOfWeek-Thursday'),
  124. 5 => Dict::S('DayOfWeek-Friday'),
  125. 6 => Dict::S('DayOfWeek-Saturday'),
  126. 7 => Dict::S('DayOfWeek-Sunday')
  127. );
  128. $aDayLabels = array();
  129. $oBackupExec = new BackupExec();
  130. foreach ($oBackupExec->InterpretWeekDays() as $iDay)
  131. {
  132. $aDayLabels[] = $aWeekDayToString[$iDay];
  133. }
  134. $sDays = implode(', ', $aDayLabels);
  135. $sBackupTime = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'time', '23:30');
  136. $oP->p(Dict::Format('bkp-week-days', $sDays, $sBackupTime));
  137. $iRetention = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'retention_count', 5);
  138. $oP->p(Dict::Format('bkp-retention', $iRetention));
  139. $oP->add("</fieldset>");
  140. // List of backups
  141. //
  142. $aFiles = $oBackup->ListFiles($sBackupDirAuto);
  143. $aFilesToDelete = array();
  144. while (count($aFiles) > $iRetention - 1)
  145. {
  146. $aFilesToDelete[] = array_shift($aFiles);
  147. }
  148. $oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment());
  149. if ($oRestoreMutex->TryLock())
  150. {
  151. $oRestoreMutex->Unlock();
  152. $sDisableRestore = '';
  153. }
  154. else
  155. {
  156. $sDisableRestore = 'disabled="disabled"';
  157. }
  158. // 1st table: list the backups made in the background
  159. //
  160. $aDetails = array();
  161. foreach ($oBackup->ListFiles($sBackupDirAuto) as $sBackupFile)
  162. {
  163. $sFileName = basename($sBackupFile);
  164. $sFilePath = 'auto/'.$sFileName;
  165. if (MetaModel::GetConfig()->Get('demo_mode'))
  166. {
  167. $sName = $sFileName;
  168. }
  169. else
  170. {
  171. $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', array('operation' => 'download', 'file' => $sFilePath));
  172. $sName = "<a href=\"$sAjax\">".$sFileName.'</a>';
  173. }
  174. $sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile));
  175. $sConfirmRestore = addslashes(Dict::Format('bkp-confirm-restore', $sFileName));
  176. $sFileEscaped = addslashes($sFilePath);
  177. $sRestoreBtn = '<button class="restore" onclick="LaunchRestoreNow(\''.$sFileEscaped.'\', \''.$sConfirmRestore.'\');" '.$sDisableRestore.'>'.Dict::S('bkp-button-restore-now').'</button>';
  178. if (in_array($sBackupFile, $aFilesToDelete))
  179. {
  180. $aDetails[] = array('file' => $sName.' <span class="next_to_delete" title="'.Dict::S('bkp-next-to-delete').'">*</span>', 'size' => $sSize, 'actions' => $sRestoreBtn);
  181. }
  182. else
  183. {
  184. $aDetails[] = array('file' => $sName, 'size' => $sSize, 'actions' => $sRestoreBtn);
  185. }
  186. }
  187. $aConfig = array(
  188. 'file' => array('label' => Dict::S('bkp-table-file'), 'description' => Dict::S('bkp-table-file+')),
  189. 'size' => array('label' => Dict::S('bkp-table-size'), 'description' => Dict::S('bkp-table-size+')),
  190. 'actions' => array('label' => Dict::S('bkp-table-actions'), 'description' => Dict::S('bkp-table-actions+')),
  191. );
  192. $oP->add("<fieldset>");
  193. $oP->add("<legend>".Dict::S('bkp-status-backups-auto')."</legend>");
  194. if (count($aDetails) > 0)
  195. {
  196. $oP->add('<div style="max-height:400px; overflow: auto;">');
  197. $oP->table($aConfig, array_reverse($aDetails));
  198. $oP->add('</div>');
  199. }
  200. else
  201. {
  202. $oP->p(Dict::S('bkp-status-backups-none'));
  203. }
  204. $oP->add("</fieldset>");
  205. // 2nd table: list the backups made manually
  206. //
  207. $aDetails = array();
  208. foreach ($oBackup->ListFiles($sBackupDirManual) as $sBackupFile)
  209. {
  210. $sFileName = basename($sBackupFile);
  211. $sFilePath = 'manual/'.$sFileName;
  212. if (MetaModel::GetConfig()->Get('demo_mode'))
  213. {
  214. $sName = $sFileName;
  215. }
  216. else
  217. {
  218. $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', array('operation' => 'download', 'file' => $sFilePath));
  219. $sName = "<a href=\"$sAjax\">".$sFileName.'</a>';
  220. }
  221. $sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile));
  222. $sConfirmRestore = addslashes(Dict::Format('bkp-confirm-restore', $sFileName));
  223. $sFileEscaped = addslashes($sFilePath);
  224. $sRestoreBtn = '<button class="restore" onclick="LaunchRestoreNow(\''.$sFileEscaped.'\', \''.$sConfirmRestore.'\');" '.$sDisableRestore.'>'.Dict::S('bkp-button-restore-now').'</button>';
  225. $aDetails[] = array('file' => $sName, 'size' => $sSize, 'actions' => $sRestoreBtn);
  226. }
  227. $aConfig = array(
  228. 'file' => array('label' => Dict::S('bkp-table-file'), 'description' => Dict::S('bkp-table-file+')),
  229. 'size' => array('label' => Dict::S('bkp-table-size'), 'description' => Dict::S('bkp-table-size+')),
  230. 'actions' => array('label' => Dict::S('bkp-table-actions'), 'description' => Dict::S('bkp-table-actions+')),
  231. );
  232. $oP->add("<fieldset>");
  233. $oP->add("<legend>".Dict::S('bkp-status-backups-manual')."</legend>");
  234. if (count($aDetails) > 0)
  235. {
  236. $oP->add('<div style="max-height:400px; overflow: auto;">');
  237. $oP->table($aConfig, array_reverse($aDetails));
  238. $oP->add('</div>');
  239. }
  240. else
  241. {
  242. $oP->p(Dict::S('bkp-status-backups-none'));
  243. }
  244. $oP->add("</fieldset>");
  245. // Ongoing operation ?
  246. //
  247. $oBackupMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
  248. if ($oBackupMutex->TryLock())
  249. {
  250. $oBackupMutex->Unlock();
  251. }
  252. else
  253. {
  254. $oP->p(Dict::S('bkp-backup-running'));
  255. }
  256. $oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment());
  257. if ($oRestoreMutex->TryLock())
  258. {
  259. $oRestoreMutex->Unlock();
  260. }
  261. else
  262. {
  263. $oP->p(Dict::S('bkp-restore-running'));
  264. }
  265. // Do backup now
  266. //
  267. $oBackupExec = new BackupExec();
  268. $oNext = $oBackupExec->GetNextOccurrence();
  269. $oP->p(Dict::Format('bkp-next-backup', $aWeekDayToString[$oNext->Format('N')], $oNext->Format('Y-m-d'), $oNext->Format('H:i')));
  270. $oP->p('<button onclick="LaunchBackupNow();">'.Dict::S('bkp-button-backup-now').'</button>');
  271. $oP->add('<div id="backup_success" class="header_message message_ok" style="display: none;"></div>');
  272. $oP->add('<div id="backup_errors" class="header_message message_error" style="display: none;"></div>');
  273. $oP->add('<input type="hidden" name="restore_token" id="restore_token"/>');
  274. $sConfirmBackup = addslashes(Dict::S('bkp-confirm-backup'));
  275. $sPleaseWaitBackup = addslashes(Dict::S('bkp-wait-backup'));
  276. $sPleaseWaitRestore = addslashes(Dict::S('bkp-wait-restore'));
  277. $sRestoreDone = addslashes(Dict::S('bkp-success-restore'));
  278. $sMySQLBinDir = addslashes(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
  279. $sDBHost = addslashes(MetaModel::GetConfig()->GetDBHost());
  280. $sDBUser = addslashes(MetaModel::GetConfig()->GetDBUser());
  281. $sDBPwd = addslashes(MetaModel::GetConfig()->GetDBPwd());
  282. $sDBName = addslashes(MetaModel::GetConfig()->GetDBName());
  283. $sDBSubName = addslashes(MetaModel::GetConfig()->GetDBSubName());
  284. $sEnvironment = addslashes(utils::GetCurrentEnvironment());
  285. $oP->add_script(
  286. <<<EOF
  287. function LaunchBackupNow()
  288. {
  289. $('#backup_success').hide();
  290. $('#backup_errors').hide();
  291. if (confirm('$sConfirmBackup'))
  292. {
  293. $.blockUI({ message: '<h1><img src="../images/indicator.gif" /> $sPleaseWaitBackup</h1>' });
  294. var oParams = {};
  295. oParams.operation = 'backup';
  296. $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){
  297. if (data.search(/error|exceptio|notice|warning/i) != -1)
  298. {
  299. $('#backup_errors').html(data);
  300. $('#backup_errors').show();
  301. }
  302. else
  303. {
  304. window.location.reload();
  305. }
  306. $.unblockUI();
  307. });
  308. }
  309. }
  310. function LaunchRestoreNow(sBackupFile, sConfirmationMessage)
  311. {
  312. if (confirm(sConfirmationMessage))
  313. {
  314. $.blockUI({ message: '<h1><img src="../images/indicator.gif" /> $sPleaseWaitRestore</h1>' });
  315. $('#backup_success').hide();
  316. $('#backup_errors').hide();
  317. var oParams = {};
  318. oParams.operation = 'restore_get_token';
  319. oParams.file = sBackupFile;
  320. $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){
  321. // Get the value of restore_token
  322. $('#backup_errors').append(data);
  323. var oParams = {};
  324. oParams.operation = 'restore_exec';
  325. oParams.token = $("#restore_token").val();
  326. oParams.mysql_bindir = '$sMySQLBinDir';
  327. oParams.db_host = '$sDBHost';
  328. oParams.db_user = '$sDBUser';
  329. oParams.db_pwd = '$sDBPwd';
  330. oParams.db_name = '$sDBName';
  331. oParams.db_subname = '$sDBSubName';
  332. oParams.environment = '$sEnvironment';
  333. if (oParams.token.length > 0)
  334. {
  335. $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){
  336. if (data.search(/error|exceptio|notice|warning/i) != -1)
  337. {
  338. $('#backup_success').hide();
  339. $('#backup_errors').html(data);
  340. $('#backup_errors').show();
  341. }
  342. else
  343. {
  344. $('#backup_errors').hide();
  345. $('#backup_success').html('$sRestoreDone');
  346. $('#backup_success').show();
  347. }
  348. $.unblockUI();
  349. });
  350. }
  351. else
  352. {
  353. $('button.restore').attr('disabled', 'disabled');
  354. $.unblockUI();
  355. }
  356. });
  357. }
  358. }
  359. EOF
  360. );
  361. if (MetaModel::GetConfig()->Get('demo_mode'))
  362. {
  363. $oP->add_ready_script("$('button').attr('disabled', 'disabled').attr('title', 'Disabled in demonstration mode')");
  364. }
  365. }
  366. catch(Exception $e)
  367. {
  368. $oP->p('<b>'.$e->getMessage().'</b>');
  369. }
  370. $oP->output();
  371. ?>