From 8df92a5f2bc2d53f567c22861b96a3ca97a925f8 Mon Sep 17 00:00:00 2001 From: Tony Murray Date: Tue, 15 Mar 2022 06:54:02 -0500 Subject: [PATCH] API: device_add support display field (#13846) * API: device_add support display field remove legacy code path update docs A few improvements to the new code too * some fixes, port_association_mode was not available * hand version -> snmpver translation --- .../Device/ValidateDeviceAndCreate.php | 21 +++-- app/Console/Commands/DeviceAdd.php | 6 +- doc/API/Devices.md | 21 +++-- includes/html/api_functions.inc.php | 78 ++++++++----------- 4 files changed, 67 insertions(+), 59 deletions(-) diff --git a/app/Actions/Device/ValidateDeviceAndCreate.php b/app/Actions/Device/ValidateDeviceAndCreate.php index a6d9dd1c5d..9117592568 100644 --- a/app/Actions/Device/ValidateDeviceAndCreate.php +++ b/app/Actions/Device/ValidateDeviceAndCreate.php @@ -28,6 +28,7 @@ namespace App\Actions\Device; use App\Models\Device; use Illuminate\Support\Arr; use LibreNMS\Config; +use LibreNMS\Enum\PortAssociationMode; use LibreNMS\Exceptions\HostIpExistsException; use LibreNMS\Exceptions\HostnameExistsException; use LibreNMS\Exceptions\HostSysnameExistsException; @@ -79,11 +80,7 @@ class ValidateDeviceAndCreate } $this->exceptIfHostnameExists(); - - // defaults - $this->device->os = $this->device->os ?: 'generic'; - $this->device->status_reason = ''; - $this->device->sysName = $this->device->sysName ?: $this->device->hostname; + $this->fillDefaults(); if (! $this->force) { $this->exceptIfIpExists(); @@ -181,6 +178,20 @@ class ValidateDeviceAndCreate } } + private function fillDefaults(): void + { + $this->device->port = $this->device->port ?: Config::get('snmp.port', 161); + $this->device->transport = $this->device->transport ?: Config::get('snmp.transports.0', 'udp'); + $this->device->poller_group = $this->device->poller_group ?: Config::get('default_poller_group', 0); + $this->device->os = $this->device->os ?: 'generic'; + $this->device->status_reason = ''; + $this->device->sysName = $this->device->sysName ?: $this->device->hostname; + $this->device->port_association_mode = $this->device->port_association_mode ?: Config::get('default_port_association_mode', 'ifIndex'); + if (! is_int($this->device->port_association_mode)) { + $this->device->port_association_mode = PortAssociationMode::getId($this->device->port_association_mode) ?? 1; + } + } + /** * @throws \LibreNMS\Exceptions\HostExistsException */ diff --git a/app/Console/Commands/DeviceAdd.php b/app/Console/Commands/DeviceAdd.php index 6c9414a1a0..dd165a12fe 100644 --- a/app/Console/Commands/DeviceAdd.php +++ b/app/Console/Commands/DeviceAdd.php @@ -57,15 +57,15 @@ class DeviceAdd extends LnmsCommand $this->addOption('ping-fallback', 'b', InputOption::VALUE_NONE); $this->addOption('port-association-mode', 'p', InputOption::VALUE_REQUIRED, null, Config::get('default_port_association_mode')); $this->addOption('community', 'c', InputOption::VALUE_REQUIRED); - $this->addOption('transport', 't', InputOption::VALUE_REQUIRED, null, 'udp'); - $this->addOption('port', 'r', InputOption::VALUE_REQUIRED, null, 161); + $this->addOption('transport', 't', InputOption::VALUE_REQUIRED, null, Config::get('snmp.transports.0', 'udp')); + $this->addOption('port', 'r', InputOption::VALUE_REQUIRED, null, Config::get('snmp.port', 161)); $this->addOption('security-name', 'u', InputOption::VALUE_REQUIRED, null, 'root'); $this->addOption('auth-password', 'A', InputOption::VALUE_REQUIRED); $this->addOption('auth-protocol', 'a', InputOption::VALUE_REQUIRED, null, 'MD5'); $this->addOption('privacy-password', 'X', InputOption::VALUE_REQUIRED); $this->addOption('privacy-protocol', 'x', InputOption::VALUE_REQUIRED, null, 'AES'); $this->addOption('ping-only', 'P', InputOption::VALUE_NONE); - $this->addOption('os', 'o', InputOption::VALUE_REQUIRED, null, 'ping'); + $this->addOption('os', 'o', InputOption::VALUE_REQUIRED); $this->addOption('hardware', 'w', InputOption::VALUE_REQUIRED); $this->addOption('sysName', 's', InputOption::VALUE_REQUIRED); } diff --git a/doc/API/Devices.md b/doc/API/Devices.md index b72b0850ad..e8cb2cab5d 100644 --- a/doc/API/Devices.md +++ b/doc/API/Devices.md @@ -1129,20 +1129,28 @@ Output: ### `add_device` -Add a new device. +Add a new device. Most fields are optional. You may omit snmp +credentials to attempt each system credential in order. See snmp.version, snmp.community, and snmp.v3 + +To guarantee device is added, use force_add. This will skip checks +for duplicate device and snmp reachability, but not duplicate hostname. Route: `/api/v0/devices` Input (JSON): -- hostname: device hostname -- overwrite_ip: alternate polling IP. Will be use instead of hostname (optional) +- hostname (required): device hostname or IP +- display: A string to display as the name of this device, defaults to + hostname (or device_display_default setting). May be a simple + template using replacements: {{ $hostname }}, {{ $sysName }}, + {{ $sysName_fallback }}, {{ $ip }} - port: SNMP port (defaults to port defined in config). - transport: SNMP protocol (defaults to transport defined in config). -- version: SNMP version to use, v1, v2c or v3. Defaults to v2c. +- snmpver: SNMP version to use, v1, v2c or v3. Defaults to v2c. +- port_association_mode: method to identify ports: ifIndex (default), ifName, ifDescr, ifAlias - poller_group: This is the poller_group id used for distributed poller setup. Defaults to 0. -- force_add: Force the device to be added regardless of it being able +- force_add: Set to true to force the device to be added regardless of it being able to respond to snmp or icmp. For SNMP v1 or v2c @@ -1154,7 +1162,7 @@ For SNMP v3 - authlevel: SNMP authlevel (noAuthNoPriv, authNoPriv, authPriv). - authname: SNMP Auth username - authpass: SNMP Auth password -- authalgo: SNMP Auth algorithm (MD5, SHA) +- authalgo: SNMP Auth algorithm (MD5, SHA) (SHA-224, SHA-256, SHA-384, SHA-512 if supported by your server) - cryptopass: SNMP Crypto Password - cryptoalgo: SNMP Crypto algorithm (AES, DES) @@ -1162,6 +1170,7 @@ For ICMP only - snmp_disable: Boolean, set to true for ICMP only. - os: OS short name for the device (defaults to ping). +- sysName: sysName for the device. - hardware: Device hardware. Example: diff --git a/includes/html/api_functions.inc.php b/includes/html/api_functions.inc.php index 6dffaf9113..4909189bd3 100644 --- a/includes/html/api_functions.inc.php +++ b/includes/html/api_functions.inc.php @@ -12,6 +12,7 @@ * the source code distribution for details. */ +use App\Actions\Device\ValidateDeviceAndCreate; use App\Models\Availability; use App\Models\Device; use App\Models\DeviceGroup; @@ -369,12 +370,8 @@ function list_devices(Illuminate\Http\Request $request) function add_device(Illuminate\Http\Request $request) { // This will add a device using the data passed encoded with json - // FIXME: Execution flow through this function could be improved - $data = json_decode($request->getContent(), true); + $data = $request->json()->all(); - $additional = []; - // keep scrutinizer from complaining about snmpver not being set for all execution paths - $snmpver = 'v2c'; if (empty($data)) { return api_error(400, 'No information has been provided to add this new device'); } @@ -382,54 +379,45 @@ function add_device(Illuminate\Http\Request $request) return api_error(400, 'Missing the device hostname'); } - $hostname = $data['hostname']; - $port = $data['port'] ?: Config::get('snmp.port'); - $transport = $data['transport'] ?: 'udp'; - $poller_group = $data['poller_group'] ?: 0; - $force_add = $data['force_add'] ? true : false; - $snmp_disable = ($data['snmp_disable']); - if ($snmp_disable) { - $additional = [ - 'sysName' => $data['sysName'] ?: '', - 'os' => $data['os'] ?: 'ping', - 'hardware' => $data['hardware'] ?: '', - 'snmp_disable' => 1, - ]; - } elseif ($data['version'] == 'v1' || $data['version'] == 'v2c') { - if ($data['community']) { - Config::set('snmp.community', [$data['community']]); + try { + $device = new Device(Arr::only($data, [ + 'hostname', + 'display', + 'overwrite_ip', + 'port', + 'transport', + 'poller_group', + 'snmpver', + 'port_association_mode', + 'community', + 'authlevel', + 'authname', + 'authpass', + 'authalgo', + 'cryptopass', + 'cryptoalgo', + ])); + + // uses different name in legacy call + if (! empty($data['version'])) { + $device->snmpver = $data['version']; } - $snmpver = $data['version']; - } elseif ($data['version'] == 'v3') { - $v3 = [ - 'authlevel' => $data['authlevel'], - 'authname' => $data['authname'], - 'authpass' => $data['authpass'], - 'authalgo' => $data['authalgo'], - 'cryptopass' => $data['cryptopass'], - 'cryptoalgo' => $data['cryptoalgo'], - ]; + if (! empty($data['snmp_disable'])) { + $device->os = $data['os'] ?? 'ping'; + $device->sysName = $data['sysName'] ?? ''; + $device->hardware = $data['hardware'] ?? ''; + $device->snmp_disable = 1; + } - $v3_config = Config::get('snmp.v3'); - array_unshift($v3_config, $v3); - Config::set('snmp.v3', $v3_config); - $snmpver = 'v3'; - } else { - return api_error(400, 'You haven\'t specified an SNMP version to use'); - } - - $additional['overwrite_ip'] = $data['overwrite_ip'] ?: null; - - try { - $device_id = addHost($hostname, $snmpver, $port, $transport, $poller_group, $force_add, 'ifIndex', $additional); + (new ValidateDeviceAndCreate($device, ! empty($data['force_add'])))->execute(); } catch (Exception $e) { return api_error(500, $e->getMessage()); } - $device = device_by_id_cache($device_id); + $message = "Device $device->hostname ($device->device_id) has been added successfully"; - return api_success([$device], 'devices', $response); + return api_success($device->attributesToArray(), 'devices', $message); } function del_device(Illuminate\Http\Request $request)