Add live MTR to LookingGlass
This commit is contained in:
parent
c18884639e
commit
f6a470e8f8
223
LookingGlass.php
223
LookingGlass.php
@ -180,7 +180,7 @@ class LookingGlass
|
||||
*/
|
||||
public static function mtr(string $host): bool
|
||||
{
|
||||
return self::procExecute('mtr -4 --report --report-wide', $host);
|
||||
return self::procExecute('mtr --raw -n -4 -c ' . self::MTR_COUNT, $host);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,7 +191,7 @@ class LookingGlass
|
||||
*/
|
||||
public static function mtr6(string $host): bool
|
||||
{
|
||||
return self::procExecute('mtr -6 --report --report-wide', $host);
|
||||
return self::procExecute('mtr --raw -n -6 -c ' . self::MTR_COUNT, $host);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -232,11 +232,11 @@ class LookingGlass
|
||||
private static function procExecute(string $cmd, string $host, int $failCount = 2): bool
|
||||
{
|
||||
// define output pipes
|
||||
$spec = array(
|
||||
0 => array("pipe", "r"),
|
||||
1 => array("pipe", "w"),
|
||||
2 => array("pipe", "w")
|
||||
);
|
||||
$spec = [
|
||||
0 => ['pipe', 'r'],
|
||||
1 => ['pipe', 'w'],
|
||||
2 => ['pipe', 'w']
|
||||
];
|
||||
|
||||
// sanitize + remove single quotes
|
||||
$host = str_replace('\'', '', filter_var($host, FILTER_SANITIZE_URL));
|
||||
@ -251,16 +251,17 @@ class LookingGlass
|
||||
// check for mtr/traceroute
|
||||
if (strpos($cmd, 'mtr') !== false) {
|
||||
$type = 'mtr';
|
||||
$parser = new Parser();
|
||||
} elseif (strpos($cmd, 'traceroute') !== false) {
|
||||
$type = 'traceroute';
|
||||
} else {
|
||||
$type = '';
|
||||
}
|
||||
|
||||
$fail = 0;
|
||||
$match = 0;
|
||||
$fail = 0;
|
||||
$match = 0;
|
||||
$traceCount = 0;
|
||||
$lastFail = 'start';
|
||||
$lastFail = 'start';
|
||||
// iterate stdout
|
||||
while (($str = fgets($pipes[1], 4096)) != null) {
|
||||
// check for output buffer
|
||||
@ -273,12 +274,14 @@ class LookingGlass
|
||||
|
||||
// correct output for mtr
|
||||
if ($type === 'mtr') {
|
||||
if ($match < 10 && preg_match('/^[0-9]\. /', $str, $string)) {
|
||||
$str = preg_replace('/^[0-9]\. /', ' ' . $string[0], $str);
|
||||
$match++;
|
||||
} else {
|
||||
$str = preg_replace('/^[0-9]{2}\. /', ' ' . substr($str, 0, 4), $str);
|
||||
}
|
||||
// correct output for mtr
|
||||
$parser->update($str);
|
||||
echo '---' . PHP_EOL . $parser->__toString() . PHP_EOL . str_pad('', 4096) . PHP_EOL;
|
||||
|
||||
// flush output buffering
|
||||
@ob_flush();
|
||||
flush();
|
||||
continue;
|
||||
}
|
||||
// correct output for traceroute
|
||||
elseif ($type === 'traceroute') {
|
||||
@ -319,7 +322,7 @@ class LookingGlass
|
||||
}
|
||||
|
||||
$status = proc_get_status($process);
|
||||
if ($status['running'] == true) {
|
||||
if ($status['running']) {
|
||||
// close pipes that are still open
|
||||
foreach ($pipes as $pipe) {
|
||||
fclose($pipe);
|
||||
@ -341,3 +344,189 @@ class LookingGlass
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Hop
|
||||
{
|
||||
/** @var int */
|
||||
public $idx;
|
||||
/** @var string */
|
||||
public $asn = '';
|
||||
/** @var float */
|
||||
public $avg = 0.0;
|
||||
/** @var int */
|
||||
public $loss = 0;
|
||||
/** @var float */
|
||||
public $stdev = 0.0;
|
||||
/** @var int */
|
||||
public $sent = 0;
|
||||
/** @var int */
|
||||
public $recieved = 0;
|
||||
/** @var float */
|
||||
public $last = 0.0;
|
||||
/** @var float */
|
||||
public $best = 0.0;
|
||||
/** @var float */
|
||||
public $worst = 0.0;
|
||||
|
||||
/** @var string[] */
|
||||
public $ips = [];
|
||||
/** @var string[] */
|
||||
public $hosts = [];
|
||||
/** @var float[] */
|
||||
public $timings = [];
|
||||
|
||||
}
|
||||
|
||||
class RawHop
|
||||
{
|
||||
/** @var string */
|
||||
public $dataType;
|
||||
/** @var int */
|
||||
public $idx;
|
||||
/** @var string */
|
||||
public $value;
|
||||
}
|
||||
|
||||
class Parser
|
||||
{
|
||||
/** @var Hop[] */
|
||||
protected $hopsCollection = [];
|
||||
/** @var int */
|
||||
private $hopCount = 0;
|
||||
/** @var int */
|
||||
private $outputWidth = 38;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
putenv('RES_OPTIONS=retrans:1 retry:1 timeout:1 attempts:1');
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$str = '';
|
||||
foreach ($this->hopsCollection as $index => $hop) {
|
||||
$host = $hop->hosts[0] ?? $hop->ips[0] ?? '???';
|
||||
|
||||
if (strlen($host) > $this->outputWidth) {
|
||||
$this->outputWidth = strlen($host);
|
||||
}
|
||||
|
||||
$hop->recieved = count($hop->timings);
|
||||
if (count($hop->timings)) {
|
||||
$hop->last = $hop->timings[count($hop->timings) - 1];
|
||||
$hop->best = $hop->timings[0];
|
||||
$hop->worst = $hop->timings[0];
|
||||
$hop->avg = array_sum($hop->timings) / count($hop->timings);
|
||||
}
|
||||
|
||||
if (count($hop->timings) > 1) {
|
||||
$hop->stdev = $this->stDev($hop->timings);
|
||||
}
|
||||
|
||||
foreach ($hop->timings as $time) {
|
||||
|
||||
if ($hop->best > $time) {
|
||||
$hop->best = $time;
|
||||
}
|
||||
|
||||
if ($hop->worst < $time) {
|
||||
$hop->worst = $time;
|
||||
}
|
||||
}
|
||||
|
||||
$hop->loss = $hop->sent ? (100 * ($hop->sent - $hop->recieved)) / $hop->sent : 100;
|
||||
|
||||
$str = sprintf(
|
||||
"%s%2d.|-- %s%3d.0%% %3d %5.1f %5.1f %5.1f %5.1f %5.1f\n",
|
||||
$str,
|
||||
$index,
|
||||
str_pad($host, $this->outputWidth + 3, ' ', STR_PAD_RIGHT),
|
||||
$hop->loss,
|
||||
$hop->sent,
|
||||
$hop->last,
|
||||
$hop->avg,
|
||||
$hop->best,
|
||||
$hop->worst,
|
||||
$hop->stdev
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(" Host%sLoss%% Snt Last Avg Best Wrst StDev\n%s", str_pad('', $this->outputWidth + 7, ' ', STR_PAD_RIGHT), $str);
|
||||
}
|
||||
|
||||
private function stDev(array $array): float
|
||||
{
|
||||
$sdSquare = function ($x, $mean) {
|
||||
return pow($x - $mean, 2);
|
||||
};
|
||||
|
||||
// square root of sum of squares devided by N-1
|
||||
return sqrt(array_sum(array_map($sdSquare, $array, array_fill(0, count($array), (array_sum($array) / count($array))))) / (count($array) - 1));
|
||||
}
|
||||
|
||||
public function update($rawMtrInput)
|
||||
{
|
||||
//Store each line of output in rawhop structure
|
||||
$things = explode(' ', $rawMtrInput);
|
||||
|
||||
if (count($things) !== 3 && (count($things) !== 4 && $things[0] === 'p')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rawHop = new RawHop();
|
||||
$rawHop->dataType = $things[0];
|
||||
$rawHop->idx = (int)$things[1];
|
||||
$rawHop->value = $things[2];
|
||||
|
||||
if ($this->hopCount < $rawHop->idx + 1) {
|
||||
$this->hopCount = $rawHop->idx + 1;
|
||||
}
|
||||
|
||||
if (!isset($this->hopsCollection[$rawHop->idx])) {
|
||||
$this->hopsCollection[$rawHop->idx] = new Hop();
|
||||
}
|
||||
|
||||
$hop = $this->hopsCollection[$rawHop->idx];
|
||||
$hop->idx = $rawHop->idx;
|
||||
switch ($rawHop->dataType) {
|
||||
case 'h':
|
||||
$hop->ips[] = $rawHop->value;
|
||||
$hop->hosts[] = gethostbyaddr($rawHop->value) ? : null;
|
||||
break;
|
||||
case 'd':
|
||||
//Not entirely sure if multiple IPs. Better use -n in mtr and resolve later in summarize.
|
||||
//out.Hops[data.idx].Host = append(out.Hops[data.idx].Host, data.value)
|
||||
break;
|
||||
case 'p':
|
||||
$hop->sent++;
|
||||
$hop->timings[] = (float)$rawHop->value / 1000;
|
||||
break;
|
||||
}
|
||||
|
||||
$this->hopsCollection[$rawHop->idx] = $hop;
|
||||
|
||||
$this->filterLastDupeHop();
|
||||
}
|
||||
|
||||
// Function to calculate standard deviation (uses sd_square)
|
||||
|
||||
private function filterLastDupeHop()
|
||||
{
|
||||
// filter dupe last hop
|
||||
$finalIdx = 0;
|
||||
$previousIp = '';
|
||||
|
||||
foreach ($this->hopsCollection as $key => $hop) {
|
||||
if (count($hop->ips) && $hop->ips[0] !== $previousIp) {
|
||||
$previousIp = $hop->ips[0];
|
||||
$finalIdx = $key + 1;
|
||||
}
|
||||
}
|
||||
|
||||
unset($this->hopsCollection[$finalIdx]);
|
||||
|
||||
usort($this->hopsCollection, function ($a, $b) {
|
||||
return $a->idx - $b->idx;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user