Chrony support (#12488)

* initial chrony implementation

* remove unused import

* add testing data

* fix test data

* fix tests

Co-authored-by: Jellyfrog <Jellyfrog@users.noreply.github.com>
This commit is contained in:
Serphentas
2021-03-18 20:11:25 +01:00
committed by GitHub
parent ac7b4539ce
commit c88ef64169
13 changed files with 560 additions and 0 deletions

View File

@@ -1634,3 +1634,29 @@ function nfsen_live_dir($hostname)
}
}
}
/**
* Get the ZFS pools for a device... just requires the device ID
* an empty return means ZFS is not in use or there are currently no pools
* @param $device_id
* @return array
*/
function get_chrony_sources($device_id)
{
$options = [
'filter' => [
'type' => ['=', 'chronyd'],
],
];
$component = new LibreNMS\Component();
$chronyd_cpnt = $component->getComponents($device_id, $options);
if (isset($chronyd_cpnt[$device_id])) {
$id = $component->getFirstComponentID($chronyd_cpnt, $device_id);
return json_decode($chronyd_cpnt[$device_id][$id]['sources']);
}
return [];
}

View File

@@ -0,0 +1,29 @@
<?php
require 'includes/html/graphs/common.inc.php';
$colours = 'mixed';
$nototal = (($width < 224) ? 1 : 0);
$unit_text = 'Seconds PPM';
$rrd_filename = rrd_name($device['hostname'], ['app', 'chronyd', $app['app_id']]);
$array = [
'frequency' => ['descr' => 'Error rate'],
'residual_frequency' => ['descr' => 'Ref clk offset'],
'skew' => ['descr' => 'Sys clk skew'],
];
$i = 0;
if (rrdtool_check_rrd_exists($rrd_filename)) {
foreach ($array as $ds => $var) {
$rrd_list[$i]['filename'] = $rrd_filename;
$rrd_list[$i]['descr'] = $var['descr'];
$rrd_list[$i]['ds'] = $ds;
$rrd_list[$i]['colour'] = \LibreNMS\Config::get("graph_colours.$colours.$i");
$i++;
}
} else {
echo "file missing: $file";
}
require 'includes/html/graphs/generic_multi_line.inc.php';

View File

@@ -0,0 +1,28 @@
<?php
require 'includes/html/graphs/common.inc.php';
$colours = 'mixed';
$nototal = (($width < 224) ? 1 : 0);
$unit_text = 'Seconds';
$rrd_filename = rrd_name($device['hostname'], ['app', 'chronyd', $app['app_id']]);
$array = [
'root_delay' => ['descr' => 'Root clk delay'],
'root_dispersion' => ['descr' => 'Root clk disp.'],
];
$i = 0;
if (rrdtool_check_rrd_exists($rrd_filename)) {
foreach ($array as $ds => $var) {
$rrd_list[$i]['filename'] = $rrd_filename;
$rrd_list[$i]['descr'] = $var['descr'];
$rrd_list[$i]['ds'] = $ds;
$rrd_list[$i]['colour'] = \LibreNMS\Config::get("graph_colours.$colours.$i");
$i++;
}
} else {
echo "file missing: $file";
}
require 'includes/html/graphs/generic_multi_line.inc.php';

View File

@@ -0,0 +1,27 @@
<?php
require 'includes/html/graphs/common.inc.php';
$colours = 'mixed';
$nototal = (($width < 224) ? 1 : 0);
$unit_text = 'Seconds PPM';
$rrd_filename = rrd_name($device['hostname'], ['app', 'chronyd', $app['app_id'], $vars['source']]);
$array = [
'frequency' => ['descr' => 'Estimated'],
'frequency_skew' => ['descr' => 'Est. error'],
];
$rrd_list = [];
if (rrdtool_check_rrd_exists($rrd_filename)) {
foreach ($array as $ds => $var) {
$rrd_list[$i]['filename'] = $rrd_filename;
$rrd_list[$i]['descr'] = $var['descr'];
$rrd_list[$i]['ds'] = $ds;
$rrd_list[$i]['colour'] = \LibreNMS\Config::get("graph_colours.$colours.$i");
$i++;
}
} else {
d_echo('RRD "' . $rrd_filename . '" not found');
}
require 'includes/html/graphs/generic_multi_line.inc.php';

