Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions LibreNMS/Enum/PollingMethodType.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function hasSecret(): bool
}

/**
* @param array<string, array> $schema
* @param array<string, array> $schema
* @return array
*/
public static function buildSchemaFields(array $schema, string $dataVar = 'formData'): array
Expand All @@ -68,11 +68,10 @@ public static function buildSchemaFields(array $schema, string $dataVar = 'formD

return [
...$field,
'key' => $key,
'field_type' => $field['type'] ?? 'text',
'key' => $key,
'field_type' => $field['type'] ?? 'text',
'visible_if_expression' => $visibleIfExpression,
];
})->values()->all();
}
}

3 changes: 0 additions & 3 deletions LibreNMS/Enum/SecretType.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@

namespace LibreNMS\Enum;

use LibreNMS\Polling\Secrets\IcmpSecret;
use LibreNMS\Polling\Secrets\IpmiSecret;
use LibreNMS\Polling\Secrets\SecretData;
use LibreNMS\Polling\Secrets\SnmpSecret;
use LibreNMS\Polling\Secrets\UnixAgentSecret;

enum SecretType: string
{
Expand All @@ -57,5 +55,4 @@ public function secretClass(): string
self::Ipmi => IpmiSecret::class,
};
}

}
3 changes: 3 additions & 0 deletions LibreNMS/Interfaces/PollingMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@ interface PollingMethod
{
/**
* UI/form schema for device-specific settings.
*
* @return array<string, array{type: string, options?: array<string,string>, visible_if: array}>
*/
public static function getSettingsSchema(): array;

/**
* Defaults for polling method per-device settings
*
* @return array<string, mixed>
*/
public static function getDefaults(): array;

/**
* Validation rules for polling method per-device settings
*
* @return array<string, array|string>
*/
public static function getRules(): array;
Expand Down
3 changes: 2 additions & 1 deletion LibreNMS/Polling/ConnectivityHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
{
public function __construct(
private Device $device,
) {}
) {
}

public function isAvailable(): bool
{
Expand All @@ -42,7 +43,7 @@
}

foreach ($this->device->pollingMethods as $method) {
if ($method->enabled && $method->affects_availability && ! $method->last_check_successful) {

Check failure on line 46 in LibreNMS/Polling/ConnectivityHelper.php

View workflow job for this annotation

GitHub Actions / PHP Static Analysis

Access to an undefined property Illuminate\Database\Eloquent\Model::$last_check_successful.

Check failure on line 46 in LibreNMS/Polling/ConnectivityHelper.php

View workflow job for this annotation

GitHub Actions / PHP Static Analysis

Access to an undefined property Illuminate\Database\Eloquent\Model::$enabled.

Check failure on line 46 in LibreNMS/Polling/ConnectivityHelper.php

View workflow job for this annotation

GitHub Actions / PHP Static Analysis

Access to an undefined property Illuminate\Database\Eloquent\Model::$affects_availability.
return true;
}
}
Expand All @@ -53,7 +54,7 @@
public function hasAvailability(): bool
{
foreach ($this->device->pollingMethods as $method) {
if ($method->enabled && $method->affects_availability) {

Check failure on line 57 in LibreNMS/Polling/ConnectivityHelper.php

View workflow job for this annotation

GitHub Actions / PHP Static Analysis

Access to an undefined property Illuminate\Database\Eloquent\Model::$enabled.

Check failure on line 57 in LibreNMS/Polling/ConnectivityHelper.php

View workflow job for this annotation

GitHub Actions / PHP Static Analysis

Access to an undefined property Illuminate\Database\Eloquent\Model::$affects_availability.
return true;
}
}
Expand Down
3 changes: 2 additions & 1 deletion LibreNMS/Polling/Method/IcmpPollingMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
public function __construct(
public bool $enabled,
public bool $affectsAvailability,
) {}
) {
}

public static function fromModel(DevicePollingMethod $method): self
{
Expand Down
3 changes: 2 additions & 1 deletion LibreNMS/Polling/Method/IpmiPollingMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public function __construct(
public int $cipherSuite,
public int $timeout,
public string $type,
) {}
) {
}

public static function fromModel(DevicePollingMethod $method): self
{
Expand Down
3 changes: 2 additions & 1 deletion LibreNMS/Polling/Method/SnmpPollingMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public function __construct(
public int $retries,
public int $maxRepeaters,
public int $maxOid,
) {}
) {
}

public static function fromModel(DevicePollingMethod $method): self
{
Expand Down
3 changes: 2 additions & 1 deletion LibreNMS/Polling/Method/UnixAgentPollingMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public function __construct(
public bool $enabled,
public bool $affectsAvailability,
public int $port,
) {}
) {
}

public static function fromModel(DevicePollingMethod $method): self
{
Expand Down
2 changes: 0 additions & 2 deletions LibreNMS/Polling/ModuleStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

namespace LibreNMS\Polling;


class ModuleStatus implements \Stringable
{
public function __construct(
Expand Down Expand Up @@ -76,7 +75,6 @@ public function reason(): string
return 'globally';
}


public function hasSubModules(): bool
{
return ! empty($this->submodules);
Expand Down
4 changes: 2 additions & 2 deletions LibreNMS/Polling/Secrets/SecretData.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ abstract class SecretData implements Arrayable, Jsonable, JsonSerializable
/**
* Create a new DTO instance from an array.
*/
public static abstract function fromArray(array $data): static;
abstract public static function fromArray(array $data): static;

/**
* Get validation rules for this credential type.
*/
public static abstract function rules(): array;
abstract public static function rules(): array;

/**
* Get UI schema for this credential type.
Expand Down
1 change: 1 addition & 0 deletions LibreNMS/Polling/Secrets/SnmpSecret.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public static function fromArray(array $data): static

/**
* @deprecated
*
* @param array $device
* @return static
*/
Expand Down
3 changes: 2 additions & 1 deletion app/Actions/Device/SetDeviceAvailability.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public function __construct(
* @param bool $commit Save changes to the database
* @return bool true if the status changed
*/
public function execute(Device $device, bool $commit = true): bool {
public function execute(Device $device, bool $commit = true): bool
{
if ($device->exists || $device->relationLoaded('pollingMethods')) {
$failedAvailabilityMethods = $device->pollingMethods
->filter(fn ($method) => $method->enabled && $method->affects_availability && ! $method->last_check_successful);
Expand Down
4 changes: 2 additions & 2 deletions app/Actions/Device/ValidateDeviceAndCreate.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ private function detectCredentials(): void
$host_unreachable_exception = new HostUnreachableSnmpException($this->device->hostname);

// Retrieve existing SNMP secret from relations if set
$existingSnmpSecret = $this->device->relationLoaded('pollingMethods')
? $this->device->pollingMethods->firstWhere('method_type', PollingMethodType::Snmp)?->secret
$existingSnmpSecret = $this->device->relationLoaded('pollingMethods')
? $this->device->pollingMethods->firstWhere('method_type', PollingMethodType::Snmp)?->secret
: null;

// which snmp version should we try (and in what order)
Expand Down
2 changes: 1 addition & 1 deletion app/Casts/EncryptedArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function get(Model $model, string $key, mixed $value, array $attributes):
*
* @param array<string, mixed> $attributes
*/
public function set(Model $model, string $key, mixed $value, array $attributes): string|null
public function set(Model $model, string $key, mixed $value, array $attributes): ?string
{
if (! is_array($value) || empty($value)) {
return null;
Expand Down
53 changes: 27 additions & 26 deletions app/Http/Controllers/Device/AddDeviceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ class AddDeviceController
use AuthorizesRequests;

public function __construct(
private readonly SecretService $secretService,
) {}
private readonly SecretService $secretService,
) {
}

public function index(Request $request): View
{
Expand All @@ -39,11 +40,11 @@ public function index(Request $request): View
$settingsSchema = $methodClass::getSettingsSchema();

return [
'type' => $type->value,
'label' => __('poller.methods.' . $type->value),
'schema_fields' => $schemaFields,
'type' => $type->value,
'label' => __('poller.methods.' . $type->value),
'schema_fields' => $schemaFields,
'schema_defaults' => collect($schema)->mapWithKeys(
fn(array $field, string $key): array => [
fn (array $field, string $key): array => [
$key => $field['default'] ?? (isset($field['options']) ? array_key_first($field['options']) : ''),
]
)->all(),
Expand All @@ -52,15 +53,15 @@ public function index(Request $request): View
});

$availableSecrets = Secret::query()->orderBy('description')->get()->groupBy(
fn(Secret $s): string => $s->secret_type->value
fn (Secret $s): string => $s->secret_type->value
);

return view('device.add', [
'availableMethods' => $availableMethods,
'availableSecrets' => $availableSecrets,
'poller_groups' => PollerGroup::orderBy('group_name')->pluck('group_name', 'id'),
'default_poller_group' => LibrenmsConfig::get('default_poller_group', 0),
'port_association_modes' => PortAssociationMode::getModes(),
'availableMethods' => $availableMethods,
'availableSecrets' => $availableSecrets,
'poller_groups' => PollerGroup::orderBy('group_name')->pluck('group_name', 'id'),
'default_poller_group' => LibrenmsConfig::get('default_poller_group', 0),
'port_association_modes' => PortAssociationMode::getModes(),
'default_port_association_mode' => LibrenmsConfig::get('default_port_association_mode', 'ifIndex'),
]);
}
Expand All @@ -72,8 +73,8 @@ public function store(StoreDeviceRequest $request, ToastInterface $toast): Redir
$validated = $request->validated();

$device = new Device([
'hostname' => $validated['hostname'],
'poller_group' => $validated['poller_group'] ?? LibrenmsConfig::get('default_poller_group', 0),
'hostname' => $validated['hostname'],
'poller_group' => $validated['poller_group'] ?? LibrenmsConfig::get('default_poller_group', 0),
'port_association_mode' => PortAssociationMode::getId($validated['port_assoc_mode'] ?? 'ifIndex') ?? 1,
]);

Expand All @@ -97,16 +98,16 @@ public function store(StoreDeviceRequest $request, ToastInterface $toast): Redir
// SNMP port/transport live in settings on the form; promote them onto
// the device row where the legacy schema expects them.
if ($type === PollingMethodType::Snmp) {
$device->port = (int) ($settings['port'] ?? LibrenmsConfig::get('snmp.port', 161));
$device->port = (int) ($settings['port'] ?? LibrenmsConfig::get('snmp.port', 161));
$device->transport = $settings['transport'] ?? LibrenmsConfig::get('snmp.transports.0', 'udp');
unset($settings['port'], $settings['transport']);
}

$pollingMethod = new DevicePollingMethod([
'method_type' => $type,
'enabled' => true,
'method_type' => $type,
'enabled' => true,
'affects_availability' => (bool) ($data['affects_availability'] ?? false),
'settings' => $settings,
'settings' => $settings,
]);

if ($type->hasSecret()) {
Expand All @@ -123,22 +124,22 @@ public function store(StoreDeviceRequest $request, ToastInterface $toast): Redir

if (! $snmpActive) {
$device->snmp_disable = 1;
$device->os = $validated['os'] ?: 'ping';
$device->sysName = $validated['sysName'] ?: '';
$device->hardware = $validated['hardware'] ?: '';
$device->os = $validated['os'] ?: 'ping';
$device->sysName = $validated['sysName'] ?: '';
$device->hardware = $validated['hardware'] ?: '';
} else {
$device->snmp_disable = 0;
}

// Per-method validate flags: validate if *any* active method requests it.
// The SNMP method's validate flag doubles as the old force_add inverse.
$forceAdd = collect($rawMethods)
->filter(fn(array $data): bool => (bool) ($data['active'] ?? false))
->every(fn(array $data): bool => empty($data['validate']));
->filter(fn (array $data): bool => (bool) ($data['active'] ?? false))
->every(fn (array $data): bool => empty($data['validate']));

try {
$validator = new ValidateDeviceAndCreate($device, $forceAdd);
$success = $validator->execute();
$success = $validator->execute();

if (! $success) {
return back()->withInput()->withErrors(['hostname' => __('Failed to save device.')]);
Expand Down Expand Up @@ -174,8 +175,8 @@ private function resolveSecret(PollingMethodType $type, array $data): ?Secret
return new Secret([
'secret_type' => SecretType::tryFrom($type->value),
'description' => $data['description'] ?? '',
'default' => (bool) ($data['default'] ?? false),
'data' => $data['secret_data'] ?? [],
'default' => (bool) ($data['default'] ?? false),
'data' => $data['secret_data'] ?? [],
]);
}

Expand Down
21 changes: 11 additions & 10 deletions app/Http/Controllers/Device/EditPollingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class EditPollingController

public function __construct(
private readonly SecretService $secretService,
) {}
) {
}

/**
* @throws AuthorizationException
Expand Down Expand Up @@ -63,11 +64,11 @@ private function buildMethodData(Device $device, PollingMethodType $type): array
->orderBy('description')
->get();
$secretDescriptions = $secretsForType->mapWithKeys(fn (Secret $availableSecret): array => [
(string)$availableSecret->id => $availableSecret->description,
(string) $availableSecret->id => $availableSecret->description,
])->all();
$secretFormDataById = $secretsForType->mapWithKeys(fn (Secret $availableSecret): array => [
(string)$availableSecret->id => collect($schemaFields)->mapWithKeys(fn (array $field): array => [
$field['key'] => $canUnmaskSecrets ? (string)data_get($availableSecret->data, $field['key'], '') : '',
(string) $availableSecret->id => collect($schemaFields)->mapWithKeys(fn (array $field): array => [
$field['key'] => $canUnmaskSecrets ? (string) data_get($availableSecret->data, $field['key'], '') : '',
])->all(),
])->all();

Expand All @@ -80,10 +81,10 @@ private function buildMethodData(Device $device, PollingMethodType $type): array
])->all(),
'settings_fields' => PollingMethodType::buildSchemaFields($settingsSchema, 'settingsData'),
'settings' => $row?->settings ?? [],
'affects_availability' => $row?->affects_availability ?? (bool)($methodClass::getDefaults()['affects_availability'] ?? false),
'affects_availability' => $row?->affects_availability ?? (bool) ($methodClass::getDefaults()['affects_availability'] ?? false),
'secret' => $secret,
'secret_form_data' => collect($schema)->mapWithKeys(fn (array $field, string $key): array => [
$key => $canUnmaskSecrets ? (string)data_get($secret?->data, $key, '') : '',
$key => $canUnmaskSecrets ? (string) data_get($secret?->data, $key, '') : '',
])->all(),
'secret_descriptions' => $secretDescriptions,
'secret_form_data_by_id' => $secretFormDataById,
Expand Down Expand Up @@ -125,7 +126,7 @@ public function store(StorePollingMethodRequest $request, Device $device, ToastI
'device_id' => $device->device_id,
'method_type' => $type,
'enabled' => true,
'affects_availability' => (bool)($methodClass::getDefaults()['affects_availability'] ?? false),
'affects_availability' => (bool) ($methodClass::getDefaults()['affects_availability'] ?? false),
'secret_id' => $secret?->id,
'settings' => $this->buildSettings($methodClass, $request->validatedSettings()),
]);
Expand Down Expand Up @@ -184,7 +185,7 @@ public function update(UpdatePollingMethodRequest $request, Device $device, stri

if ($type->hasSecret() && array_key_exists('secret_id', $validated)) {
$this->authorize('update', Secret::class);
$pollingMethod->secret_id = $this->resolveExistingSecret((int)$validated['secret_id'], $type)->id;
$pollingMethod->secret_id = $this->resolveExistingSecret((int) $validated['secret_id'], $type)->id;
} elseif ($type->hasSecret() && $request->has('secret_data')) {
$this->authorize('update', Secret::class);
$mode = $validated['secret_update_mode'] ?? 'update';
Expand All @@ -200,8 +201,8 @@ public function update(UpdatePollingMethodRequest $request, Device $device, stri

$methodClass = $type->methodClass();

$pollingMethod->enabled = (bool)($validated['enabled'] ?? true);
$pollingMethod->affects_availability = (bool)($validated['affects_availability'] ?? false);
$pollingMethod->enabled = (bool) ($validated['enabled'] ?? true);
$pollingMethod->affects_availability = (bool) ($validated['affects_availability'] ?? false);
$pollingMethod->settings = $this->mergeSettings($pollingMethod->settings ?? [], $validated['settings'] ?? [], $methodClass);

$pollingMethod->save();
Expand Down
6 changes: 3 additions & 3 deletions app/Http/Controllers/SecretController.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public function update(Request $request, Secret $secret, ToastInterface $toast):

$validated = $request->validate([
'description' => 'required|string|max:255',
'default' => 'boolean',
'default' => 'boolean',
]);

$secretType = $secret->secret_type;
Expand All @@ -128,8 +128,8 @@ public function update(Request $request, Secret $secret, ToastInterface $toast):

$secret->update([
'description' => $validated['description'],
'default' => $request->boolean('default'),
'data' => $data,
'default' => $request->boolean('default'),
'data' => $data,
]);

$toast->success(__('Secret updated'));
Expand Down
1 change: 0 additions & 1 deletion app/Models/Device.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace App\Models;

use App\Data\DeviceCredentialRepository;
use App\Facades\LibrenmsConfig;
use App\Models\Traits\Filterable;
use App\View\SimpleTemplate;
Expand Down
Loading
Loading