OSPF SNMP Trap Handlers (#11647)

* Intitial push working tests

* Adding unit tests

* created interface state unit tests

* started ospf nbr unit tests

* finished nbr change unit tests

* precommit checks pass

* removed uneccsarry relationship
This commit is contained in:
Heath Barnhart
2020-05-21 15:15:39 -05:00
committed by GitHub
parent b71d933ac9
commit e414352356
7 changed files with 612 additions and 16 deletions

View File

@@ -0,0 +1,88 @@
<?php
/**
* OspfIfStateChange.php
*
* -Description-
* Handles the ospfIfStateChange SNMP trap signaling an interface
* in the OSPF topology has changed its state. The handler logs the
* change and updates the interface's state in ospf_ports table.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2020 KanREN Inc
* @author Heath Barnhart <hbarnhart@kanren.net>
*/
namespace LibreNMS\Snmptrap\Handlers;
use App\Models\Device;
use LibreNMS\Interfaces\SnmptrapHandler;
use LibreNMS\Snmptrap\Trap;
use Log;
class OspfIfStateChange implements SnmptrapHandler
{
/**
* Handle snmptrap.
* Data is pre-parsed and delivered as a Trap.
*
* @param Device $device
* @param Trap $trap
* @return void
*/
public function handle(Device $device, Trap $trap)
{
$ospfIfIpAddress = $trap->getOidData($trap->findOid('OSPF-MIB::ospfIfIpAddress'));
$ospfPort = $device->ospfPorts()->where('ospfIfIpAddress', $ospfIfIpAddress)->first();
$port = $device->ports()->where('port_id', $ospfPort->port_id)->first();
if (!$port) {
Log::warning("Snmptrap ospfIfStateChange: Could not find port at port_id $ospfPort->port_id for device: " . $device->hostname);
return;
}
$ospfPort->ospfIfState = $trap->getOidData($trap->findOid('OSPF-MIB::ospfIfState'));
switch ($ospfPort->ospfIfState) {
case 'down':
$severity = 5;
break;
case 'designatedRouter':
$severity = 1;
break;
case 'backupDesignatedRouter':
$severity = 1;
break;
case 'otherDesignatedRouter':
$severity = 1;
break;
case 'pointToPoint':
$severity = 1;
break;
case 'waiting':
$severity = 4;
break;
case 'loopback':
$severity = 4;
break;
}
Log::event("OSPF interface $port->ifName is $ospfPort->ospfIfState", $device->device_id, 'trap', $severity);
$ospfPort->save();
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* OspfNbrStateChange.php
*
* -Description-
* Handles ospfNbrStateChange SNMP traps. Trap is sent when an OSPF
* neighbor changes state. Handler logs the change and updates the
* neighbor's information in the ospf_nbrs table.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2020 KanREN Inc
* @author Heath Barnhart <hbarnhart@kanren.net>
*/
namespace LibreNMS\Snmptrap\Handlers;
use App\Models\Device;
use LibreNMS\Interfaces\SnmptrapHandler;
use LibreNMS\Snmptrap\Trap;
use Log;
class OspfNbrStateChange implements SnmptrapHandler
{
/**
* Handle snmptrap.
* Data is pre-parsed and delivered as a Trap.
*
* @param Device $device
* @param Trap $trap
* @return void
*/
public function handle(Device $device, Trap $trap)
{
$ospfNbrIpAddr = $trap->getOidData($trap->findOid('OSPF-MIB::ospfNbrRtrId'));
$ospfNbr = $device->ospfNbrs()->where('ospfNbrRtrId', $ospfNbrIpAddr)->first();
$ospfNbr->ospfNbrState = $trap->getOidData($trap->findOid('OSPF-MIB::ospfNbrState'));
$severity = 4;
if ($ospfNbr->ospfNbrState == 'full') {
$severity = 1;
} elseif ($ospfNbr->ospfNbrState == 'down') {
$severity = 5;
}
Log::event("OSPF neighbor $ospfNbrIpAddr changed state to $ospfNbr->ospfNbrState", $device->device_id, 'trap', $severity);
$ospfNbr->save();
}
}

View File

@@ -45,7 +45,7 @@ class Device extends BaseModel
*/
public static function pollerTarget($device)
{
if (! is_array($device)) {
if (!is_array($device)) {
$ret = static::where('hostname', $device)->first(['hostname', 'overwrite_ip']);
if (empty($ret)) {
return $device;
@@ -222,7 +222,7 @@ class Device extends BaseModel
];
foreach ($options as $file) {
if (is_file(public_path()."/$file")) {
if (is_file(public_path() . "/$file")) {
return asset($file);
}
}
@@ -281,9 +281,9 @@ class Device extends BaseModel
public function validateStandalone()
{
if ($this->max_depth === 0 && $this->children()->count() > 0) {
$this->max_depth = 1; // has children
$this->max_depth = 1; // has children
} elseif ($this->max_depth === 1 && $this->parents()->count() === 0) {
$this->max_depth = 0; // no children or parents
$this->max_depth = 0; // no children or parents
}
$this->save();
@@ -306,7 +306,7 @@ class Device extends BaseModel
}
$attrib->attrib_value = $value;
return (bool)$this->attribs()->save($attrib);
return (bool) $this->attribs()->save($attrib);
}
public function forgetAttrib($name)
@@ -316,7 +316,7 @@ class Device extends BaseModel
});
if ($attrib_index !== false) {
$deleted=(bool)$this->attribs->get($attrib_index)->delete();
$deleted = (bool) $this->attribs->get($attrib_index)->delete();
// only forget the attrib_index after delete, otherwise delete() will fail fatally with:
// Symfony\\Component\\Debug\Exception\\FatalThrowableError(code: 0): Call to a member function delete() on null
$this->attribs->forget($attrib_index);
@@ -365,7 +365,7 @@ class Device extends BaseModel
public function setStatusAttribute($status)
{
$this->attributes['status'] = (int)$status;
$this->attributes['status'] = (int) $status;
}
// ---- Query scopes ----
@@ -376,7 +376,7 @@ class Device extends BaseModel
['status', '=', 1],
['ignore', '=', 0],
['disable_notify', '=', 0],
['disabled', '=', 0]
['disabled', '=', 0],
]);
}
@@ -384,7 +384,7 @@ class Device extends BaseModel
{
return $query->where([
['ignore', '=', 0],
['disabled', '=', 0]
['disabled', '=', 0],
]);
}
@@ -394,7 +394,7 @@ class Device extends BaseModel
['status', '=', 0],
['disable_notify', '=', 0],
['ignore', '=', 0],
['disabled', '=', 0]
['disabled', '=', 0],
]);
}
@@ -402,28 +402,28 @@ class Device extends BaseModel
{
return $query->where([
['ignore', '=', 1],
['disabled', '=', 0]
['disabled', '=', 0],
]);
}
public function scopeNotIgnored($query)
{
return $query->where([
['ignore', '=', 0]
['ignore', '=', 0],
]);
}
public function scopeIsDisabled($query)
{
return $query->where([
['disabled', '=', 1]
['disabled', '=', 1],
]);
}
public function scopeIsDisableNotify($query)
{
return $query->where([
['disable_notify', '=', 1]
['disable_notify', '=', 1],
]);
}
@@ -431,7 +431,7 @@ class Device extends BaseModel
{
return $query->where([
['disable_notify', '=', 0],
['disabled', '=', 0]
['disabled', '=', 0],
]);
}
@@ -439,7 +439,7 @@ class Device extends BaseModel
{
return $query->where([
['uptime', '>', 0],
['uptime', $modifier, $uptime]
['uptime', $modifier, $uptime],
]);
}
@@ -571,6 +571,15 @@ class Device extends BaseModel
{
return $this->hasMany(\App\Models\OspfInstance::class, 'device_id');
}
public function ospfNbrs()
{
return $this->hasMany(\App\Models\OspfNbr::class, 'device_id');
}
public function ospfPorts()
{
return $this->hasMany(\App\Models\OspfPort::class, 'device_id');
}
public function netscalerVservers()
{

View File

@@ -99,5 +99,7 @@ return [
'VMWARE-VMINFO-MIB::vmwVmPoweredOn' => \LibreNMS\Snmptrap\Handlers\VmwVmPoweredOn::class,
'VMWARE-VMINFO-MIB::vmwVmPoweredOff' => \LibreNMS\Snmptrap\Handlers\VmwVmPoweredOff::class,
'VMWARE-VMINFO-MIB::vmwVmSuspended' => \LibreNMS\Snmptrap\Handlers\VmwVmSuspended::class,
'OSPF-TRAP-MIB::ospfIfStateChange' => \LibreNMS\Snmptrap\Handlers\OspfIfStateChange::class,
'OSPF-TRAP-MIB::ospfNbrStateChange' => \LibreNMS\Snmptrap\Handlers\OspfNbrStateChange::class,
]
];