View File

@@ -0,0 +1,29 @@
<?php
require 'includes/html/graphs/common.inc.php';
$colours = 'mixed';
$nototal = (($width < 224) ? 1 : 0);
$rrd_filename = rrd_name($device['hostname'], ['app', 'chronyd', $app['app_id'], $vars['source']]);
$array = [
'polling_rate' => ['descr' => 'Polling rate'],
'last_rx' => ['descr' => 'Last RX'],
'number_samplepoints' => ['descr' => '# sample pts'],
'number_runs' => ['descr' => '# runs'],
'span' => ['descr' => 'Sample span'],
];
$rrd_list = [];
if (rrdtool_check_rrd_exists($rrd_filename)) {
foreach ($array as $ds => $var) {
$rrd_list[$i]['filename'] = $rrd_filename;
$rrd_list[$i]['descr'] = $var['descr'];
$rrd_list[$i]['ds'] = $ds;
$rrd_list[$i]['colour'] = \LibreNMS\Config::get("graph_colours.$colours.$i");
$i++;
}
} else {
d_echo('RRD "' . $rrd_filename . '" not found');
}
require 'includes/html/graphs/generic_multi_line.inc.php';

View File

@@ -0,0 +1,30 @@
<?php
require 'includes/html/graphs/common.inc.php';
$colours = 'mixed';
$nototal = (($width < 224) ? 1 : 0);
$unit_text = 'Seconds';
$rrd_filename = rrd_name($device['hostname'], ['app', 'chronyd', $app['app_id'], $vars['source']]);
$array = [
'adjusted_offset' => ['descr' => 'Adjusted'],
'measured_offset' => ['descr' => 'Measured'],
'offset' => ['descr' => 'Estimated'],
'estimated_error' => ['descr' => 'Est. error'],
'stddev' => ['descr' => 'Std dev'],
];
$rrd_list = [];
if (rrdtool_check_rrd_exists($rrd_filename)) {
foreach ($array as $ds => $var) {
$rrd_list[$i]['filename'] = $rrd_filename;
$rrd_list[$i]['descr'] = $var['descr'];
$rrd_list[$i]['ds'] = $ds;
$rrd_list[$i]['colour'] = \LibreNMS\Config::get("graph_colours.$colours.$i");
$i++;
}
} else {
d_echo('RRD "' . $rrd_filename . '" not found');
}
require 'includes/html/graphs/generic_multi_line.inc.php';

View File

@@ -0,0 +1,26 @@
<?php
require 'includes/html/graphs/common.inc.php';
$colours = 'mixed';
$nototal = (($width < 224) ? 1 : 0);
$unit_text = 'Level';
$rrd_filename = rrd_name($device['hostname'], ['app', 'chronyd', $app['app_id']]);
$array = [
'stratum' => ['descr' => 'Stratum'],
];
$rrd_list = [];
if (rrdtool_check_rrd_exists($rrd_filename)) {
foreach ($array as $ds => $var) {
$rrd_list[$i]['filename'] = $rrd_filename;
$rrd_list[$i]['descr'] = $var['descr'];
$rrd_list[$i]['ds'] = $ds;
$rrd_list[$i]['colour'] = \LibreNMS\Config::get("graph_colours.$colours.$i");
$i++;
}
} else {
d_echo('RRD "' . $rrd_filename . '" not found');
}
require 'includes/html/graphs/generic_multi_line.inc.php';

View File

