PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/package/app/app/alpha/apps/kaltura/lib/batch/batchStatus.class.php

https://bitbucket.org/pandaos/kaltura
PHP | 429 lines | 227 code | 33 blank | 169 comment | 23 complexity | b750b896858c8ff60e1eb72b6aef9e1f MD5 | raw file
Possible License(s): AGPL-3.0, GPL-3.0, BSD-3-Clause, LGPL-2.1, GPL-2.0, LGPL-3.0, JSON, MPL-2.0-no-copyleft-exception, Apache-2.0
  1. <?php
  2. class batchStatus
  3. {
  4. public $batch_name = null;
  5. public $in_path = null;
  6. public $pending = 0;
  7. public $in_proc = 0;
  8. public $errors = null;
  9. public $last_log_time = null;
  10. public $oldest = null;
  11. public $newest = null;
  12. public $succeedded_in_period;
  13. public $failed_in_period;
  14. private $running_os = '';
  15. private $pending_data = array();
  16. private $in_proc_data = array();
  17. private $db_job_stats = null;
  18. private static $proc_res = null;
  19. private static $batch_names = array();
  20. function __construct()
  21. {
  22. $this->running_os = $this->getPlatform();
  23. }
  24. private static function getBatchNameFromCommandLine ( $batch_cmd_line )
  25. {
  26. $batch_name = $batch_cmd_line;
  27. // crack and find the name of the process from the command line
  28. $tokens = explode ( " " , $batch_cmd_line );
  29. foreach ( $tokens as $token )
  30. {
  31. // if found the word batch - use it
  32. if ( stripos ( $token , "batch" ) !== FALSE )
  33. {
  34. $batch_name = $token;
  35. break;
  36. }
  37. }
  38. return pathinfo( $batch_name , PATHINFO_FILENAME );
  39. }
  40. public static function cleanup ( )
  41. {
  42. foreach ( self::$batch_names as $batch_name )
  43. {
  44. self::batchEnd ( $batch_cmd_line );
  45. }
  46. }
  47. public static function batchStart( $batch_cmd_line )
  48. {
  49. $batch_name = self::getBatchNameFromCommandLine( $batch_cmd_line );
  50. $path = myContentStorage::getFSContentRootPath() . "/batchwatch/_$batch_name";
  51. touch ( $path );
  52. self::$batch_names[] = $batch_name;
  53. register_shutdown_function(array("batchStatus", "cleanup"));
  54. return $path;
  55. }
  56. public static function batchEnd( $batch_cmd_line )
  57. {
  58. $batch_name = self::getBatchNameFromCommandLine( $batch_cmd_line );
  59. $path = myContentStorage::getFSContentRootPath() . "/batchwatch/_$batch_name";
  60. @unlink ( $path );
  61. return $path;
  62. }
  63. public static function getPlatform()
  64. {
  65. if (isset($_SERVER['SERVER_SOFTWARE']))
  66. {
  67. $os = (substr_count($_SERVER['SERVER_SOFTWARE'], 'Win32'))? 'win': 'nix';
  68. }
  69. else
  70. {
  71. $os = (substr_count(strtolower(@$_SERVER['OS']), 'windows'))? 'win': 'nix';
  72. }
  73. return $os;
  74. }
  75. public function getProcessStatusByPid($pid)
  76. {
  77. if ($this->running_os == 'win')
  78. {
  79. $command = 'tasklist /FI "PID eq '.$pid.'"';
  80. }
  81. if ($this->running_os == 'nix')
  82. {
  83. $command = 'ps aux | grep '.$pid.' |grep -v grep';
  84. }
  85. exec($command, $output, $error);
  86. $output = trim(implode("\n",$output));
  87. if ($error || !$output)
  88. {
  89. return 0;
  90. }
  91. else
  92. {
  93. return 1;
  94. }
  95. }
  96. public static function getIndicatorFilesForBatch($batch_name)
  97. {
  98. $files_in_dir = scandir(myBatchBase::getBatchwatchPath());
  99. $batch_indicators = array();
  100. foreach($files_in_dir as $ind)
  101. {
  102. if (substr_count($ind, $batch_name.'.running'))
  103. {
  104. $batch_indicators[] = $ind;
  105. }
  106. }
  107. return $batch_indicators;
  108. }
  109. public function getStatus()
  110. {
  111. /* status check according to running.pid filename and platform relevant command */
  112. $batch_name = self::getBatchNameFromCommandLine( $this->batch_name );
  113. $running = 0;
  114. $batch_indicators = self::getIndicatorFilesForBatch($batch_name);
  115. foreach($batch_indicators as $ind)
  116. {
  117. $filename_parts = explode('.', $ind);
  118. if ($this->getProcessStatusByPid($filename_parts[2]))
  119. {
  120. $running++;
  121. }
  122. else
  123. {
  124. // indicator exists, process doesn't
  125. //echo "i should unlink now: ".$ind;
  126. unlink(myBatchBase::getBatchwatchPath().$ind);
  127. }
  128. }
  129. return $running;
  130. }
  131. public function getName( $name_only = true )
  132. {
  133. if ( $name_only )
  134. return self::getBatchNameFromCommandLine( $this->batch_name );
  135. return $this->batch_name;
  136. }
  137. public static function getPhpPath()
  138. {
  139. $kaltura_env_path = myContentStorage::getFSContentRootPath() . '/kaltura/alpha/batch/kaltura_env.sh';
  140. $kaltura_env = file_get_contents($kaltura_env_path);
  141. $kaltura_env_lines = explode("\n", $kaltura_env);
  142. foreach($kaltura_env_lines as $line)
  143. {
  144. // find PHP_PATH and return the path
  145. if(substr_count($line, 'PHP_PATH'))
  146. {
  147. $parts = explode('=', $line);
  148. return $parts[1];
  149. }
  150. }
  151. throw("could not find php path");
  152. }
  153. public static function executeRunBatch ( $action , $batch_name )
  154. {
  155. $output = array();
  156. $error = "";
  157. $batch_runner_log = myContentStorage::getFSContentRootPath().'/logs/'.php_uname('n').'-batchRunner.log';
  158. //$command = myContentStorage::getFSContentRootPath() . '/kaltura/alpha/batch/runBatch.sh ' . $action . " " . $batch_name;
  159. $runbatch_path = myContentStorage::getFSContentRootPath() .'/kaltura/alpha/batch/runBatch.php';
  160. if (self::getPlatform() == 'win')
  161. {
  162. $args = '"'.$runbatch_path.'" "'. $action .'" "'. $batch_name .'"';
  163. pclose(popen("start \"kaltura_batchrunner\" \"" . self::getPhpPath() . "\" " . $args, "r"));
  164. }
  165. else
  166. {
  167. $args = $runbatch_path.' '. $action .' '. $batch_name.' >> '.$batch_runner_log.' &';
  168. pclose(popen(self::getPhpPath() . " " . $args, "r"));
  169. }
  170. // exec($command, $output, $error);
  171. }
  172. /*
  173. * When creating a batchWatch command do 2 things:
  174. * 1. create the batchwatch/ file for the batchWatch to handle
  175. * 2. execute the same command line the batchwatch would only in the scope of the web
  176. *
  177. * the 2 together will make sure the processes will really stop/start/restart
  178. */
  179. private function createBatchwatchCmd ( $cmd )
  180. {
  181. // use this with no prefix
  182. $batch_name = self::getBatchNameFromCommandLine( $this->batch_name );
  183. $path = myContentStorage::getFSContentRootPath() . "/batchwatch/$batch_name";
  184. self::executeRunBatch ( $cmd , $batch_name );
  185. if ( $cmd == "stop")
  186. {
  187. $path = batchStatus::batchEnd( $batch_name );
  188. }
  189. return $path;
  190. }
  191. public function start()
  192. {
  193. if (!$this->getStatus())
  194. {
  195. return $this->createBatchwatchCmd( "start" );
  196. }
  197. return 1;
  198. }
  199. public function stop()
  200. {
  201. if($this->getStatus())
  202. {
  203. return $this->createBatchwatchCmd( "stop" );
  204. }
  205. return 1;
  206. }
  207. public function restart()
  208. {
  209. return $this->createBatchwatchCmd( "restart" );
  210. }
  211. public function addToPending ( $title , $count )
  212. {
  213. if ( ! $count ) $count = 0;
  214. $this->pending_data[$title] = $count ;
  215. $this->pending += $count ;
  216. }
  217. public function getPendingData()
  218. {
  219. if ( $this->pending_data )
  220. return $this->pending_data;
  221. return null;
  222. }
  223. public function addToInProc ( $title , $count )
  224. {
  225. if ( ! $count ) $count = 0;
  226. $this->in_proc_data[$title] = $count ;
  227. $this->in_proc += $count ;
  228. }
  229. public function getInProcData()
  230. {
  231. if ( $this->in_proc_data )
  232. return $this->in_proc_data;
  233. return null;
  234. }
  235. public function getLogData ( $log_name )
  236. {
  237. $file_name = null;
  238. $log_timestamp = "";
  239. $log_size = "0";
  240. if ( $log_name )
  241. {
  242. $log_dir = myContentStorage::getFSContentRootPath() . "/logs/";
  243. $pat = $log_dir . "/*$log_name*";
  244. $possible_log_files = glob ( $pat );
  245. // iterate through all the files - the last (and most updated) will be taken
  246. // TODO - the above didn't work - stopped at the first log - SHOUD FIX
  247. foreach ( $possible_log_files as $file )
  248. {
  249. $file_name = realpath ( $file );
  250. clearstatcache();//true , $file_name );
  251. $log_timestamp = filemtime ( $file_name );
  252. $log_size = filesize( $file_name );
  253. break;
  254. }
  255. }
  256. if ( ! $file_name ) $file_name = $log_name;
  257. return array ( $file_name , $log_timestamp , $log_size );
  258. }
  259. public function getDiskStatsCount ( $log_name , $dir , $pattern = null )
  260. {
  261. //print ( "getDiskStatsCount ( $log_name , $dir , $pattern = null )<br>") ;
  262. $oldest = null;
  263. $newest = 0;
  264. $path = realpath ( $dir ) . ( $pattern ? "/$pattern" : "/*" );
  265. $files = glob($path);
  266. $count = count ( $files );
  267. return $count;
  268. }
  269. public function getDiskStats ( $log_name , $dir , $pattern = null )
  270. {
  271. $oldest = null;
  272. $newest = 0;
  273. $path = realpath ( $dir ) . ( $pattern ? "/$pattern" : "/*" );
  274. $files = glob($path);
  275. $count = count ( $files );
  276. foreach ( $files as $file )
  277. {
  278. $timestamp = filemtime ( $file );
  279. if ( $oldest == null || $timestamp < $oldest ) $oldest = $timestamp;
  280. if ( $timestamp > $newest ) $newest = $timestamp;
  281. }
  282. if ( $oldest > $newest ) $oldest = $newest;
  283. return array (
  284. "service_name" => $log_name , // all log_names are called after their services
  285. "path" => $path ,
  286. "count" => $count ,
  287. "oldest" => $oldest ,
  288. "newest" => $newest ,
  289. );
  290. }
  291. private function getJobStats ( $hours_ago = 24 )
  292. {
  293. $connection = Propel::getConnection();
  294. $from_date = date("Y-m-d H:i:s", time()- $hours_ago * 3600 );
  295. // don't fetch status 7=aborted
  296. // need type 1=import, 3=flatten, 4=bulkupload and 6=download
  297. // select status = 5 as well , but don't include in problematic count
  298. $query = "select job_type , status , UNIX_TIMESTAMP(min(created_at)) as oldest , UNIX_TIMESTAMP(max(created_at)) as newest , count(1) as cnt " .
  299. "from batch_job where status in (0,1,2,3,4,6 , 5) and job_type in (1,3,4,6) and created_at>\"$from_date\" " .
  300. "group by job_type,status";
  301. $statement = $connection->prepareStatement($query);
  302. $resultset = $statement->executeQuery();
  303. $db_job_stats = array();
  304. while ($resultset->next())
  305. {
  306. $job_type = $resultset->getInt('job_type');
  307. $status = $resultset->getInt('status');
  308. // $oldest = $resultset->getTimestamp('oldest');
  309. // $newest = $resultset->getTimestamp('newest');
  310. $oldest = $resultset->getInt('oldest');
  311. $newest = $resultset->getInt('newest');
  312. $count = $resultset->getInt('cnt');
  313. if ( !isset ( $db_job_stats[$job_type] ) )
  314. {
  315. $job_type_data = array();
  316. }
  317. else
  318. {
  319. $job_type_data = $db_job_stats[$job_type];
  320. }
  321. // foreach job_type - creaet an array per status
  322. $job_type_data [$status] = array ( "oldest" => $oldest , "newest" => $newest , "count" => $count ) ;
  323. $db_job_stats[$job_type] = $job_type_data;
  324. }
  325. return $db_job_stats;
  326. }
  327. public function getDbStats ( $log_name , $job_type , $successful_stats_enum = BatchJob::BATCHJOB_STATUS_FINISHED ,
  328. $failed_stats_enum = BatchJob::BATCHJOB_STATUS_FAILED )
  329. {
  330. if ( $this->db_job_stats == null )
  331. {
  332. $this->db_job_stats = $this->getJobStats ( 2400 );
  333. }
  334. $oldest = null;
  335. $newest = 0;
  336. $count = 0;
  337. $stats_for_job = @$this->db_job_stats[$job_type];
  338. $successful_stats = null;
  339. $failed_stats = null;
  340. if ( $stats_for_job )
  341. {
  342. foreach ( $stats_for_job as $status => $stats_for_job_per_status )
  343. {
  344. if ( $status == $successful_stats_enum )
  345. {
  346. $successful_stats = $stats_for_job_per_status;
  347. }
  348. elseif ( $status == $failed_stats_enum )
  349. {
  350. $failed_stats = $stats_for_job_per_status;
  351. }
  352. else
  353. {
  354. if ( $oldest == null || $stats_for_job_per_status["oldest"] < $oldest )
  355. $oldest = $stats_for_job_per_status["oldest"];
  356. if ( $stats_for_job_per_status["newest"] > $newest ) $newest = $stats_for_job_per_status["newest"];
  357. $count += $stats_for_job_per_status["count"];
  358. }
  359. }
  360. }
  361. list ( $file_name , $log_timestamp , $log_size ) = $this->getLogData( $log_name );
  362. if ( $oldest > $newest ) $oldest = $newest;
  363. return array (
  364. "service_name" => $log_name , // all log_names are called after their services
  365. "path" => null ,
  366. "count" => $count ,
  367. "oldest" => $oldest ,
  368. "newest" => $newest ,
  369. "full_stats" => $stats_for_job ,
  370. "successful_stats" => $successful_stats ,
  371. "failed_stats" => $failed_stats ,
  372. "log_name" => $file_name ,
  373. "log_timestamp" => $log_timestamp ,
  374. "log_size" => $log_size );
  375. }
  376. }
  377. ?>