Poudriere support (#16229)

* add the poller

* save text status stuff

* add initial app page stuff

* add history table and start work on graphs

* checkpoint

* re-work the poudriere graph bits a bit more

* fix the poller and start work on the graphs

* polling fix

* more work on the poudriere stuff

* poke stuff with php-cs-fix

* add some more graphs

* more style fixes

* another style fix

* add more graphs

* add Poudriere docs

* add tests data

* some minor tests fixes

* a test tweak

* more test tweaks

* some more test tweaks

* tweak .data.build_info data .data.build_info to make the app data test saner

* derp... missed a item

* add app data
This commit is contained in:
Zane C. Bowers-Hadley
2024-08-21 01:27:26 -05:00
committed by GitHub
parent 7aeb540e5a
commit 7b98721e32
31 changed files with 1899 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
<?php
$name = 'poudriere';
if (! isset($colours)) {
$colours = 'psychedelic';
}
$dostack = 0;
$printtotal = 0;
$addarea = 1;
$transparency = 15;
$scale_min = 0;
$rrd_list = [];
foreach ($stats_list as $stat_to_add) {
if (isset($vars['poudriere_set'])) {
$rrd_filename = Rrd::name($device['hostname'], ['app', $name, $app->app_id, 'jps___' . $vars['poudriere_set'] . '___' . $stat_to_add['stat']]);
} else {
$rrd_filename = Rrd::name($device['hostname'], ['app', $name, $app->app_id, 'totals_' . $stat_to_add['stat']]);
}
if (Rrd::checkRrdExists($rrd_filename)) {
$rrd_list[] = [
'filename' => $rrd_filename,
'descr' => $stat_to_add['descr'],
'ds' => 'data',
];
}
}
require 'includes/html/graphs/generic_multi_line.inc.php';

View File

@@ -0,0 +1,16 @@
<?php
$unit_text = 'switches';
$stats_list = [
'voluntary-context-switches' => [
'stat' => 'voluntary-context-switches',
'descr' => 'Voluntary Context',
],
'involuntary-context-switches' => [
'stat' => 'involuntary-context-switches',
'descr' => 'Involuntary Context',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'faults';
$stats_list = [
'copy-on-write-faults' => [
'stat' => 'copy-on-write-faults',
'descr' => 'COW Faults',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = '';
$stats_list = [
'percent-cpu' => [
'stat' => 'percent-cpu',
'descr' => 'CPU%',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'seconds';
$stats_list = [
'cpu-time' => [
'stat' => 'cpu-time',
'descr' => 'CPU Time',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'kilobytes';
$stats_list = [
'data-size' => [
'stat' => 'data-size',
'descr' => 'Data Size',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'switches';
$stats_list = [
'involuntary-context-switches' => [
'stat' => 'involuntary-context-switches',
'descr' => 'Involuntary Context',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,20 @@
<?php
$unit_text = 'bytes';
$stats_list = [
'log_size_done' => [
'stat' => 'log_size_done',
'descr' => 'done',
],
'log_size_latest' => [
'stat' => 'log_size_latest',
'descr' => 'latest',
],
'log_size_per_package' => [
'stat' => 'log_size_per_package',
'descr' => 'per_package',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'faults';
$stats_list = [
'major-faults' => [
'stat' => 'major-faults',
'descr' => 'Major Faults',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = '';
$stats_list = [
'percent-memory' => [
'stat' => 'percent-memory',
'descr' => 'Mem%',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'faults';
$stats_list = [
'minor-faults' => [
'stat' => 'minor-faults',
'descr' => 'Minor Faults',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,20 @@
<?php
$unit_text = 'bytes';
$stats_list = [
'package_size_all' => [
'stat' => 'package_size_all',
'descr' => 'all',
],
'package_size_building' => [
'stat' => 'package_size_building',
'descr' => 'building',
],
'package_size_latest' => [
'stat' => 'package_size_latest',
'descr' => 'latest',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,66 @@
<?php
$unit_text = 'count';
$colours = 'rainbow';
$stats_list = [
'check-sanity' => [
'stat' => 'check-sanity',
'descr' => 'check-sanity',
],
'pkg-depends' => [
'stat' => 'pkg-depends',
'descr' => 'pkg-depends',
],
'fetch-depends' => [
'stat' => 'fetch-depends',
'descr' => 'fetch-depends',
],
'fetch checksum' => [
'stat' => 'fetch checksum',
'descr' => 'fetch checksum',
],
'extract-depends' => [
'stat' => 'extract-depends',
'descr' => 'extract-depends',
],
'extract patch-depends' => [
'stat' => 'extract patch-depends',
'descr' => 'extract patch-depends',
],
'patch' => [
'stat' => 'patch',
'descr' => 'patch',
],
'build-depends' => [
'stat' => 'build-depends',
'descr' => 'build-depends',
],
'lib-depends' => [
'stat' => 'lib-depends',
'descr' => 'lib-depends',
],
'configure' => [
'stat' => 'configure',
'descr' => 'configure',
],
'build' => [
'stat' => 'build',
'descr' => 'build',
],
'run-depends' => [
'stat' => 'run-depends',
'descr' => 'run-depends',
],
'stage' => [
'stat' => 'stage',
'descr' => 'stage',
],
'package' => [
'stat' => 'package',
'descr' => 'package',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'blocks';
$stats_list = [
'read-blocks' => [
'stat' => 'read-blocks',
'descr' => 'Read Blocks',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'kilobytes';
$stats_list = [
'rss' => [
'stat' => 'rss',
'descr' => 'RSS',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,20 @@
<?php
$unit_text = 'kilobytes';
$stats_list = [
'stack-size' => [
'stat' => 'stack-size',
'descr' => 'Stack Size',
],
'data-size' => [
'stat' => 'data-size',
'descr' => 'Data Size',
],
'text-size' => [
'stat' => 'text-size',
'descr' => 'Text Size',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'kilobytes';
$stats_list = [
'stack-size' => [
'stat' => 'stack-size',
'descr' => 'Stack Size',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,36 @@
<?php
$unit_text = 'count';
$stats_list = [
'QUEUE' => [
'stat' => 'QUEUE',
'descr' => 'QUEUE',
],
'BUILT' => [
'stat' => 'BUILT',
'descr' => 'BUILT',
],
'FAIL' => [
'stat' => 'FAIL',
'descr' => 'FAIL',
],
'SKIP' => [
'stat' => 'SKIP',
'descr' => 'SKIP',
],
'IGNORE' => [
'stat' => 'IGNORE',
'descr' => 'IGNORE',
],
'REMAIN' => [
'stat' => 'REMAIN',
'descr' => 'REMAIN',
],
'FETCH' => [
'stat' => 'FETCH',
'descr' => 'FETCH',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'swaps';
$stats_list = [
'swaps' => [
'stat' => 'swaps',
'descr' => 'Swaps',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'seconds';
$stats_list = [
'system-time' => [
'stat' => 'system-time',
'descr' => 'System Time',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'kilobytes';
$stats_list = [
'text-size' => [
'stat' => 'text-size',
'descr' => 'Text Size',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'count';
$stats_list = [
'threads' => [
'stat' => 'threads',
'descr' => 'Threads',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'seconds';
$stats_list = [
'TIME' => [
'stat' => 'TIME',
'descr' => 'TIME',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,20 @@
<?php
$unit_text = 'seconds';
$stats_list = [
'cpu-time' => [
'stat' => 'cpu-time',
'descr' => 'CPU Time',
],
'system-time' => [
'stat' => 'system-time',
'descr' => 'System Time',
],
'user-time' => [
'stat' => 'user-time',
'descr' => 'User Time',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'seconds';
$stats_list = [
'system-time' => [
'stat' => 'system-time',
'descr' => 'System Time',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,12 @@
<?php
$unit_text = 'switches';
$stats_list = [
'voluntary-context-switches' => [
'stat' => 'voluntary-context-switches',
'descr' => 'Voluntary Context',
],
];
require 'poudriere-common.inc.php';

View File

@@ -0,0 +1,334 @@
<?php
$name = 'poudriere';
$link_array = [
'page' => 'device',
'device' => $device['device_id'],
'tab' => 'apps',
'app' => 'poudriere',
];
$app_data = $app->data;
print_optionbar_start();
// print the link to the totals
$label = (isset($vars['poudriere_page']) || isset($vars['poudriere_set']))
? 'Totals'
: '<span class="pagemenu-selected">Totals</span>';
echo generate_link($label, $link_array);
echo ' | ';
// print the link to the details page
$label = (! isset($vars['poudriere_page']) && $vars['poudriere_page'] != 'details')
? 'Details'
: '<span class="pagemenu-selected">Details</span>';
echo generate_link($label, $link_array, ['poudriere_page' => 'details']);
echo ' | Sets: ';
$index_int = 0;
foreach ($app_data['sets'] as $index => $set_name) {
$label = (! isset($vars['poudriere_set']) || $vars['poudriere_set'] != $set_name)
? $set_name
: '<span class="pagemenu-selected">' . $set_name . '</span>';
$index_int++;
echo generate_link($label, $link_array, ['poudriere_set' => $set_name]);
if (isset($app_data['sets'][$index_int])) {
echo ', ';
}
}
print_optionbar_end();
$graphs = [];
if (isset($vars['poudriere_page']) && $vars['poudriere_page'] == 'details') {
print_optionbar_start();
if (isset($app_data['status']) && ! is_null($app_data['status'])) {
echo "<b><center>Status</center></b><br>\n";
$table = [
'headers' => [
'Set',
'Ports',
'Jail',
'Build',
'Status',
'Queue',
'Built',
'Fail',
'Skip',
'Ignore',
'Fetch',
'Remain',
'Time',
'Logs',
],
'rows' => [],
];
$status_split = explode("\n", $app_data['status']);
$status_split_int = 1;
while (isset($status_split[$status_split_int])) {
$line = preg_replace("/^\s+/", '', $status_split[$status_split_int]);
$row = preg_split("/\s+/", $line, 14);
if (isset($row[13])) {
$table['rows'][] = [
['data' => $row[0]],
['data' => $row[1]],
['data' => $row[2]],
['data' => $row[3]],
['data' => $row[4]],
['data' => $row[5]],
['data' => $row[6]],
['data' => $row[7]],
['data' => $row[8]],
['data' => $row[9]],
['data' => $row[10]],
['data' => $row[11]],
['data' => $row[12]],
['data' => $row[13]],
];
}
$status_split_int++;
}
echo view('widgets/sortable_table', $table);
}
if (isset($app_data['build_info']) && ! is_null($app_data['build_info'])) {
echo "<b><center>Build Info</center></b><br>\n";
$table = [
'headers' => [
'Set',
'Build',
'ID',
'Total',
'Origin',
'Pkg Name',
'Phase',
'Time',
'TMP FS',
'CPU%',
'MEM%',
],
'rows' => [],
];
$build_split = explode("\n", $app_data['build_info']);
$build_split_int = 0;
while (isset($build_split[$build_split_int])) {
$line = preg_replace("/^\s+/", '', $build_split[$build_split_int]);
if (preg_match('/\[.*\]\ +\[.*\]\ +\[.*\]/', $line)) {
$row = preg_split("/\s+/", $line, 14);
if (isset($row[0])) {
$current_set = preg_replace("/[\[\]]/", '', $row[0]);
} else {
$current_set = '';
}
if (isset($row[1])) {
$current_build = preg_replace("/[\[\]]/", '', $row[1]);
} else {
$current_build = '';
}
} elseif (preg_match('/^\[.*\]/', $line)) {
$line_split = preg_split("/[^\w\d\-\,\.\:\/\@\%]+/", $line, 14);
if (isset($line_split[9])) {
$tmp_fs = $line_split[7];
$cpu_perc = $line_split[8];
$mem_perc = $line_split[9];
} else {
$tmp_fs = '-';
$cpu_perc = $line_split[7];
$mem_perc = $line_split[8];
}
$table['rows'][] = [
['data' => $current_set],
['data' => $current_build],
['data' => $line_split[1]],
['data' => $line_split[2]],
['data' => $line_split[3]],
['data' => $line_split[4]],
['data' => $line_split[5]],
['data' => $line_split[6]],
['data' => $tmp_fs],
['data' => $cpu_perc],
['data' => $mem_perc],
];
}
$build_split_int++;
}
echo view('widgets/sortable_table', $table);
}
if (isset($app_data['history']) && ! is_null($app_data['history'])) {
echo "<b><center>History</center></b><br>\n";
$table = [
'headers' => [
'Set',
'Ports',
'Jail',
'Build',
'Status',
'Queue',
'Built',
'Fail',
'Skip',
'Ignore',
'Fetch',
'Remain',
'Time',
'Logs',
],
'rows' => [],
];
$status_split = explode("\n", $app_data['history']);
$status_split_int = 1;
while (isset($status_split[$status_split_int])) {
$line = preg_replace("/^\s+/", '', $status_split[$status_split_int]);
$row = preg_split("/\s+/", $line, 14);
if (isset($row[13])) {
$table['rows'][] = [
['data' => $row[0]],
['data' => $row[1]],
['data' => $row[2]],
['data' => $row[3]],
['data' => $row[4]],
['data' => $row[5]],
['data' => $row[6]],
['data' => $row[7]],
['data' => $row[8]],
['data' => $row[9]],
['data' => $row[10]],
['data' => $row[11]],
['data' => $row[12]],
['data' => $row[13]],
];
}
$status_split_int++;
}
echo view('widgets/sortable_table', $table);
}
print_optionbar_end();
} else {
$graphs = [
[
'type' => 'status',
'description' => 'General Status',
],
[
'type' => 'phase',
'description' => 'Build Phase',
],
[
'type' => 'time',
'description' => 'Build Time',
],
[
'type' => 'log_size',
'description' => 'Log Size',
],
[
'type' => 'package_size',
'description' => 'Package Size',
],
[
'type' => 'cpu_perc',
'description' => 'CPU%',
],
[
'type' => 'mem_perc',
'description' => 'Memory%',
],
[
'type' => 'time_comparison',
'description' => 'Time Comparison(CPU, User, System)',
],
[
'type' => 'cpu_time',
'description' => 'Time, CPU',
],
[
'type' => 'user_time',
'description' => 'Time, User',
],
[
'type' => 'system_time',
'description' => 'Time, System',
],
[
'type' => 'rss',
'description' => 'RSS',
],
[
'type' => 'threads',
'description' => 'Threads',
],
[
'type' => 'major_faults',
'description' => 'Faults, Major',
],
[
'type' => 'minor_faults',
'description' => 'Faults, Minor',
],
[
'type' => 'swaps',
'description' => 'Swaps',
],
[
'type' => 'size_comparison',
'description' => 'Size, Comparison(Stack, Data, Text)',
],
[
'type' => 'stack_size',
'description' => 'Size, Stack',
],
[
'type' => 'data_size',
'description' => 'Size, Data',
],
[
'type' => 'text_size',
'description' => 'Size, Text',
],
[
'type' => 'read_blocks',
'description' => 'Read Blocks',
],
[
'type' => 'copy_on_write_faults',
'description' => 'COW Faults',
],
[
'type' => 'context_switches_comparison',
'description' => 'Context Switches Comparison(Voluntary, Involuntary)',
],
[
'type' => 'voluntary_context_switches',
'description' => 'Context Switches, Voluntary',
],
[
'type' => 'involuntary_context_switches',
'description' => 'Context Switches, Involuntary',
],
];
}
foreach ($graphs as $key => $graph_info) {
$graph_type = $graph_info['type'];
$graph_array['height'] = '100';
$graph_array['width'] = '215';
$graph_array['to'] = time();
$graph_array['id'] = $app['app_id'];
$graph_array['type'] = 'application_' . $name . '_' . $graph_info['type'];
if (isset($vars['poudriere_set'])) {
$graph_array['poudriere_set'] = $vars['poudriere_set'];
}
echo '<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">' . $graph_info['description'] . '</h3>
</div>
<div class="panel-body">
<div class="row">';
include 'includes/html/print-graphrow.inc.php';
echo '</div>';
echo '</div>';
echo '</div>';
}

View File

@@ -0,0 +1,129 @@
<?php
use LibreNMS\Exceptions\JsonAppException;
use LibreNMS\RRD\RrdDefinition;
$name = 'poudriere';
try {
$returned = json_app_get($device, $name, 1);
} catch (JsonAppException $e) {
echo PHP_EOL . $name . ':' . $e->getCode() . ':' . $e->getMessage() . PHP_EOL;
update_application($app, $e->getCode() . ':' . $e->getMessage(), []); // Set empty metrics and error message
return;
}
$stat_vars = [
'BUILT',
'FAIL',
'FETCH',
'IGNORE',
'QUEUE',
'REMAIN',
'SKIP',
'TIME',
'build',
'build-depends',
'check-sanity',
'checksum',
'configure',
'copy-on-write-faults',
'cpu-time',
'data-size',
'elapsed-times',
'extract',
'extract-depends',
'fetch',
'fetch-depends',
'involuntary-context-switches',
'job-control-count',
'lib-depends',
'log_size_done',
'log_size_latest',
'log_size_per_package',
'major-faults',
'minor-faults',
'package',
'package_size_all',
'package_size_building',
'package_size_latest',
'patch',
'patch-depends',
'percent-cpu',
'percent-memory',
'pkg-depends',
'read-blocks',
'received-messages',
'rss',
'run-depends',
'sent-messages',
'stack-size',
'stage',
'swaps',
'system-time',
'text-size',
'threads',
'user-time',
'voluntary-context-switches',
'written-blocks',
];
$metrics = [];
$old_data = $app->data;
$new_data = [
'status' => $returned['data']['status'],
'build_info' => $returned['data']['build_info'],
'history' => $returned['data']['history'],
];
$data = $returned['data'];
$gauge_rrd_def = RrdDefinition::make()
->addDataset('data', 'GAUGE', 0);
// process total stats, .data.stats
foreach ($stat_vars as $key => $stat) {
$var_name = 'totals_' . $stat;
$value = $data['stats'][$stat];
$rrd_name = ['app', $name, $app->app_id, $var_name];
$fields = ['data' => $value];
$metrics[$var_name] = $value;
$tags = ['name' => $name, 'app_id' => $app->app_id, 'rrd_def' => $gauge_rrd_def, 'rrd_name' => $rrd_name];
data_update($device, 'app', $tags, $fields);
}
// process each jail/ports/sets item
$sets = [];
foreach ($data['jailANDportsANDset'] as $jps_key => $jps) {
$sets[] = $jps_key;
foreach ($stat_vars as $key => $stat) {
$var_name = 'jps___' . $jps_key . '___' . $stat;
$value = $jps[$stat];
$rrd_name = ['app', $name, $app->app_id, $var_name];
$fields = ['data' => $value];
$metrics[$var_name] = $value;
$tags = ['name' => $name, 'app_id' => $app->app_id, 'rrd_def' => $gauge_rrd_def, 'rrd_name' => $rrd_name];
data_update($device, 'app', $tags, $fields);
}
}
// check for added or removed jps sets
sort($sets);
$old_sets = $old_data['sets'] ?? [];
$added_sets = array_diff($sets, $old_sets);
$removed_sets = array_diff($old_sets, $sets);
$new_data['sets'] = $sets;
$app->data = $new_data;
// if we have any source instances, save and log
if (count($added_sets) > 0 || count($removed_sets) > 0) {
$log_message = 'Poudriere jail/ports/sets Change:';
$log_message .= count($added_sets) > 0 ? ' Added ' . implode(',', $added_sets) : '';
$log_message .= count($removed_sets) > 0 ? ' Removed ' . implode(',', $added_sets) : '';
log_event($log_message, $device, 'application');
}
// all done so update the app metrics
update_application($app, 'OK', $metrics);