@@ -0,0 +1,29 @@
<?php
require 'includes/html/graphs/common.inc.php';
$colours = 'mixed';
$nototal = (($width < 224) ? 1 : 0);
$unit_text = 'Seconds';
$rrd_filename = rrd_name($device['hostname'], ['app', 'chronyd', $app['app_id']]);
$array = [
'system_time' => ['descr' => 'Clock lag'],
'last_offset' => ['descr' => 'Last offset'],
'rms_offset' => ['descr' => 'Avg offset'],
];
$i = 0;
if (rrdtool_check_rrd_exists($rrd_filename)) {
foreach ($array as $ds => $var) {
$rrd_list[$i]['filename'] = $rrd_filename;
$rrd_list[$i]['descr'] = $var['descr'];
$rrd_list[$i]['ds'] = $ds;
$rrd_list[$i]['colour'] = \LibreNMS\Config::get("graph_colours.$colours.$i");
$i++;
}
} else {
echo "file missing: $file";
}
require 'includes/html/graphs/generic_multi_line.inc.php';

View File

@@ -367,6 +367,11 @@ $graphs['docker'] = [
'mem_used',
'mem_perc',
];
$graphs['chronyd'] = [
'time',
'frequency',
'root',
];
echo '<div class="panel panel-default">';
echo '<div class="panel-heading">';

View File

