array(), 'path' => $val[2], 'version' => $val[3], 'media' => $val[4], 'section' => $val[5], 'user' => $val[7], 'host' => $val[8], 'job' => $val[9], 'revision' => '', 'summary' => '', 'package' => '' ); } $status = ''; if ($val[1] != 'done' && $val[1] != 'failure') { // partial/done are detected with .done files // failures are detected with .fail files $status = $val[1]; } $data = $val[10]; if (preg_match("/@(\d+):/", $data, $revision)) { $pkgs[$key]['revision'] = $revision[1]; } $ext = $val[11]; if ($ext == '.src.rpm.info') { preg_match("!^(?:@\d+:)?(.*)!", $data, $name); $pkgs[$key]['package'] = $name[1]; } else if ($ext == '.src.rpm') { $pkgs[$key]['status']['src'] = 1; } else if ($ext == '.upload') { $pkgs[$key]['status']['upload'] = 1; } else if ($ext == '.lock') { preg_match("!(.*)\.iurt\.(.*)\.\d+\.\d+!", $data, $buildhost); if (!isset($hosts[$buildhost[2]])) { $hosts[$buildhost[2]]= array(); } $hosts[$buildhost[2]][$buildhost[1]] = $key; if (isset($pkgs[$key]['status']['build'])) { array_push($pkgs[$key]['status']['build'], $buildhost[2]); } else { $pkgs[$key]['status']['build'] = array($buildhost[2]); } } else if ($ext == '.done') { // beware! this block is called twice for a given $key $status = 'done'; $pkgs[$key]['buildtime']['start'] = key2timestamp($val[6]); $pkgs[$key]['buildtime']['end'] = round($val[12]); $pkgs[$key]['buildtime']['diff'] = $pkgs[$key]['buildtime']['end'] - $pkgs[$key]['buildtime']['start']; @$build_dates[gmdate('G', $pkgs[$key]['buildtime']['start'])] += 1; // keep obviously dubious values out of there // 12 hours is be an acceptable threshold given current BS global perfs // as of April 2011 if ($pkgs[$key]['buildtime']['diff'] < 43200) { $buildtime_total[$key] = $pkgs[$key]['buildtime']['diff']; } } else if ($ext == '.fail') { $arch = $data; if (!isset($pkgs[$key]['status']['fail'])) { $pkgs[$key]['status']['fail'] = array(); } if (!isset($pkgs[$key]['status']['kill'][$arch])) { $pkgs[$key]['status']['fail'][$arch] = 1; } if (in_array($arch, $mandatory_arches) || $arch == 'noarch') { $status = 'failure'; } } else if ($ext == '.kill') { if (!isset($pkgs[$key]['status']['kill'])) { $pkgs[$key]['status']['kill'] = array(); } $pkgs[$key]['status']['kill'][$arch] = 1; unset($pkgs[$key]['status']['fail'][$arch]); } if ($status !== '') { $pkgs[$key]['status'][$status] = 1; } } // filter packages if a package name was provided if (!is_null($package)) { foreach ($pkgs as $key => $pkg) { preg_match("/^(.*)-[^-]*-[^-]*$/", $pkg['package'], $name); if ($package != $name[1]) { unset($pkgs[$key]); } } } // sort by key in reverse order to have more recent pkgs first krsort($pkgs); ksort($build_dates); $build_count = count($buildtime_total); $buildtime_total = array_sum($buildtime_total); // above block. OUTPUT: $pkgs, $build_dates, $buildtime_total, $hosts return array( $pkgs, $hosts, $build_count, $build_dates, $buildtime_total ); } /** * Return a human-readable label for this package build status. * * @param array $pkg package information * * @return string */ function pkg_gettype($pkg) { $labels = array( 'rejected' => 'rejected', 'upload' => 'uploaded', 'failure' => 'failure', 'done' => 'partial', 'build' => 'building', 'src' => 'todo' ); foreach ($labels as $k => $v) { if (array_key_exists($k, $pkg['status'])) { return $v; } } return 'unknown'; } /** * @param integer $num * * @return string */ function plural($num) { if ($num > 1) return "s"; } /** * Return timestamp from package key * * @param string $key package submission key * * @return integer */ function key2timestamp($key) { global $tz; $date = DateTime::createFromFormat("YmdHis", (int)$key, $tz); if ($date === false) return null; return $date->getTimestamp(); } /** * Return human-readable time difference * * @param integer $start timestamp * @param integer $end timestamp, defaults to now * * @return string */ function timediff($start, $end = null) { if (is_null($end)) { $end = time(); } $diff = $end - $start; if ($diff < 60) { return $diff . " second" . plural($diff); } $diff = round($diff/60); if ($diff < 60) { return $diff . " minute" . plural($diff); } $diff = round($diff/60); if ($diff < 24) { return $diff . " hour" . plural($diff); } $diff = round($diff/24); return $diff . " day" . plural($diff); } /** * Compare two duration strings * * @param string $a "1 hour" or "23 mins" * @param string $b * * @return integer */ function timesort($a, $b) { $a = explode(' ', trim($a)); $b = explode(' ', trim($b)); if ($a[1] == 'hour' || $a[1] == 'hours') { $a[0] *= 3600; } if ($b[1] == 'hour' || $a[1] == 'hours') { $b[0] *= 3600; } if ($a[0] > $b[0]) { return 1; } elseif ($a[0] < $b[0]) { return -1; } else { return 0; } } /** * Publish BS stats as HTTP headers for remote services to adapt. * * @param array $stats * @param integer $buildtime_total * @param integer $build_count * @param array $last_package * * @return void */ function publish_stats_headers($stats, $buildtime_total, $buildtime_avg, $build_count, $last_package = null) { foreach ($stats as $k => $v) { header("X-BS-Queue-$k: $v"); } $w = $stats['todo'] - 10; if ($w < 0) { $w = 0; } $w = $w * 60; header("X-BS-Throttle: $w"); if (!is_null($last_package)) { header("X-BS-Package-Status: ".$last_package['type']); } header(sprintf('X-BS-Buildtime: %d', round($buildtime_total))); header(sprintf('X-BS-Buildtime-Average: %5.2f', $buildtime_avg)); } /** * check if emi is running * * @return */ function get_upload_time() { if (file_exists('/var/lib/schedbot/tmp/upload')) { $stat = stat('/var/lib/schedbot/tmp/upload'); if ($stat) { return $stat['mtime']; } } return null; } /** * Build and return stats about all packages. * * @todo should not alter/return $pkgs * * @param array $pkgs * * @return array (array, array, integer, array) */ function build_stats($pkgs) { // count all packages statuses $stats = array( 'todo' => 0, 'building' => 0, 'partial' => 0, 'uploaded' => 0, 'rejected' => 0, 'failure' => 0 ); $total = count($pkgs); // count users' packages $users = array(); if ($total > 0) { foreach ($pkgs as $key => $p) { $pkgs[$key]['type'] = pkg_gettype($p); $stats[$pkgs[$key]['type']] += 1; if (!array_key_exists($p['user'], $users)) { $users[$p['user']] = 1; } else { $users[$p['user']] += 1; } } } return array( $stats, $users, $total, $pkgs ); } class mga_bs_charts { public static function js_init() { return << S; } public static function js_draw_charts() { return << $count) { $rows[] = sprintf("['%s', %d]", $status, $count); } $rows = implode(', ', $rows); $s = << $count) { $rows[] = sprintf("['%s', %d]", $packager, $count); } $rows = implode(', ', $rows); $s = << $count) { if (false !== strpos($duration, 'hour')) { if (!array_key_exists('20 minutes', $newdata)) { $newdata['60 minutes'] = $count; } else { $newdata['60 minutes'] += $count; } } else { $d = explode(' ', $duration); if ($d[0] >= 20 && $d[1] == "minutes") { if (!array_key_exists('20 minutes', $newdata)) { $newdata['20 minutes'] = $count; } else { $newdata['20 minutes'] += $count; } } elseif ($d[0] >= 10 && $d[1] == "minutes") { if (!array_key_exists('10 minutes', $newdata)) { $newdata['10 minutes'] = $count; } else { $newdata['10 minutes'] += $count; } } else { $newdata[$duration] = $count; } } } uksort($newdata, "timesort"); $rows = array("['Duration', 'Builds']"); foreach ($newdata as $duration => $count) { if ($duration == '0 second') { $duration = '<1'; } elseif ($duration == '10 minutes') { $duration = '≥10'; } elseif ($duration == '20 minutes') { $duration = '≥20'; } elseif ($duration == '60 minutes') { $duration = '≥60'; } else { $duration = explode(' ', $duration); $duration = $duration[0]; } $rows[] = sprintf("['%s', %d]", $duration, $count); } $rows = implode(', ', $rows); return << $count) { $rows[] = sprintf("['%s', %d]", $hour, $count); } $rows = implode(', ', $rows); return <<