Merge pull request #5 from librenms/master

Merge librenms master into this fork
This commit is contained in:
Oldemar Gonçalves 2024-09-18 10:04:40 +01:00 committed by GitHub
commit d5c94b43e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 313 additions and 241 deletions

View File

@ -478,9 +478,6 @@ class Config
} }
self::populateTime(); self::populateTime();
// populate legacy DB credentials, just in case something external uses them. Maybe remove this later
self::populateLegacyDbCredentials();
} }
/** /**
@ -561,18 +558,6 @@ class Config
self::set('time.twoyear', $now - 63072000); // time() - (2 * 365 * 24 * 60 * 60); self::set('time.twoyear', $now - 63072000); // time() - (2 * 365 * 24 * 60 * 60);
} }
public static function populateLegacyDbCredentials()
{
$db = config('database.default');
self::set('db_host', config("database.connections.$db.host", 'localhost'));
self::set('db_name', config("database.connections.$db.database", 'librenms'));
self::set('db_user', config("database.connections.$db.username", 'librenms'));
self::set('db_pass', config("database.connections.$db.password"));
self::set('db_port', config("database.connections.$db.port", 3306));
self::set('db_socket', config("database.connections.$db.unix_socket"));
}
/** /**
* Check if the config has been loaded yet * Check if the config has been loaded yet
* *

View File

@ -26,7 +26,6 @@
namespace LibreNMS\DB; namespace LibreNMS\DB;
use App\Models\Device; use App\Models\Device;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use LibreNMS\Interfaces\Models\Keyable; use LibreNMS\Interfaces\Models\Keyable;
@ -37,15 +36,15 @@ trait SyncsModels
* Sync several models for a device's relationship * Sync several models for a device's relationship
* Model must implement \LibreNMS\Interfaces\Models\Keyable interface * Model must implement \LibreNMS\Interfaces\Models\Keyable interface
* *
* @param \App\Models\Device $device * @param \Illuminate\Database\Eloquent\Model $parentModel
* @param string $relationship * @param string $relationship
* @param \Illuminate\Support\Collection<Keyable> $models \LibreNMS\Interfaces\Models\Keyable * @param \Illuminate\Support\Collection<Keyable> $models \LibreNMS\Interfaces\Models\Keyable
* @return \Illuminate\Support\Collection * @return \Illuminate\Support\Collection
*/ */
protected function syncModels($device, $relationship, $models, $existing = null): Collection protected function syncModels($parentModel, $relationship, $models, $existing = null): Collection
{ {
$models = $models->keyBy->getCompositeKey(); $models = $models->keyBy->getCompositeKey();
$existing = ($existing ?? $device->$relationship)->groupBy->getCompositeKey(); $existing = ($existing ?? $parentModel->$relationship)->groupBy->getCompositeKey();
foreach ($existing as $exist_key => $existing_rows) { foreach ($existing as $exist_key => $existing_rows) {
if ($models->offsetExists($exist_key)) { if ($models->offsetExists($exist_key)) {
@ -67,12 +66,12 @@ trait SyncsModels
} }
$new = $models->diffKeys($existing); $new = $models->diffKeys($existing);
if (is_a($device->$relationship(), HasManyThrough::class)) { if (is_a($parentModel->$relationship(), HasManyThrough::class)) {
// if this is a distant relation, the models need the intermediate relationship set // if this is a distant relation, the models need the intermediate relationship set
// just save assuming things are correct // just save assuming things are correct
$new->each->save(); $new->each->save();
} else { } else {
$device->$relationship()->saveMany($new); $parentModel->$relationship()->saveMany($new);
} }
return $existing->map->first()->merge($new); return $existing->map->first()->merge($new);

View File

@ -167,7 +167,7 @@ def get_config_data(base_dir):
) )
logger.debug("Traceback:", exc_info=True) logger.debug("Traceback:", exc_info=True)
config_cmd = ["/usr/bin/env", "php", "%s/config_to_json.php" % base_dir] config_cmd = ["/usr/bin/env", "php", "%s/lnms" % base_dir, "config:get", "--dump"]
try: try:
exit_code, output = command_runner(config_cmd, timeout=300, stderr=False) exit_code, output = command_runner(config_cmd, timeout=300, stderr=False)
if exit_code != 0: if exit_code != 0:

View File

@ -1,3 +1,6 @@
import os
class DBConfig: class DBConfig:
""" """
Bare minimal config class for LibreNMS.DB class usage Bare minimal config class for LibreNMS.DB class usage
@ -14,10 +17,17 @@ class DBConfig:
db_ssl_ca = "/etc/ssl/certs/ca-certificates.crt" db_ssl_ca = "/etc/ssl/certs/ca-certificates.crt"
def populate(self, _config): def populate(self, _config):
for key, val in _config.items(): self.db_host = os.getenv("DB_HOST", _config.get("db_host", self.db_host))
if key == "db_port": self.db_name = os.getenv("DB_DATABASE", _config.get("db_name", self.db_name))
# Special case: port number self.db_pass = os.getenv("DB_PASSWORD", _config.get("db_pass", self.db_pass))
self.db_port = int(val) self.db_port = int(os.getenv("DB_PORT", _config.get("db_port", self.db_port)))
elif key.startswith("db_"): self.db_socket = os.getenv(
# Prevent prototype pollution by enforcing prefix "DB_SOCKET", _config.get("db_socket", self.db_socket)
setattr(DBConfig, key, val) )
self.db_user = os.getenv("DB_USERNAME", _config.get("db_user", self.db_user))
self.db_sslmode = os.getenv(
"DB_SSLMODE", _config.get("db_sslmode", self.db_sslmode)
)
self.db_ssl_ca = os.getenv(
"MYSQL_ATTR_SSL_CA", _config.get("db_ssl_ca", self.db_ssl_ca)
)

View File

@ -26,8 +26,16 @@
namespace App\Discovery; namespace App\Discovery;
use App\Models\Device; use App\Models\Device;
use App\Models\Eventlog;
use App\Models\SensorToStateIndex;
use App\Models\StateIndex;
use App\Models\StateTranslation;
use Illuminate\Database\UniqueConstraintViolationException;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use LibreNMS\Config;
use LibreNMS\DB\SyncsModels; use LibreNMS\DB\SyncsModels;
use LibreNMS\Enum\Severity;
class Sensor class Sensor
{ {
@ -38,6 +46,8 @@ class Sensor
private array $discovered = []; private array $discovered = [];
private string $relationship = 'sensors'; private string $relationship = 'sensors';
private Device $device; private Device $device;
/** @var array<string, Collection<StateTranslation>> */
private array $states = [];
public function __construct(Device $device) public function __construct(Device $device)
{ {
@ -47,10 +57,31 @@ class Sensor
public function discover(\App\Models\Sensor $sensor): static public function discover(\App\Models\Sensor $sensor): static
{ {
if ($this->canSkip($sensor)) {
Log::info('~');
Log::debug("Skipped Sensor: $sensor");
return $this;
}
$sensor->device_id ??= \DeviceCache::getPrimary()->device_id; $sensor->device_id ??= \DeviceCache::getPrimary()->device_id;
$this->models->push($sensor); $this->models->push($sensor);
$this->discovered[$sensor->syncGroup()] = false; $this->discovered[$sensor->syncGroup()] = false;
Log::debug("Discovered Sensor: $sensor");
Log::info("$sensor->sensor_descr: Cur $sensor->sensor_current, Low: $sensor->sensor_limit_low, Low Warn: $sensor->sensor_limit_low_warn, Warn: $sensor->sensor_limit_warn, High: $sensor->sensor_limit");
return $this;
}
/**
* @param string $stateName
* @param StateTranslation[]|Collection<StateTranslation> $states
* @return $this
*/
public function withStateTranslations(string $stateName, array|Collection $states): static
{
$this->states[$stateName] = new Collection($states);
return $this; return $this;
} }
@ -67,6 +98,8 @@ class Sensor
$synced = $this->syncModelsByGroup($this->device, 'sensors', $this->getModels(), $params); $synced = $this->syncModelsByGroup($this->device, 'sensors', $this->getModels(), $params);
$this->discovered[$type] = true; $this->discovered[$type] = true;
$this->syncStates($synced);
return $synced; return $synced;
} }
@ -77,4 +110,80 @@ class Sensor
{ {
return $this->models; return $this->models;
} }
public function canSkip(\App\Models\Sensor $sensor): bool
{
if (! empty($sensor->sensor_class) && (Config::getOsSetting($this->device->os, "disabled_sensors.$sensor->sensor_class") || Config::get("disabled_sensors.$sensor->sensor_class"))) {
return true;
}
foreach (Config::getCombined($this->device->os, 'disabled_sensors_regex') as $skipRegex) {
if (! empty($sensor->sensor_descr) && preg_match($skipRegex, $sensor->sensor_descr)) {
return true;
}
}
foreach (Config::getCombined($this->device->os, "disabled_sensors_regex.$sensor->sensor_class") as $skipRegex) {
if (! empty($sensor->sensor_descr) && preg_match($skipRegex, $sensor->sensor_descr)) {
return true;
}
}
return false;
}
private function syncStates(Collection $sensors): void
{
$stateSensors = $sensors->where('sensor_class', 'state');
if ($stateSensors->isEmpty()) {
return;
}
$usedStates = $stateSensors->pluck('sensor_type');
$existingStateIndexes = StateIndex::whereIn('state_name', $usedStates)->get()->keyBy('state_name');
foreach ($usedStates as $stateName) {
// make sure the state translations were given for this state name
if (! isset($this->states[$stateName])) {
Log::error("Non existent state name ($stateName) set by sensor: " . $stateSensors->where('sensor_type', $stateName)->first()?->sensor_descr);
continue;
}
$stateIndex = $existingStateIndexes->get($stateName);
// create new state indexes
if ($stateIndex == null) {
try {
$stateIndex = StateIndex::create(['state_name' => $stateName]);
$existingStateIndexes->put($stateName, $stateIndex);
} catch (UniqueConstraintViolationException) {
Eventlog::log("Duplicate state name $stateName (with case mismatch)", $this->device, 'sensor', Severity::Error, $stateSensors->where('sensor_type', $stateName)->first()?->sensor_id);
continue;
}
}
// set state_index_id
$stateTranslations = $this->states[$stateName];
foreach ($stateTranslations as $translation) {
$translation->state_index_id = $stateIndex->state_index_id;
}
// sync the translations to make sure they are up to date
$this->syncModels($stateIndex, 'translations', $stateTranslations);
}
// update sensor to state indexes
foreach ($stateSensors as $stateSensor) {
$state_index_id = $existingStateIndexes->get($stateSensor->sensor_type)?->state_index_id;
// only map if sensor gave a valid state name
if ($state_index_id) {
SensorToStateIndex::updateOrCreate(
['sensor_id' => $stateSensor->sensor_id],
['state_index_id' => $state_index_id],
);
}
}
}
} }

View File

@ -4,6 +4,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphMany;
use LibreNMS\Interfaces\Models\Keyable; use LibreNMS\Interfaces\Models\Keyable;
@ -99,7 +100,7 @@ class Sensor extends DeviceRelatedModel implements Keyable
public function guessLimits(): void public function guessLimits(): void
{ {
$this->sensor_limit = match ($this->sensor_class) { $this->sensor_limit_low = match ($this->sensor_class) {
'temperature' => $this->sensor_current - 10, 'temperature' => $this->sensor_current - 10,
'voltage' => $this->sensor_current * 0.85, 'voltage' => $this->sensor_current * 0.85,
'humidity' => 30, 'humidity' => 30,
@ -110,7 +111,7 @@ class Sensor extends DeviceRelatedModel implements Keyable
default => null, default => null,
}; };
$this->sensor_limit_low = match ($this->sensor_class) { $this->sensor_limit = match ($this->sensor_class) {
'temperature' => $this->sensor_current + 20, 'temperature' => $this->sensor_current + 20,
'voltage' => $this->sensor_current * 1.15, 'voltage' => $this->sensor_current * 1.15,
'humidity' => 70, 'humidity' => 70,
@ -129,9 +130,14 @@ class Sensor extends DeviceRelatedModel implements Keyable
return $this->morphMany(Eventlog::class, 'events', 'type', 'reference'); return $this->morphMany(Eventlog::class, 'events', 'type', 'reference');
} }
public function stateIndex(): HasOneThrough
{
return $this->hasOneThrough(StateIndex::class, SensorToStateIndex::class, 'sensor_id', 'state_index_id', 'sensor_id', 'state_index_id');
}
public function translations(): BelongsToMany public function translations(): BelongsToMany
{ {
return $this->belongsToMany(StateTranslation::class, 'sensors_to_state_indexes', 'sensor_id', 'state_index_id'); return $this->belongsToMany(StateTranslation::class, 'sensors_to_state_indexes', 'sensor_id', 'state_index_id', 'sensor_id', 'state_index_id');
} }
public function getCompositeKey(): string public function getCompositeKey(): string
@ -143,4 +149,23 @@ class Sensor extends DeviceRelatedModel implements Keyable
{ {
return "$this->sensor_class-$this->poller_type"; return "$this->sensor_class-$this->poller_type";
} }
public function __toString()
{
$data = $this->only([
'sensor_oid',
'sensor_index',
'sensor_type',
'sensor_descr',
'poller_type',
'sensor_divisor',
'sensor_multiplier',
'entPhysicalIndex',
'sensor_current',
]);
$data[] = "(limits: LL: $this->sensor_limit_low, LW: $this->sensor_limit_low_warn, W: $this->sensor_limit_warn, H: $this->sensor_limit)";
$data[] = "rrd_type = $this->rrd_type";
return implode(', ', $data);
}
} }

View File

@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
class SensorToStateIndex extends Model
{
protected $table = 'sensors_to_state_indexes';
protected $primaryKey = 'sensors_to_state_translations_id';
public $timestamps = false;
protected $fillable = ['sensor_id', 'state_index_id'];
public function sensor(): HasOne
{
return $this->hasOne(Sensor::class, 'sensor_id', 'sensor_id');
}
public function stateIndex(): HasOne
{
return $this->hasOne(StateIndex::class, 'state_index_id', 'state_index_id');
}
}

28
app/Models/StateIndex.php Normal file
View File

@ -0,0 +1,28 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
class StateIndex extends Model
{
use HasFactory;
public $timestamps = false;
protected $table = 'state_indexes';
protected $fillable = ['state_name'];
protected $primaryKey = 'state_index_id';
public function sensors(): HasManyThrough
{
return $this->hasManyThrough(Sensor::class, SensorToStateIndex::class, 'state_index_id', 'sensor_id', 'state_index_id', 'sensor_id');
}
public function translations(): HasMany
{
return $this->hasMany(StateTranslation::class, 'state_index_id', 'state_index_id');
}
}

View File

@ -26,9 +26,28 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use LibreNMS\Interfaces\Models\Keyable;
class StateTranslation extends Model class StateTranslation extends Model implements Keyable
{ {
public $timestamps = false; const CREATED_AT = null;
protected $primaryKey = 'state_index_id'; const UPDATED_AT = 'state_lastupdated';
protected $primaryKey = 'state_translation_id';
protected $fillable = [
'state_descr',
'state_draw_graph',
'state_value',
'state_generic_value',
];
public function stateIndex(): BelongsTo
{
return $this->belongsTo(StateIndex::class, 'state_index_id', 'state_index_id');
}
public function getCompositeKey()
{
return $this->state_value;
}
} }

View File

@ -5,6 +5,7 @@ namespace App\Observers;
use App\Models\Eventlog; use App\Models\Eventlog;
use App\Models\Sensor; use App\Models\Sensor;
use Illuminate\Foundation\Application; use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Log;
use LibreNMS\Enum\Severity; use LibreNMS\Enum\Severity;
class SensorObserver class SensorObserver
@ -20,6 +21,8 @@ class SensorObserver
{ {
// fix inverted limits // fix inverted limits
if ($sensor->sensor_limit !== null && $sensor->sensor_limit_low !== null && $sensor->sensor_limit_low > $sensor->sensor_limit) { if ($sensor->sensor_limit !== null && $sensor->sensor_limit_low !== null && $sensor->sensor_limit_low > $sensor->sensor_limit) {
Log::error('Fixing swapped sensor limits');
// Fix high/low thresholds (i.e. on negative numbers) // Fix high/low thresholds (i.e. on negative numbers)
[$sensor->sensor_limit, $sensor->sensor_limit_low] = [$sensor->sensor_limit_low, $sensor->sensor_limit]; [$sensor->sensor_limit, $sensor->sensor_limit_low] = [$sensor->sensor_limit_low, $sensor->sensor_limit];
} }
@ -29,6 +32,14 @@ class SensorObserver
} }
} }
public function creating(Sensor $sensor): void
{
$guess_limits = \LibreNMS\Config::get('sensors.guess_limits', true);
if ($guess_limits && $sensor->sensor_current !== null && $sensor->sensor_limit === null && $sensor->sensor_limit_low === null) {
$sensor->guessLimits();
}
}
/** /**
* Handle the service "created" event. * Handle the service "created" event.
* *
@ -37,11 +48,6 @@ class SensorObserver
*/ */
public function created(Sensor $sensor): void public function created(Sensor $sensor): void
{ {
$guess_limits = \LibreNMS\Config::get('sensors.guess_limits', true);
if ($guess_limits && $sensor->sensor_current !== null && $sensor->sensor_limit === null && $sensor->sensor_limit_low === null) {
$sensor->guessLimits();
}
EventLog::log('Sensor Added: ' . $sensor->sensor_class . ' ' . $sensor->sensor_type . ' ' . $sensor->sensor_index . ' ' . $sensor->sensor_descr, $sensor->device_id, 'sensor', Severity::Notice, $sensor->sensor_id); EventLog::log('Sensor Added: ' . $sensor->sensor_class . ' ' . $sensor->sensor_type . ' ' . $sensor->sensor_index . ' ' . $sensor->sensor_descr, $sensor->device_id, 'sensor', Severity::Notice, $sensor->sensor_id);
if ($this->runningInConsole) { if ($this->runningInConsole) {

View File

@ -13,8 +13,5 @@ $init_modules = ['nodb'];
require __DIR__ . '/includes/init.php'; require __DIR__ . '/includes/init.php';
if (App::runningInConsole()) { if (App::runningInConsole()) {
// fill in db variables for legacy external scripts
Config::populateLegacyDbCredentials();
echo Config::toJson(); echo Config::toJson();
} }

View File

@ -105,7 +105,7 @@ modules:
- { value: 4096, generic: 2, graph: 0, descr: macSpoof } - { value: 4096, generic: 2, graph: 0, descr: macSpoof }
- { value: 8192, generic: 2, graph: 0, descr: cpuHigh } - { value: 8192, generic: 2, graph: 0, descr: cpuHigh }
- { value: 16384, generic: 2, graph: 0, descr: memoryUsageHigh } - { value: 16384, generic: 2, graph: 0, descr: memoryUsageHigh }
- { value: 32768, generic: 2, graph: 0, descr: packetBufferUsageHigh } # - { value: 32768, generic: 2, graph: 0, descr: packetBufferUsageHigh } # state value larger than smallint 32767
- -
oid: ledAlarmStatus oid: ledAlarmStatus
value: ledAlarmStatus value: ledAlarmStatus

View File

@ -8,7 +8,7 @@ mib_dir: eaton
over: over:
- { graph: device_voltage, text: Voltage } - { graph: device_voltage, text: Voltage }
- { graph: device_current, text: Current } - { graph: device_current, text: Current }
- { graph: device_frequency, text: Load } - { graph: device_load, text: Load }
discovery: discovery:
- -
sysObjectID: .1.3.6.1.4.1.534.1 sysObjectID: .1.3.6.1.4.1.534.1

View File

@ -208,11 +208,6 @@ function discover_sensor($unused, $class, $device, $oid, $index, $type, $descr,
if (! is_numeric($divisor)) { if (! is_numeric($divisor)) {
$divisor = 1; $divisor = 1;
} }
if (can_skip_sensor($device, $class, $descr)) {
return false;
}
d_echo("Discover sensor: $oid, $index, $type, $descr, $poller_type, $divisor, $multiplier, $entPhysicalIndex, $current, (limits: LL: $low_limit, LW: $low_warn_limit, W: $warn_limit, H: $high_limit), rrd_type = $rrd_type \n");
app('sensor-discovery')->discover(new \App\Models\Sensor([ app('sensor-discovery')->discover(new \App\Models\Sensor([
'poller_type' => $poller_type, 'poller_type' => $poller_type,
@ -635,7 +630,7 @@ function discovery_process($os, $sensor_class, $pre_cache)
$discovery = $os->getDiscovery('sensors'); $discovery = $os->getDiscovery('sensors');
$device = $os->getDeviceArray(); $device = $os->getDeviceArray();
if (! empty($discovery[$sensor_class]) && ! can_skip_sensor($device, $sensor_class, '')) { if (! empty($discovery[$sensor_class]) && ! app('sensor-discovery')->canSkip(new \App\Models\Sensor(['sensor_class' => $sensor_class]))) {
$sensor_options = []; $sensor_options = [];
if (isset($discovery[$sensor_class]['options'])) { if (isset($discovery[$sensor_class]['options'])) {
$sensor_options = $discovery[$sensor_class]['options']; $sensor_options = $discovery[$sensor_class]['options'];
@ -747,7 +742,6 @@ function discovery_process($os, $sensor_class, $pre_cache)
$value = ($value / $divisor) * $multiplier; $value = ($value / $divisor) * $multiplier;
} }
echo "$descr: Cur $value, Low: $low_limit, Low Warn: $low_warn_limit, Warn: $warn_limit, High: $high_limit" . PHP_EOL;
$entPhysicalIndex = YamlDiscovery::replaceValues('entPhysicalIndex', $index, null, $data, $pre_cache) ?: null; $entPhysicalIndex = YamlDiscovery::replaceValues('entPhysicalIndex', $index, null, $data, $pre_cache) ?: null;
$entPhysicalIndex_measured = isset($data['entPhysicalIndex_measured']) ? $data['entPhysicalIndex_measured'] : null; $entPhysicalIndex_measured = isset($data['entPhysicalIndex_measured']) ? $data['entPhysicalIndex_measured'] : null;
@ -974,33 +968,6 @@ function add_cbgp_peer($device, $peer, $afi, $safi)
} }
} }
/**
* check if we should skip this sensor from discovery
*
* @param $device
* @param string $sensor_class
* @param string $sensor_descr
* @return bool
*/
function can_skip_sensor($device, $sensor_class = '', $sensor_descr = '')
{
if (! empty($sensor_class) && (Config::getOsSetting($device['os'], "disabled_sensors.$sensor_class") || Config::get("disabled_sensors.$sensor_class"))) {
return true;
}
foreach (Config::getCombined($device['os'], 'disabled_sensors_regex') as $skipRegex) {
if (! empty($sensor_descr) && preg_match($skipRegex, $sensor_descr)) {
return true;
}
}
foreach (Config::getCombined($device['os'], "disabled_sensors_regex.$sensor_class") as $skipRegex) {
if (! empty($sensor_descr) && preg_match($skipRegex, $sensor_descr)) {
return true;
}
}
return false;
}
/** /**
* check if we should skip this device from discovery * check if we should skip this device from discovery
* *

View File

@ -8,6 +8,7 @@
* @copyright (C) 2006 - 2012 Adam Armstrong * @copyright (C) 2006 - 2012 Adam Armstrong
*/ */
use App\Models\StateTranslation;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use LibreNMS\Config; use LibreNMS\Config;
use LibreNMS\Enum\Severity; use LibreNMS\Enum\Severity;
@ -493,106 +494,23 @@ function dnslookup($device, $type = false, $return = false)
* *
* @param string $state_name the unique name for this state translation * @param string $state_name the unique name for this state translation
* @param array $states array of states, each must contain keys: descr, graph, value, generic * @param array $states array of states, each must contain keys: descr, graph, value, generic
* @return int|null * @return void
*/ */
function create_state_index($state_name, $states = []) function create_state_index($state_name, $states = []): void
{ {
$state_index_id = dbFetchCell('SELECT `state_index_id` FROM state_indexes WHERE state_name = ? LIMIT 1', [$state_name]); app('sensor-discovery')->withStateTranslations($state_name, array_map(function ($state) {
if (! is_numeric($state_index_id)) { return new StateTranslation([
$state_index_id = dbInsert(['state_name' => $state_name], 'state_indexes');
// legacy code, return index so states are created
if (empty($states)) {
return $state_index_id;
}
}
// check or synchronize states
if (empty($states)) {
$translations = dbFetchRows('SELECT * FROM `state_translations` WHERE `state_index_id` = ?', [$state_index_id]);
if (count($translations) == 0) {
// If we don't have any translations something has gone wrong so return the state_index_id so they get created.
return $state_index_id;
}
} else {
sync_sensor_states($state_index_id, $states);
}
return null;
}
/**
* Synchronize the sensor state translations with the database
*
* @param int $state_index_id index of the state
* @param array $states array of states, each must contain keys: descr, graph, value, generic
*/
function sync_sensor_states($state_index_id, $states)
{
$new_translations = array_reduce($states, function ($array, $state) use ($state_index_id) {
$array[$state['value']] = [
'state_index_id' => $state_index_id,
'state_descr' => $state['descr'], 'state_descr' => $state['descr'],
'state_draw_graph' => $state['graph'], 'state_draw_graph' => $state['graph'],
'state_value' => $state['value'], 'state_value' => $state['value'],
'state_generic_value' => $state['generic'], 'state_generic_value' => $state['generic'],
]; ]);
}, $states));
return $array;
}, []);
$existing_translations = dbFetchRows(
'SELECT `state_index_id`,`state_descr`,`state_draw_graph`,`state_value`,`state_generic_value` FROM `state_translations` WHERE `state_index_id`=?',
[$state_index_id]
);
foreach ($existing_translations as $translation) {
$value = $translation['state_value'];
if (isset($new_translations[$value])) {
if ($new_translations[$value] != $translation) {
dbUpdate(
$new_translations[$value],
'state_translations',
'`state_index_id`=? AND `state_value`=?',
[$state_index_id, $value]
);
}
// this translation is synchronized, it doesn't need to be inserted
unset($new_translations[$value]);
} else {
dbDelete('state_translations', '`state_index_id`=? AND `state_value`=?', [$state_index_id, $value]);
}
}
// insert any new translations
dbBulkInsert($new_translations, 'state_translations');
} }
function create_sensor_to_state_index($device, $state_name, $index) function create_sensor_to_state_index($device, $state_name, $index)
{ {
$sensor_entry = dbFetchRow('SELECT sensor_id FROM `sensors` WHERE `sensor_class` = ? AND `device_id` = ? AND `sensor_type` = ? AND `sensor_index` = ?', [ // no op
'state',
$device['device_id'],
$state_name,
$index,
]);
$state_indexes_entry = dbFetchRow('SELECT state_index_id FROM `state_indexes` WHERE `state_name` = ?', [
$state_name,
]);
if (! empty($sensor_entry['sensor_id']) && ! empty($state_indexes_entry['state_index_id'])) {
$insert = [
'sensor_id' => $sensor_entry['sensor_id'],
'state_index_id' => $state_indexes_entry['state_index_id'],
];
foreach ($insert as $key => $val_check) {
if (! isset($val_check)) {
unset($insert[$key]);
}
}
dbInsert($insert, 'sensors_to_state_indexes');
}
} }
function delta_to_bits($delta, $period) function delta_to_bits($delta, $period)

122
package-lock.json generated
View File

@ -3107,9 +3107,9 @@
"dev": true "dev": true
}, },
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.2", "version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "3.1.2",
@ -3120,7 +3120,7 @@
"http-errors": "2.0.0", "http-errors": "2.0.0",
"iconv-lite": "0.4.24", "iconv-lite": "0.4.24",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"qs": "6.11.0", "qs": "6.13.0",
"raw-body": "2.5.2", "raw-body": "2.5.2",
"type-is": "~1.6.18", "type-is": "~1.6.18",
"unpipe": "1.0.0" "unpipe": "1.0.0"
@ -3154,21 +3154,6 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true "dev": true
}, },
"node_modules/body-parser/node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dev": true,
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/bonjour-service": { "node_modules/bonjour-service": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz",
@ -4487,9 +4472,9 @@
} }
}, },
"node_modules/encodeurl": { "node_modules/encodeurl": {
"version": "1.0.2", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
@ -4704,37 +4689,37 @@
} }
}, },
"node_modules/express": { "node_modules/express": {
"version": "4.19.2", "version": "4.21.0",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
"body-parser": "1.20.2", "body-parser": "1.20.3",
"content-disposition": "0.5.4", "content-disposition": "0.5.4",
"content-type": "~1.0.4", "content-type": "~1.0.4",
"cookie": "0.6.0", "cookie": "0.6.0",
"cookie-signature": "1.0.6", "cookie-signature": "1.0.6",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "2.0.0", "depd": "2.0.0",
"encodeurl": "~1.0.2", "encodeurl": "~2.0.0",
"escape-html": "~1.0.3", "escape-html": "~1.0.3",
"etag": "~1.8.1", "etag": "~1.8.1",
"finalhandler": "1.2.0", "finalhandler": "1.3.1",
"fresh": "0.5.2", "fresh": "0.5.2",
"http-errors": "2.0.0", "http-errors": "2.0.0",
"merge-descriptors": "1.0.1", "merge-descriptors": "1.0.3",
"methods": "~1.1.2", "methods": "~1.1.2",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"path-to-regexp": "0.1.7", "path-to-regexp": "0.1.10",
"proxy-addr": "~2.0.7", "proxy-addr": "~2.0.7",
"qs": "6.11.0", "qs": "6.13.0",
"range-parser": "~1.2.1", "range-parser": "~1.2.1",
"safe-buffer": "5.2.1", "safe-buffer": "5.2.1",
"send": "0.18.0", "send": "0.19.0",
"serve-static": "1.15.0", "serve-static": "1.16.2",
"setprototypeof": "1.2.0", "setprototypeof": "1.2.0",
"statuses": "2.0.1", "statuses": "2.0.1",
"type-is": "~1.6.18", "type-is": "~1.6.18",
@ -4760,21 +4745,6 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true "dev": true
}, },
"node_modules/express/node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dev": true,
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -4893,13 +4863,13 @@
} }
}, },
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "1.2.0", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"debug": "2.6.9", "debug": "2.6.9",
"encodeurl": "~1.0.2", "encodeurl": "~2.0.0",
"escape-html": "~1.0.3", "escape-html": "~1.0.3",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
@ -6368,10 +6338,13 @@
} }
}, },
"node_modules/merge-descriptors": { "node_modules/merge-descriptors": {
"version": "1.0.1", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"dev": true "dev": true,
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
}, },
"node_modules/merge-source-map": { "node_modules/merge-source-map": {
"version": "1.1.0", "version": "1.1.0",
@ -7083,9 +7056,9 @@
} }
}, },
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "0.1.7", "version": "0.1.10",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
"dev": true "dev": true
}, },
"node_modules/path-type": { "node_modules/path-type": {
@ -7886,9 +7859,9 @@
"dev": true "dev": true
}, },
"node_modules/qs": { "node_modules/qs": {
"version": "6.12.1", "version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"side-channel": "^1.0.6" "side-channel": "^1.0.6"
@ -8455,9 +8428,9 @@
"dev": true "dev": true
}, },
"node_modules/send": { "node_modules/send": {
"version": "0.18.0", "version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"debug": "2.6.9", "debug": "2.6.9",
@ -8493,6 +8466,15 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true "dev": true
}, },
"node_modules/send/node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
},
"node_modules/send/node_modules/ms": { "node_modules/send/node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -8587,15 +8569,15 @@
} }
}, },
"node_modules/serve-static": { "node_modules/serve-static": {
"version": "1.15.0", "version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"encodeurl": "~1.0.2", "encodeurl": "~2.0.0",
"escape-html": "~1.0.3", "escape-html": "~1.0.3",
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"send": "0.18.0" "send": "0.19.0"
}, },
"engines": { "engines": {
"node": ">= 0.8.0" "node": ">= 0.8.0"

View File

@ -33,6 +33,7 @@ from sys import stdout
from time import time from time import time
Result = namedtuple("Result", ["ip", "hostname", "outcome", "output"]) Result = namedtuple("Result", ["ip", "hostname", "outcome", "output"])
args = {}
class Outcome: class Outcome:
@ -263,7 +264,9 @@ Example: 192.168.0.1/32 will be treated as a single host address""",
chdir(install_dir) chdir(install_dir)
try: try:
CONFIG = json.loads( CONFIG = json.loads(
check_output(["/usr/bin/env", "php", "config_to_json.php"]).decode() check_output(
["/usr/bin/env", "php", "lnms", "config:get", "--dump"]
).decode()
) )
except CalledProcessError as e: except CalledProcessError as e:
parser.error( parser.error(

View File

@ -118,7 +118,7 @@ class OSModulesTest extends DBTestCase
// output all discovery and poller output if debug mode is enabled for phpunit // output all discovery and poller output if debug mode is enabled for phpunit
$phpunit_debug = in_array('--debug', $_SERVER['argv'], true); $phpunit_debug = in_array('--debug', $_SERVER['argv'], true);
foreach ($modules as $module) { foreach ($modules as $module => $module_status) {
$expected = $expected_data[$module]['discovery'] ?? []; $expected = $expected_data[$module]['discovery'] ?? [];
$actual = $results[$module]['discovery'] ?? []; $actual = $results[$module]['discovery'] ?? [];
$this->checkTestData($expected, $actual, 'Discovered', $os, $module, $filename, $helper, $phpunit_debug); $this->checkTestData($expected, $actual, 'Discovered', $os, $module, $filename, $helper, $phpunit_debug);