@@ -0,0 +1,74 @@
<?php
$sources = get_chrony_sources($device['device_id']);
$link_array = [
'page' => 'device',
'device' => $device['device_id'],
'tab' => 'apps',
'app' => 'chronyd',
];
print_optionbar_start();
echo generate_link('Tracking', $link_array);
echo ' | Sources: ';
$sources_ctr = 0;
while (isset($sources[$sources_ctr])) {
$source = $sources[$sources_ctr];
$label = $source;
if ($vars['source'] == $source) {
$label = '>>' . $source . '<<';
}
$sources_ctr++;
$append = '';
if (isset($sources[$sources_ctr])) {
$append = ', ';
}
echo generate_link($label, $link_array, ['source'=>$source]) . $append;
}
print_optionbar_end();
if (! isset($vars['source'])) {
$graphs = [
'chronyd_time' => 'System time',
'chronyd_frequency' => 'System clock frequency',
'chronyd_root' => 'Root stratum',
'chronyd_stratum' => 'Stratum level',
];
} else {
$graphs = [
'chronyd_source_sampling' => 'Clock sampling offsets',
'chronyd_source_frequency' => 'Clock residual frequency',
'chronyd_source_polling' => 'Polling',
];
}
foreach ($graphs as $key => $text) {
$graph_type = $key;
$graph_array['height'] = '100';
$graph_array['width'] = '215';
$graph_array['to'] = \LibreNMS\Config::get('time.now');
$graph_array['id'] = $app['app_id'];
$graph_array['type'] = 'application_' . $key;
if (isset($vars['source'])) {
$graph_array['source'] = $vars['source'];
}
echo '<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">' . $text . '</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,139 @@
<?php
use LibreNMS\Exceptions\JsonAppException;
use LibreNMS\RRD\RrdDefinition;
$name = 'chronyd';
$app_id = $app['app_id'];
echo $name;
try {
$chronyd = json_app_get($device, $name, 1)['data'];
} 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;
}
$rrd_name = ['app', $name, $app_id];
$rrd_def = RrdDefinition::make()
->addDataset('stratum', 'GAUGE', 0, 15)
->addDataset('reference_time', 'DCOUNTER', 0.0, 10000000000) // good until year 2286
->addDataset('system_time', 'GAUGE', -10000.0, 10000.0)
->addDataset('last_offset', 'GAUGE', -1000.0, 1000.0)
->addDataset('rms_offset', 'GAUGE', -1000.0, 1000.0)
->addDataset('frequency', 'GAUGE', -1000.0, 1000.0)
->addDataset('residual_frequency', 'GAUGE', -1000.0, 1000.0)
->addDataset('skew', 'GAUGE', -1000.0, 1000.0)
->addDataset('root_delay', 'GAUGE', -1000.0, 1000.0)
->addDataset('root_dispersion', 'GAUGE', -1000.0, 1000.0)
->addDataset('update_interval', 'GAUGE', 0, 4096); // good for >1h
$fields = [
'stratum' => $chronyd['tracking']['stratum'],
'reference_time' => $chronyd['tracking']['reference_time'],
'system_time' => $chronyd['tracking']['system_time'],
'last_offset' => $chronyd['tracking']['last_offset'],
'rms_offset' => $chronyd['tracking']['rms_offset'],
'frequency' => $chronyd['tracking']['frequency'],
'residual_frequency' => $chronyd['tracking']['residual_frequency'],
'skew' => $chronyd['tracking']['skew'],
'root_delay' => $chronyd['tracking']['root_delay'],
'root_dispersion' => $chronyd['tracking']['root_dispersion'],
'update_interval' => $chronyd['tracking']['update_interval'],
];
// $tags = compact('name', 'app_id', 'rrd_name', 'rrd_def');
$tags = ['name' => $name, 'app_id' => $app_id, 'rrd_def' => $rrd_def, 'rrd_name' => $rrd_name];
data_update($device, 'app', $tags, $fields);
// process sources data
$sources = [];
$source_rrd_def = RrdDefinition::make()
->addDataset('stratum', 'GAUGE', 0, 15)
->addDataset('polling_rate', 'GAUGE', 0, 4096) // good for >1h
->addDataset('last_rx', 'GAUGE', 0, 777)
->addDataset('adjusted_offset', 'GAUGE', -10000, 10000)
->addDataset('measured_offset', 'GAUGE', -10000, 10000)
->addDataset('estimated_error', 'GAUGE', -10000, 10000)
->addDataset('number_samplepoints', 'GAUGE', 0, 4096)
->addDataset('number_runs', 'GAUGE', 0, 100)
->addDataset('span', 'GAUGE', 0, 10000)
->addDataset('frequency', 'GAUGE', -1000, 1000)
->addDataset('frequency_skew', 'GAUGE', -1000, 1000)
->addDataset('offset', 'GAUGE', -1000, 1000)
->addDataset('stddev', 'GAUGE', -1000, 1000);
$metrics = $chronyd;
unset($metrics['sources']);
foreach ($chronyd['sources'] as $source) {
$sources[] = $source['source_name'];
$rrd_name = ['app', $name, $app_id, $source['source_name']];
$fields = [
'stratum' => $source['stratum'],
'polling_rate' => $source['polling_rate'],
'last_rx' => $source['last_rx'],
'adjusted_offset' => $source['adjusted_offset'],
'measured_offset' => $source['measured_offset'],
'estimated_error' => $source['estimated_error'],
'number_samplepoints' => $source['number_samplepoints'],
'number_runs' => $source['number_runs'],
'span' => $source['span'],
'frequency' => $source['frequency'],
'frequency_skew' => $source['frequency_skew'],
'offset' => $source['offset'],
'stddev' => $source['stddev'],
];
$tags = ['name' => $name, 'app_id' => $app_id, 'rrd_def' => $source_rrd_def, 'rrd_name' => $rrd_name];
data_update($device, 'app', $tags, $fields);
// insert flattened source metrics into the metrics array
foreach ($fields as $field => $value) {
$metrics['source_' . $source['source_name'] . '_' . $field] = $value;
}
}
//
// component processing for Chrony
//
$device_id = $device['device_id'];
$options = [
'filter' => [
'device_id' => ['=', $device_id],
'type' => ['=', 'chronyd'],
],
];
$component = new LibreNMS\Component();
$components = $component->getComponents($device_id, $options);
// if no sources, delete chrony components
if (empty($sources)) {
if (isset($components[$device_id])) {
foreach ($components[$device_id] as $component_id => $_unused) {
$component->deleteComponent($component_id);
}
}
} else {
if (isset($components[$device_id])) {
$chrony_cpnt = $components[$device_id];
} else {
$chrony_cpnt = $component->createComponent($device_id, 'chronyd');
}
// Make sure we don't readd it, just in a different order.
sort($sources);
$id = $component->getFirstComponentID($chrony_cpnt);
$chrony_cpnt[$id]['label'] = 'Chrony';
$chrony_cpnt[$id]['sources'] = json_encode($sources);
$component->setComponentPrefs($device_id, $chrony_cpnt);
}
update_application($app, 'OK', $metrics);

