. * * @link https://www.librenms.org * * @copyright 2024 Tony Murray * @copyright 2024 Ruben van Komen * @author Ruben van Komen */ namespace LibreNMS\Data\Store; use App\Facades\DeviceCache; use App\Polling\Measure\Measurement; use InfluxDB2\Client; use InfluxDB2\Model\WritePrecision; use InfluxDB2\Point; use LibreNMS\Config; use Log; class InfluxDBv2 extends BaseDatastore { public function getName() { return 'InfluxDBv2'; } public static function isEnabled() { return Config::get('influxdbv2.enable', false); } /** * Datastore-independent function which should be used for all polled metrics. * * RRD Tags: * rrd_def RrdDefinition * rrd_name array|string: the rrd filename, will be processed with rrd_name() * rrd_oldname array|string: old rrd filename to rename, will be processed with rrd_name() * rrd_step int: rrd step, defaults to 300 * * @param array $device * @param string $measurement Name of this measurement * @param array $tags tags for the data (or to control rrdtool) * @param array|mixed $fields The data to update in an associative array, the order must be consistent with rrd_def, * single values are allowed and will be paired with $measurement */ public function put($device, $measurement, $tags, $fields) { $device_data = DeviceCache::get($device['device_id']); $excluded_groups = Config::get('influxdbv2.groups-exclude'); if (! empty($excluded_groups)) { $device_groups = $device_data->groups; foreach ($device_groups as $group) { // The group name will always be parsed as lowercase, even when uppercase in the GUI. if (in_array(strtoupper($group->name), array_map('strtoupper', $excluded_groups))) { Log::warning('Skipped parsing to InfluxDBv2, device is in group: ' . $group->name); return; } } } $stat = Measurement::start('write'); $tmp_fields = []; $tmp_tags['hostname'] = $device['hostname']; foreach ($tags as $k => $v) { if (empty($v)) { $v = '_blank_'; } $tmp_tags[$k] = $v; } foreach ($fields as $k => $v) { if ($k == 'time') { $k = 'rtime'; } if (($value = $this->forceType($v)) !== null) { $tmp_fields[$k] = $value; } } if (empty($tmp_fields)) { Log::warning('All fields empty, skipping update', ['orig_fields' => $fields]); return; } if (Config::get('influxdbv2.debug') === true) { Log::debug('InfluxDB data: ', [ 'measurement' => $measurement, 'tags' => $tmp_tags, 'fields' => $tmp_fields, ]); } // Get a WriteApi instance from the client $client = self::createFromConfig(); $writeApi = $client->createWriteApi(); try { // Construct data points using the InfluxDB2\Point class $point = Point::measurement($measurement) ->addTag('hostname', $device['hostname']) ->time(microtime(true)); // Assuming you want to use the current time // Write the data points to the database using the WriteApi instance foreach ($tmp_fields as $field => $value) { $point->addField($field, $value); } // Adding tags from $tmpTags array foreach ($tmp_tags as $tag => $value) { $point->addTag($tag, $value); } $writeApi->write($point); $this->recordStatistic($stat->end()); } catch (\InfluxDB2\ApiException $e) { print_r($e); // Handle exceptions } finally { // Close the WriteApi to free resources $writeApi->close(); } } public static function createFromConfig() { $host = Config::get('influxdbv2.host', 'localhost'); $transport = Config::get('influxdbv2.transport', 'http'); $port = Config::get('influxdbv2.port', 8086); $bucket = Config::get('influxdbv2.bucket', 'librenms'); $organization = Config::get('influxdbv2.organization', ''); $allow_redirects = Config::get('influxdbv2.allow_redirects', true); $token = Config::get('influxdbv2.token', ''); $debug = Config::get('influxdbv2.debug', false); $client = new Client([ 'url' => $transport . '://' . $host . ':' . $port, 'token' => $token, 'bucket' => $bucket, 'org' => $organization, 'precision' => WritePrecision::S, 'allow_redirects' => $allow_redirects, 'debug' => $debug, ]); return $client; } private function forceType($data) { /* * It is not trivial to detect if something is a float or an integer, and * therefore may cause breakages on inserts. * Just setting every number to a float gets around this, but may introduce * inefficiencies. */ if (is_numeric($data)) { return floatval($data); } return $data === 'U' ? null : $data; } /** * Checks if the datastore wants rrdtags to be sent when issuing put() * * @return bool */ public function wantsRrdTags() { return false; } }