<?php
$INC = isset($INC) ? $INC : dirname(__FILE__);
require_once($INC . '/repo.inc.php');
require_once($INC . '/user.inc.php');
/*
* Function to load job list
*/
function rg_builder_load_jobs($db, $where, $order, $limit)
{
$ret = array();
$ret['ok'] = 0;
$ret['list'] = array();
while (1) {
$sql = 'SELECT * FROM build_jobs'
. ' WHERE ' . $where
. ' ORDER BY ' . $order
. ' ' . $limit;
$res = rg_sql_query($db, $sql);
if ($res === FALSE)
break;
while (($row = rg_sql_fetch_array($res))) {
$jid = $row['id'];
// TODO: set 'url' when adding the job in queue!
// Why not now?
// TODO: in case of errors, we should abort the job!
$row['request'] = rg_unserialize($row['request']);
if ($row['request'] === FALSE)
$row['request'] = array();
$row['status'] = rg_unserialize($row['status']);
if ($row['status'] === FALSE)
$row['status'] = array();
$ret['list'][$jid] = $row;
}
rg_sql_free_result($res);
$ret['ok'] = 1;
break;
}
return $ret;
}
/*
* Add a build job in queue
*/
function rg_builder_add($db, $repo_id, $d)
{
rg_log_ml('builder_add: ' . print_r($d, TRUE));
$ret = array('ok' => 0);
while (1) {
$params = array(
'repo_id' => $repo_id,
'prio' => 10, // TODO
'itime' => time(),
'request' => rg_serialize($d),
'done' => 0,
'status' => ''
);
$sql = 'INSERT INTO build_jobs (repo_id, prio, itime'
. ', request, done, status)'
. ' VALUES (@@repo_id@@, @@prio@@, @@itime@@'
. ', @@request@@, @@done@@, @@status@@)';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
$ret['errmsg'] = 'cannot add job';
break;
}
rg_sql_free_result($res);
// TODO: notify build system to not poll?
$ret['ok'] = 1;
break;
}
return $ret;
}
/*
* List virtual machines
* TODO: for now, only libvirt
*/
function rg_builder_vm_list()
{
$cmd = 'virsh list --name';
$r = rg_exec($cmd, '', FALSE, FALSE, FALSE);
if ($r['ok'] != 1) {
rg_log('Cannot find out virtual machines: ' . $r['errmsg']);
return FALSE;
}
return explode("\n", trim($r['data']));
}
/*
* Function executed when a job is done
*/
function rg_builder_done($db, $job, $s)
{
rg_log_enter('builder_done');
//rg_log_ml('DEBUG: builder_done: job: ' . print_r($job, TRUE));
//rg_log_ml('DEBUG: builder_done: status: ' . print_r($s, TRUE));
// old way
$req = isset($job['request']) ? $job['request'] : $job;
$job['done'] = time();
$ret = FALSE;
$rollback = FALSE;
while (1) {
$res = rg_sql_begin($db);
if ($res === FALSE)
break;
$rollback = TRUE;
$labels = $s['labels'];
// Some cosmetic stuff
$env = $req['env'];
$labels[] = 'worker_elap/' . ($s['done'] - $s['start']) . 's';
$labels[] = 'wait_time/' . ($job['worker_sent'] - $job['itime']) . 's';
$labels[] = 'date/' . gmdate('Y-m-d', $job['itime']);
$labels[] = 'time/' . gmdate('H:i', $job['itime']);
// add labels to the commit
$params = array(
'repo_id' => $job['repo_id'],
'head' => $req['head'],
'type' => 'build',
'misc' => $env,
'labels' => rg_serialize($labels),
'itime' => time()
);
$sql = 'DELETE FROM commit_labels'
. ' WHERE repo_id = @@repo_id@@'
. ' AND type = @@type@@'
. ' AND misc = @@misc@@'
. ' AND head = @@head@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE)
break;
rg_sql_free_result($res);
$sql = 'INSERT INTO commit_labels (repo_id, head, type, misc'
. ', labels, itime)'
. ' VALUES (@@repo_id@@, @@head@@, @@type@@'
. ', @@misc@@, @@labels@@, @@itime@@)';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE)
break;
rg_sql_free_result($res);
rg_cache_unset('repo_commit_labels' . '::' . $job['repo_id']
. '::' . $req['head'], RG_SOCKET_NO_WAIT);
$params = array(
'id' => $job['id'],
'done' => $job['done'],
'status' => rg_serialize($s)
);
$sql = 'UPDATE build_jobs SET done = @@done@@'
. ', status = @@status@@'
. ' WHERE id = @@id@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE)
break;
rg_sql_free_result($res);
$ev = array(
'category' => 'wh_build_job_done',
'prio' => 100,
'ui' => array('uid' => $req['uid']),
'job' => $job,
'status' => $s
);
$r = rg_event_add($db, $ev);
if ($r !== TRUE) {
rg_repo_set_error('cannot add event'
. ' (' . rg_event_error() . ')');
break;
}
$res = rg_sql_commit($db);
if ($res === FALSE)
break;
$rollback = FALSE;
$key = 'wh' . '::' . $req['uid'] . '::' . 'list'
. '::' . $req['hook_id'];
unset($params['id']);
rg_cache_merge($key, $params, RG_SOCKET_NO_WAIT);
rg_event_signal_daemon('', 0);
$ret = TRUE;
break;
}
if ($rollback)
rg_sql_rollback($db);
rg_log_exit();
return $ret;
}
/*
* Nice status for a job
*/
function rg_builder_nice_status(&$a)
{
rg_log_ml('DEBUG: nice_status: a: ' . print_r($a, TRUE));
$a['start_nice'] = gmdate('Y-m-d H:i', intval($a['start']));
$a['net_ok_nice'] = gmdate('Y-m-d H:i', intval($a['net_ok']));
$a['pkgs_ok_nice'] = gmdate('Y-m-d H:i', intval($a['pkgs_ok']));
$a['done_nice'] = gmdate('Y-m-d H:i', intval($a['done']));
if (isset($a['vm_start']) && ($a['vm_start'] > 0))
$a['vm_start_nice'] = gmdate('Y-m-d H:i', $a['vm_start']);
else
$a['vm_start_nice'] = 'n/a';
if (isset($a['build_sh_start']) && ($a['build_sh_start'] > 0))
$a['build_sh_start_nice'] = gmdate('Y-m-d H:i', trim($a['build_sh_start']));
else
$a['build_sh_start_nice'] = 'n/a';
if (!isset($a['clone_elap']))
$a['clone_elap_nice'] = 'n/a';
else
$a['clone_elap_nice'] = $a['clone_elap'] . 's';
foreach ($a['cmds'] as &$i) {
if (empty($i['start']))
continue;
$i['start_nice'] = gmdate('Y-m-d H:i', intval($i['start']));
$i['done_nice'] = gmdate('Y-m-d H:i', intval($i['done']));
$i['elap'] = rg_human_time_interval($i['done'] - $i['start'] + 1);
$_a = rg_xss_safe($i['log']);
$i['HTML:log_nlbr'] = nl2br($_a);
}
}
/*
* Cosmetic fixes
*/
function rg_builder_cosmetic($db, &$row)
{
rg_log_ml('DEBUG: builder_cosmetic: ' . print_r($row, TRUE));
if (isset($row['itime']))
$row['itime_nice'] = gmdate('Y-m-d H:i', $row['itime']);
else
$row['itime_nice'] = 'n/a';
if (!isset($row['done']))
$row['done'] = 0;
if ($row['done'] > 0)
$row['done_nice'] = gmdate('Y-m-d H:i', $row['done']);
else
$row['done_nice'] = 'n/a';
$ri = rg_repo_info($db, $row['repo_id'], 0, '');
if ($ri['ok'] == 1) {
$row['ri'] = $ri;
$row['user'] = rg_user_nice($db, $row['ri']['uid']);
} else {
$row['ri'] = array('name' => 'n/a');
$row['user'] = 'n/a';
}
}
/*
* Lists the build jobs
*/
function rg_builder_list_high_level($db, $rg, $op, $paras)
{
rg_prof_start('builder_list_high_level');
rg_log_enter('builder_list_high_level op=' . $op);
$ret = '';
$errmsg = array();
$rg['HTML:status'] = '';
if ($rg['login_ui']['is_admin'] == 1)
$where = '1 = 1';
else
$where = 'repo_id IN (SELECT repo_id FROM repos'
. ' WHERE uid = ' . $rg['login_ui']['uid'] . ')';
if (strcmp($op, 'queue') == 0)
$where .= ' AND done = 0';
$r = rg_builder_load_jobs($db, $where, 'itime DESC', 'LIMIT 100');
if ($r['ok'] != 1)
return rg_template('builder/load_err.html', $rg, TRUE /*xss*/);
// Preparing the list for template
$d = array();
foreach ($r['list'] as $_id => &$i) {
rg_builder_cosmetic($db, $i);
if (empty($i['status'])) {
$i['HTML:status_list'] = 'n/a';
} else {
if (!empty($i['status']['packages'])) {
$_x = array();
$_x['packages'] = rg_xss_safe($i['status']['packages']);
$i['HTML:status_packages'] =
rg_template('builder/packages.html', $_x, TRUE/*xss*/);
} else {
$i['HTML:status_packages'] = '';
}
rg_builder_nice_status($i['status']);
$i['HTML:status_list'] = rg_template_table('builder/cmds',
$i['status']['cmds'], $rg);
}
$d[] = $i;
}
rg_log_ml('DEBUG: d: ' . print_r($d, TRUE));
return rg_template_table('builder/queue', $d, $rg);
}