View File

@@ -0,0 +1,110 @@
{
"applications": {
"discovery": {
"applications": [{
"app_type": "chronyd",
"app_state": "UNKNOWN",
"discovered": 1,
"app_state_prev": null,
"app_status": "",
"app_instance": ""
}],
"application_metrics": []
},
"poller": {
"applications": [{
"app_type": "chronyd",
"app_state": "OK",
"discovered": 1,
"app_state_prev": "UNKNOWN",
"app_status": "",
"app_instance": ""
}],
"application_metrics": [{
"metric": "tracking_frequency",
"value": -9.845,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_last_offset",
"value": 1.5e-8,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_leap_status",
"value": 0,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_reference_name",
"value": 50505300,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_reference_time",
"value": 1613135374.765365,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_reference_type",
"value": 0,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_residual_frequency",
"value": 0,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_rms_offset",
"value": 9.5e-8,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_root_delay",
"value": 1.0e-9,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_root_dispersion",
"value": 8.054e-6,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_skew",
"value": 0.006,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_stratum",
"value": 1,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_system_time",
"value": -1.6e-8,
"value_prev": null,
"app_type": "chronyd"
},
{
"metric": "tracking_update_interval",
"value": 8,
"value_prev": null,
"app_type": "chronyd"
}
]
}
}
}

View File

@@ -0,0 +1,8 @@
1.3.6.1.2.1.1.1.0|4|Linux time.theswissbay.ch 5.4.83-v7+ #1379 SMP Mon Dec 14 13:08:57 GMT 2020 armv7l
1.3.6.1.2.1.1.2.0|6|1.3.6.1.4.1.8072.3.2.10
1.3.6.1.2.1.1.3.0|67|45899334
1.3.6.1.2.1.1.4.0|4|<private>
1.3.6.1.2.1.1.5.0|4|<private>
1.3.6.1.4.1.8072.1.3.2.2.1.21.7.99.104.114.111.110.121.100|2|1
1.3.6.1.4.1.8072.1.3.2.3.1.2.7.99.104.114.111.110.121.100|4|{"data": {"tracking": {"reference_name": "50505300", "reference_type": "PPS", "stratum": "1", "reference_time": "1613135374.765364947", "system_time": "-0.000000016", "last_offset": "0.000000015", "rms_offset": "0.000000095", "frequency": "-9.845", "residual_frequency": "0.000", "skew": "0.006", "root_delay": "0.000000001", "root_dispersion": "0.000008054", "update_interval": "8.0", "leap_status": "Normal"}, "sources": [{"source_mode": "#", "source_state": "*", "source_name": "PPS", "stratum": "0", "polling_rate": "3", "reachability": "377", "last_rx": "7", "adjusted_offset": "0.000000045", "measured_offset": "0.000000058", "estimated_error": "0.000000232", "number_samplepoints": "19", "number_runs": "10", "span": "144", "frequency": "0.000", "frequency_skew": "0.006", "offset": "0.000000000", "stddev": "0.000000271"}, {"source_mode": "#", "source_state": "-", "source_name": "GPS", "stratum": "0", "polling_rate": "3", "reachability": "377", "last_rx": "7", "adjusted_offset": "-0.012771881", "measured_offset": "-0.012771881", "estimated_error": "0.003427120", "number_samplepoints": "15", "number_runs": "9", "span": "113", "frequency": "-81.117", "frequency_skew": "218.921", "offset": "-0.005838305", "stddev": "0.006443196"}]}, "error": false, "errorString": "", "version": 1}
1.3.6.1.6.3.10.2.1.3.0|2|458993