<?php namespace App\Sensor; use \App\AbstractSensor; /** * Description of MemInfo * * @author tibo */ class Ifconfig extends AbstractSensor { public function report() { $interfaces = []; $record = $this->getLastRecord("ifconfig"); if ($record !== null) { $interfaces = $this->parseIfconfigRecord($record); } return view("agent.ifconfig", [ "server" => $this->getServer(), "interfaces" => $interfaces]); } public function points() { // Get records in time ascending order $records = $this->getLastRecords("ifconfig", 289); // Compute the time ordered list of arrays of interfaces $interfaces = []; foreach ($records as $record) { $interfaces[] = $this->parseIfconfigRecord($record); } // Foreach interface, compute the array of points $dataset = []; $current_value = []; foreach ($interfaces[0] as $interface) { $iname = $interface->name; $dataset[$iname . "/TX"] = [ "name" => $iname . "/TX", "points" => [] ]; $dataset[$iname . "/RX"] = [ "name" => $iname . "/RX", "points" => [] ]; $current_value[$interface->name] = $interface; } for ($i = 1; $i < count($interfaces); $i++) { foreach ($interfaces[$i] as $interface) { $iname = $interface->name; $previous_value = $current_value[$iname]; $delta_time = $interface->time - $previous_value->time; // RX $delta = $interface->rx - $previous_value->rx; if ($delta < 0) { // Can happen after a reboot... $delta = 0; } $dataset[$iname . "/RX"]["points"][] = new Point( $interface->time * 1000, round(8 / 1024 * $delta / $delta_time) ); // TX $delta = $interface->tx - $previous_value->tx; if ($delta < 0) { // Can happen after a reboot... $delta = 0; } $dataset[$iname . "/TX"]["points"][] = new Point( $interface->time * 1000, round(8 / 1024 * $delta / $delta_time) ); // Keep current value for next record $current_value[$iname] = $interface; } } return array_values($dataset); } public function status() { return self::STATUS_OK; } const IFNAME = '/^(?|(\S+)\s+Link encap:|(\S+): flags)/m'; const IPV4 = '/^\s+inet (?>addr:)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/m'; const RXTX = '/^\s+RX bytes:(\d+) .*TX bytes:(\d+)/m'; const RX = '/^\s+RX packets (?>\d+) bytes (\d+)/m'; const TX = '/^\s+TX packets (?>\d+) bytes (\d+)/m'; public function parseIfconfigRecord($record) { $interfaces = $this->parseIfconfig($record->ifconfig); foreach ($interfaces as $interface) { $interface->time = $record->time; } return $interfaces; } /** * Parse the result of the ifconfig command, skipping every virtual * interfaces (docker, br, lo) * @param type $string * @return \App\Sensor\NetworkInterface[] */ public function parseIfconfig($string) { $allowed_prefixes = ["en", "eth", "wl", "venet"]; if ($string == null) { return []; } $interfaces = []; $lines = explode("\n", $string); $if = null; foreach ($lines as $line) { $name = $this->pregMatchOne(self::IFNAME, $line); if ($name !== false) { // Starting the section of a new interface $if = new NetworkInterface(); $interfaces[] = $if; $if->name = $name; continue; } $ip = $this->pregMatchOne(self::IPV4, $line); if ($ip !== false) { $if->address = $ip; continue; } $matches = []; if (preg_match(self::RXTX, $line, $matches) === 1) { $if->rx = $matches[1]; $if->tx = $matches[2]; continue; } $rx = $this->pregMatchOne(self::RX, $line); if ($rx !== false) { $if->rx = $rx; } $tx = $this->pregMatchOne(self::TX, $line); if ($tx !== false) { $if->tx = $tx; } } // filter out uninteresting interfaces $filtered = []; foreach ($interfaces as $interface) { if (\starts_with($interface->name, $allowed_prefixes)) { $filtered[] = $interface; } } return $filtered; } public function pregMatchOne(string $pattern, string $string, int $match_group = 1) { $matches = []; if (preg_match($pattern, $string, $matches) === 1) { return $matches[$match_group]; } return false; } }