View File

@@ -132,6 +132,32 @@ $factory->define(\App\Models\Vminfo::class, function (Faker\Generator $faker) {
];
});
$factory->define(\App\Models\OspfNbr::class, function (Faker\Generator $faker) {
return [
'id' => $faker->randomDigit,
'ospfNbrIpAddr' => $faker->ipv4,
'ospfNbrAddressLessIndex' => $faker->randomDigit,
'ospfNbrRtrId' => $faker->ipv4,
'ospfNbrOptions' => 0,
'ospfNbrPriority' => 1,
'ospfNbrEvents' => $faker->randomDigit,
'ospfNbrLsRetransQLen' => 0,
'ospfNbmaNbrStatus' => 'active',
'ospfNbmaNbrPermanence' => 'dynamic',
'ospfNbrHelloSuppressed' => 'false',
];
});
$factory->define(\App\Models\OspfPort::class, function (Faker\Generator $faker) {
return [
'id' => $faker->randomDigit,
'ospf_port_id' => $faker->randomDigit,
'ospfIfIpAddress' => $faker->ipv4,
'ospfAddressLessIf' => $faker->randomDigit,
'ospfIfAreaId' => '0.0.0.0',
];
});
$factory->define(\App\Models\Component::class, function (Faker\Generator $faker) {
return [
'device_id' => $faker->randomDigit,

View File

@@ -0,0 +1,271 @@
<?php
/**
* OspfIfStateChangeTest.php
*
* -Description-
*
* Unit test for the OspfIfStateChange SNMP trap handler. Will verify
* trap is properly logged and ospf_ports.ospfIfState is updated in the
* database.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2020 KanREN, Inc
* @author Heath Barnhart <hbarnhart@kanren.net>
*/
namespace LibreNMS\Tests\Feature\SnmpTraps;
use App\Models\Device;
use App\Models\OspfPort;
use App\Models\Port;
use LibreNMS\Snmptrap\Dispatcher;
use LibreNMS\Snmptrap\Trap;
use Log;
class OspfIfStateChangeTest extends SnmpTrapTestCase
{
//Test OSPF interface state down
public function testOspfIfDown()
{
$device = factory(Device::class)->create();
$port = factory(Port::class)->make(['ifAdminStatus' => 'up', 'ifOperStatus' => 'up']);
$device->ports()->save($port);
$ospfIf = factory(OspfPort::class)->make(['port_id' => $port->port_id, 'ospfIfState' => 'designatedRouter']);
$device->ospfPorts()->save($ospfIf);
$trapText = "$device->hostname
UDP: [$device->ip]:57602->[192.168.5.5]:162
DISMAN-EVENT-MIB::sysUpTimeInstance 0:6:11:31.55
SNMPv2-MIB::snmpTrapOID.0 OSPF-TRAP-MIB::ospfIfStateChange
OSPF-MIB::ospfRouterId.0 $device->ip
OSPF-MIB::ospfIfIpAddress.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfIfIpAddress
OSPF-MIB::ospfAddressLessIf.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfAddressLessIf
OSPF-MIB::ospfIfState.$ospfIf->ospfIfIpAddress.0 down
SNMPv2-MIB::snmpTrapEnterprise.0 JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameSRX240";
$trap = new Trap($trapText);
$message = "OSPF interface $port->ifName is down";
\Log::shouldReceive('event')->once()->with($message, $device->device_id, 'trap', 5);
$this->assertTrue(Dispatcher::handle($trap), 'Could not handle ospfIfStateChange down');
$ospfIf = $ospfIf->fresh();
$this->assertEquals($ospfIf->ospfIfState, 'down');
}
//Test OSPF interface state DesignatedRouter
public function testOspfIfDr()
{
$device = factory(Device::class)->create();
$port = factory(Port::class)->make(['ifAdminStatus' => 'up', 'ifOperStatus' => 'up']);
$device->ports()->save($port);
$ospfIf = factory(OspfPort::class)->make(['port_id' => $port->port_id, 'ospfIfState' => 'down']);
$device->ospfPorts()->save($ospfIf);
$trapText = "$device->hostname
UDP: [$device->ip]:57602->[192.168.5.5]:162
DISMAN-EVENT-MIB::sysUpTimeInstance 0:6:11:31.55
SNMPv2-MIB::snmpTrapOID.0 OSPF-TRAP-MIB::ospfIfStateChange
OSPF-MIB::ospfRouterId.0 $device->ip
OSPF-MIB::ospfIfIpAddress.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfIfIpAddress
OSPF-MIB::ospfAddressLessIf.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfAddressLessIf
OSPF-MIB::ospfIfState.$ospfIf->ospfIfIpAddress.0 designatedRouter
SNMPv2-MIB::snmpTrapEnterprise.0 JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameSRX240";
$trap = new Trap($trapText);
$message = "OSPF interface $port->ifName is designatedRouter";
\Log::shouldReceive('event')->once()->with($message, $device->device_id, 'trap', 1);
$this->assertTrue(Dispatcher::handle($trap), 'Could not handle ospfIfStateChange designatedRouter');
$ospfIf = $ospfIf->fresh();
$this->assertEquals($ospfIf->ospfIfState, 'designatedRouter');
}
//Test OSPF interface state backupDesignatedRouter
public function testOspfIfBdr()
{
$device = factory(Device::class)->create();
$port = factory(Port::class)->make(['ifAdminStatus' => 'up', 'ifOperStatus' => 'up']);
$device->ports()->save($port);
$ospfIf = factory(OspfPort::class)->make(['port_id' => $port->port_id, 'ospfIfState' => 'down']);
$device->ospfPorts()->save($ospfIf);
$trapText = "$device->hostname
UDP: [$device->ip]:57602->[192.168.5.5]:162
DISMAN-EVENT-MIB::sysUpTimeInstance 0:6:11:31.55
SNMPv2-MIB::snmpTrapOID.0 OSPF-TRAP-MIB::ospfIfStateChange
OSPF-MIB::ospfRouterId.0 $device->ip
OSPF-MIB::ospfIfIpAddress.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfIfIpAddress
OSPF-MIB::ospfAddressLessIf.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfAddressLessIf
OSPF-MIB::ospfIfState.$ospfIf->ospfIfIpAddress.0 backupDesignatedRouter
SNMPv2-MIB::snmpTrapEnterprise.0 JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameSRX240";
$trap = new Trap($trapText);
$message = "OSPF interface $port->ifName is backupDesignatedRouter";
\Log::shouldReceive('event')->once()->with($message, $device->device_id, 'trap', 1);
$this->assertTrue(Dispatcher::handle($trap), 'Could not handle ospfIfStateChange backupDesignatedRouter');
$ospfIf = $ospfIf->fresh();
$this->assertEquals($ospfIf->ospfIfState, 'backupDesignatedRouter');
}
//Test OSPF interface state otherDesignatedRouter
public function testOspfIfOdr()
{
$device = factory(Device::class)->create();
$port = factory(Port::class)->make(['ifAdminStatus' => 'up', 'ifOperStatus' => 'up']);
$device->ports()->save($port);
$ospfIf = factory(OspfPort::class)->make(['port_id' => $port->port_id, 'ospfIfState' => 'down']);
$device->ospfPorts()->save($ospfIf);
$trapText = "$device->hostname
UDP: [$device->ip]:57602->[192.168.5.5]:162
DISMAN-EVENT-MIB::sysUpTimeInstance 0:6:11:31.55
SNMPv2-MIB::snmpTrapOID.0 OSPF-TRAP-MIB::ospfIfStateChange
OSPF-MIB::ospfRouterId.0 $device->ip
OSPF-MIB::ospfIfIpAddress.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfIfIpAddress
OSPF-MIB::ospfAddressLessIf.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfAddressLessIf
OSPF-MIB::ospfIfState.$ospfIf->ospfIfIpAddress.0 otherDesignatedRouter
SNMPv2-MIB::snmpTrapEnterprise.0 JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameSRX240";
$trap = new Trap($trapText);
$message = "OSPF interface $port->ifName is otherDesignatedRouter";
\Log::shouldReceive('event')->once()->with($message, $device->device_id, 'trap', 1);
$this->assertTrue(Dispatcher::handle($trap), 'Could not handle ospfIfStateChange otherDesignatedRouter');
$ospfIf = $ospfIf->fresh();
$this->assertEquals($ospfIf->ospfIfState, 'otherDesignatedRouter');
}
//Test OSPF interface state pointToPoint
public function testOspfIfPtp()
{
$device = factory(Device::class)->create();
$port = factory(Port::class)->make(['ifAdminStatus' => 'up', 'ifOperStatus' => 'up']);
$device->ports()->save($port);
$ospfIf = factory(OspfPort::class)->make(['port_id' => $port->port_id, 'ospfIfState' => 'down']);
$device->ospfPorts()->save($ospfIf);
$trapText = "$device->hostname
UDP: [$device->ip]:57602->[192.168.5.5]:162
DISMAN-EVENT-MIB::sysUpTimeInstance 0:6:11:31.55
SNMPv2-MIB::snmpTrapOID.0 OSPF-TRAP-MIB::ospfIfStateChange
OSPF-MIB::ospfRouterId.0 $device->ip
OSPF-MIB::ospfIfIpAddress.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfIfIpAddress
OSPF-MIB::ospfAddressLessIf.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfAddressLessIf
OSPF-MIB::ospfIfState.$ospfIf->ospfIfIpAddress.0 pointToPoint
SNMPv2-MIB::snmpTrapEnterprise.0 JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameSRX240";
$trap = new Trap($trapText);
$message = "OSPF interface $port->ifName is pointToPoint";
\Log::shouldReceive('event')->once()->with($message, $device->device_id, 'trap', 1);
$this->assertTrue(Dispatcher::handle($trap), 'Could not handle ospfIfStateChange pointToPoint');
$ospfIf = $ospfIf->fresh();
$this->assertEquals($ospfIf->ospfIfState, 'pointToPoint');
}
//Test OSPF interface state waiting
public function testOspfIfWait()
{
$device = factory(Device::class)->create();
$port = factory(Port::class)->make(['ifAdminStatus' => 'up', 'ifOperStatus' => 'up']);
$device->ports()->save($port);
$ospfIf = factory(OspfPort::class)->make(['port_id' => $port->port_id, 'ospfIfState' => 'designatedRouter']);
$device->ospfPorts()->save($ospfIf);
$trapText = "$device->hostname
UDP: [$device->ip]:57602->[192.168.5.5]:162
DISMAN-EVENT-MIB::sysUpTimeInstance 0:6:11:31.55
SNMPv2-MIB::snmpTrapOID.0 OSPF-TRAP-MIB::ospfIfStateChange
OSPF-MIB::ospfRouterId.0 $device->ip
OSPF-MIB::ospfIfIpAddress.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfIfIpAddress
OSPF-MIB::ospfAddressLessIf.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfAddressLessIf
OSPF-MIB::ospfIfState.$ospfIf->ospfIfIpAddress.0 waiting
SNMPv2-MIB::snmpTrapEnterprise.0 JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameSRX240";
$trap = new Trap($trapText);
$message = "OSPF interface $port->ifName is waiting";
\Log::shouldReceive('event')->once()->with($message, $device->device_id, 'trap', 4);
$this->assertTrue(Dispatcher::handle($trap), 'Could not handle ospfIfStateChange waiting');
$ospfIf = $ospfIf->fresh();
$this->assertEquals($ospfIf->ospfIfState, 'waiting');
}
//Test OSPF interface state loopback
public function testOspfIfLoop()
{
$device = factory(Device::class)->create();
$port = factory(Port::class)->make(['ifAdminStatus' => 'up', 'ifOperStatus' => 'up']);
$device->ports()->save($port);
$ospfIf = factory(OspfPort::class)->make(['port_id' => $port->port_id, 'ospfIfState' => 'designatedRouter']);
$device->ospfPorts()->save($ospfIf);
$trapText = "$device->hostname
UDP: [$device->ip]:57602->[192.168.5.5]:162
DISMAN-EVENT-MIB::sysUpTimeInstance 0:6:11:31.55
SNMPv2-MIB::snmpTrapOID.0 OSPF-TRAP-MIB::ospfIfStateChange
OSPF-MIB::ospfRouterId.0 $device->ip
OSPF-MIB::ospfIfIpAddress.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfIfIpAddress
OSPF-MIB::ospfAddressLessIf.$ospfIf->ospfIfIpAddress.0 $ospfIf->ospfAddressLessIf
OSPF-MIB::ospfIfState.$ospfIf->ospfIfIpAddress.0 loopback
SNMPv2-MIB::snmpTrapEnterprise.0 JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameSRX240";
$trap = new Trap($trapText);
$message = "OSPF interface $port->ifName is loopback";
\Log::shouldReceive('event')->once()->with($message, $device->device_id, 'trap', 4);
$this->assertTrue(Dispatcher::handle($trap), 'Could not handle ospfIfStateChange loopback');
$ospfIf = $ospfIf->fresh();
$this->assertEquals($ospfIf->ospfIfState, 'loopback');
}
}

View File

@@ -0,0 +1,135 @@
<?php
/**
* OspfNbrStateChangeTest.php
*
* -Description-
*
* Unit test for the OspfNbStateChange SNMP trap handler. Will verify
* trap is properly logged and ospf_nbrs.ospfNbrState is updated in the
* database.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2020 KanREN, Inc
* @author Heath Barnhart <hbarnhart@kanren.net>
*/
namespace LibreNMS\Tests\Feature\SnmpTraps;
use App\Models\Device;
use App\Models\OspfNbr;
use LibreNMS\Snmptrap\Dispatcher;
use LibreNMS\Snmptrap\Trap;
use Log;
class OspfNbrStateChangeTest extends SnmpTrapTestCase
{
//Test OSPF neighbor state down trap
public function testOspfNbrDown()
{
$device = factory(Device::class)->create();
$ospfNbr = factory(OspfNbr::class)->make(['device_id' => $device->device_id, 'ospfNbrState' => 'full']);
$ospfNbr->ospf_nbr_id = "$ospfNbr->ospfNbrIpAddr.$ospfNbr->ospfNbrAddressLessIndex";
$device->ospfNbrs()->save($ospfNbr);
$trapText = "$device->hostname
UDP: [$device->ip]:57602->[192.168.5.5]:162
DISMAN-EVENT-MIB::sysUpTimeInstance 0:1:07:16.06
SNMPv2-MIB::snmpTrapOID.0 OSPF-TRAP-MIB::ospfNbrStateChange
OSPF-MIB::ospfRouterId.0 $device->ip
OSPF-MIB::ospfNbrIpAddr.$ospfNbr->ospf_nbr_id $ospfNbr->ospfNbrIpAddr
OSPF-MIB::ospfNbrAddressLessIndex.$ospfNbr->ospf_nbr_id $ospfNbr->ospfNbrAddressLessIndex
OSPF-MIB::ospfNbrRtrId.$ospfNbr->ospf_nbr_id $ospfNbr->ospfNbrRtrId
OSPF-MIB::ospfNbrState.$ospfNbr->ospf_nbr_id down
SNMPv2-MIB::snmpTrapEnterprise.0 JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameSRX240 ";
$trap = new Trap($trapText);
$message = "OSPF neighbor $ospfNbr->ospfNbrRtrId changed state to down";
\Log::shouldReceive('event')->once()->with($message, $device->device_id, 'trap', 5);
$this->assertTrue(Dispatcher::handle($trap), 'Could not handle ospfNbrStateChange down');
$ospfNbr = $ospfNbr->fresh();
$this->assertEquals($ospfNbr->ospfNbrState, 'down');
}
//Test OSPF neighbor state full trap
public function testOspfNbrFull()
{
$device = factory(Device::class)->create();
$ospfNbr = factory(OspfNbr::class)->make(['device_id' => $device->device_id, 'ospfNbrState' => 'down']);
$ospfNbr->ospf_nbr_id = "$ospfNbr->ospfNbrIpAddr.$ospfNbr->ospfNbrAddressLessIndex";
$device->ospfNbrs()->save($ospfNbr);
$trapText = "$device->hostname
UDP: [$device->ip]:57602->[192.168.5.5]:162
DISMAN-EVENT-MIB::sysUpTimeInstance 0:1:07:16.06
SNMPv2-MIB::snmpTrapOID.0 OSPF-TRAP-MIB::ospfNbrStateChange
OSPF-MIB::ospfRouterId.0 $device->ip
OSPF-MIB::ospfNbrIpAddr.$ospfNbr->ospf_nbr_id $ospfNbr->ospfNbrIpAddr
OSPF-MIB::ospfNbrAddressLessIndex.$ospfNbr->ospf_nbr_id $ospfNbr->ospfNbrAddressLessIndex
OSPF-MIB::ospfNbrRtrId.$ospfNbr->ospf_nbr_id $ospfNbr->ospfNbrRtrId
OSPF-MIB::ospfNbrState.$ospfNbr->ospf_nbr_id full
SNMPv2-MIB::snmpTrapEnterprise.0 JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameSRX240 ";
$trap = new Trap($trapText);
$message = "OSPF neighbor $ospfNbr->ospfNbrRtrId changed state to full";
\Log::shouldReceive('event')->once()->with($message, $device->device_id, 'trap', 1);
$this->assertTrue(Dispatcher::handle($trap), 'Could not handle ospfNbrStateChange full');
$ospfNbr = $ospfNbr->fresh();
$this->assertEquals($ospfNbr->ospfNbrState, 'full');
}
//Test OSPF neighbor state trap any other state
public function testOspfNbrOther()
{
$device = factory(Device::class)->create();
$ospfNbr = factory(OspfNbr::class)->make(['device_id' => $device->device_id, 'ospfNbrState' => 'full']);
$ospfNbr->ospf_nbr_id = "$ospfNbr->ospfNbrIpAddr.$ospfNbr->ospfNbrAddressLessIndex";
$device->ospfNbrs()->save($ospfNbr);
$trapText = "$device->hostname
UDP: [$device->ip]:57602->[192.168.5.5]:162
DISMAN-EVENT-MIB::sysUpTimeInstance 0:1:07:16.06
SNMPv2-MIB::snmpTrapOID.0 OSPF-TRAP-MIB::ospfNbrStateChange
OSPF-MIB::ospfRouterId.0 $device->ip
OSPF-MIB::ospfNbrIpAddr.$ospfNbr->ospf_nbr_id $ospfNbr->ospfNbrIpAddr
OSPF-MIB::ospfNbrAddressLessIndex.$ospfNbr->ospf_nbr_id $ospfNbr->ospfNbrAddressLessIndex
OSPF-MIB::ospfNbrRtrId.$ospfNbr->ospf_nbr_id $ospfNbr->ospfNbrRtrId
OSPF-MIB::ospfNbrState.$ospfNbr->ospf_nbr_id exstart
SNMPv2-MIB::snmpTrapEnterprise.0 JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameSRX240 ";
$trap = new Trap($trapText);
$message = "OSPF neighbor $ospfNbr->ospfNbrRtrId changed state to exstart";
\Log::shouldReceive('event')->once()->with($message, $device->device_id, 'trap', 4);
$this->assertTrue(Dispatcher::handle($trap), 'Could not handle ospfNbrStateChange exstart');
$ospfNbr = $ospfNbr->fresh();
$this->assertEquals($ospfNbr->ospfNbrState, 'exstart');
}
}