New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add basic STP/RSTP support #2690
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php | ||
|
||
$stp_raw = dbFetchRow('SELECT * FROM `stp` WHERE `device_id` = ?', array($device['device_id'])); | ||
$stp = array ( | ||
'Root bridge' => ($stp_raw['rootBridge'] == 1) ? 'Yes' : 'No', | ||
'Bridge address (MAC)' => $stp_raw['bridgeAddress'], | ||
'Protocol specification' => $stp_raw['protocolSpecification'], | ||
'Priority (0-61440)' => $stp_raw['priority'], | ||
'Time since topology change' => formatUptime($stp_raw['timeSinceTopologyChange']), | ||
'Topology changes' => $stp_raw['topChanges'], | ||
'Designated root (MAC)' => $stp_raw['designatedRoot'], | ||
'Root cost' => $stp_raw['rootCost'], | ||
'Root port' => $stp_raw['rootPort'], | ||
'Max age (s)' => $stp_raw['maxAge'], | ||
'Hello time (s)' => $stp_raw['helloTime'], | ||
'Hold time (s)' => $stp_raw['holdTime'], | ||
'Forward delay (s)' => $stp_raw['forwardDelay'], | ||
'Bridge max age (s)' => $stp_raw['bridgeMaxAge'], | ||
'Bridge hello time (s)' => $stp_raw['bridgeHelloTime'], | ||
'Bridge forward delay (s)' => $stp_raw['bridgeForwardDelay'] | ||
); | ||
foreach (array_keys($stp) as $key) { | ||
echo " | ||
<tr> | ||
<td width=280 class=list-large>$key</td> | ||
<td class=box-desc>$stp[$key]</td> | ||
</tr> | ||
"; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -229,6 +229,14 @@ | |
</a> | ||
</li>'); | ||
|
||
if (@dbFetchCell("SELECT COUNT(stp_id) FROM stp WHERE device_id = '".$device['device_id']."'") > '0') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should do not count all the id entries, because of "WHERE device_id =" statement in the query. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well it boils down to: If so, the count is more excessive than just a boolean check whether something is there or not. On 8 January 2016 10:52:39 CET, Vitali Kari notifications@github.com wrote:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that we would have more then one STP/RSTP instance per device, maybe in VRF. |
||
echo '<li class="'.$select['stp'].'"> | ||
<a href="'.generate_device_url($device, array('tab' => 'stp')).'"> | ||
<img src="images/16/chart_organisation.png" align="absmiddle" border="0" /> STP | ||
</a> | ||
</li>'; | ||
} | ||
|
||
if (@dbFetchCell("SELECT COUNT(*) FROM `packages` WHERE device_id = '".$device['device_id']."'") > '0') { | ||
echo '<li class="'.$select['packages'].'"> | ||
<a href="'.generate_device_url($device, array('tab' => 'packages')).'"> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?php | ||
|
||
$link_array = array( | ||
'page' => 'device', | ||
'device' => $device['device_id'], | ||
'tab' => 'stp', | ||
); | ||
|
||
print_optionbar_start(); | ||
|
||
echo "<span style='font-weight: bold;'>STP</span> » "; | ||
|
||
if (!$vars['view']) { | ||
$vars['view'] = 'basic'; | ||
} | ||
|
||
$menu_options['basic'] = 'Basic'; | ||
// $menu_options['details'] = 'Details'; | ||
$sep = ''; | ||
foreach ($menu_options as $option => $text) { | ||
echo $sep; | ||
if ($vars['view'] == $option) { | ||
echo "<span class='pagemenu-selected'>"; | ||
} | ||
|
||
echo generate_link($text, $link_array, array('view' => $option)); | ||
if ($vars['view'] == $option) { | ||
echo '</span>'; | ||
} | ||
|
||
$sep = ' | '; | ||
} | ||
|
||
unset($sep); | ||
|
||
print_optionbar_end(); | ||
|
||
echo '<table border="0" cellspacing="0" cellpadding="5" width="100%">'; | ||
|
||
$i = '1'; | ||
|
||
foreach (dbFetchRows("SELECT * FROM `stp` WHERE `device_id` = ? ORDER BY 'stp_id'", array($device['device_id'])) as $stp) { | ||
include 'includes/print-stp.inc.php'; | ||
|
||
$i++; | ||
} | ||
|
||
echo '</table>'; | ||
|
||
$pagetitle[] = 'STP'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
<?php | ||
/* | ||
* LibreNMS | ||
* | ||
* Copyright (c) 2015 Vitali Kari <vitali.kari@gmail.com> | ||
* | ||
* 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. Please see LICENSE.txt at the top level of | ||
* the source code distribution for details. | ||
* | ||
* Based on IEEE-802.1D-2004, (STP, RSTP) | ||
* needs RSTP-MIB | ||
*/ | ||
|
||
echo "Spanning Tree: "; | ||
|
||
// Pre-cache existing state of STP for this device from database | ||
$stp_db = dbFetchRow('SELECT * FROM `stp` WHERE `device_id` = ?', array($device['device_id'])); | ||
//d_echo($stp_db); | ||
|
||
$stpprotocol = snmp_get($device, 'dot1dStpProtocolSpecification.0', '-Oqv', 'RSTP-MIB'); | ||
|
||
// FIXME I don't know what "unknown" means, perhaps MSTP? (saw it on some cisco devices) | ||
// But we can try to retrieve data | ||
if ($stpprotocol == 'ieee8021d' || $stpprotocol == 'unknown') { | ||
|
||
// set time multiplier to convert from centiseconds to seconds | ||
// all time values are stored in databese as seconds | ||
$tm = '0.01'; | ||
// some vendors like PBN dont follow the 802.1D implementation and use seconds in SNMP | ||
if ($device['os'] == 'pbn') { | ||
preg_match('/^.* Build (?<build>\d+)/', $device['version'], $version); | ||
if ($version[build] <= 16607) { // Buggy version :-( | ||
$tm = '1'; | ||
} | ||
} | ||
|
||
// read the 802.1D subtree | ||
$stp_raw = snmpwalk_cache_oid($device, 'dot1dStp', array(), 'RSTP-MIB'); | ||
$stp = array( | ||
'protocolSpecification' => $stp_raw[0]['dot1dStpProtocolSpecification'], | ||
'priority' => $stp_raw[0]['dot1dStpPriority'], | ||
'topChanges' => $stp_raw[0]['dot1dStpTopChanges'], | ||
'rootCost' => $stp_raw[0]['dot1dStpRootCost'], | ||
'rootPort' => $stp_raw[0]['dot1dStpRootPort'], | ||
'maxAge' => $stp_raw[0]['dot1dStpMaxAge'] * $tm, | ||
'helloTime' => $stp_raw[0]['dot1dStpHelloTime'] * $tm, | ||
'holdTime' => $stp_raw[0]['dot1dStpHoldTime'] * $tm, | ||
'forwardDelay' => $stp_raw[0]['dot1dStpForwardDelay'] * $tm, | ||
'bridgeMaxAge' => $stp_raw[0]['dot1dStpBridgeMaxAge'] * $tm, | ||
'bridgeHelloTime' => $stp_raw[0]['dot1dStpBridgeHelloTime'] * $tm, | ||
'bridgeForwardDelay' => $stp_raw[0]['dot1dStpBridgeForwardDelay'] * $tm | ||
); | ||
|
||
// set device binding | ||
$stp['device_id'] = $device['device_id']; | ||
|
||
// read the 802.1D bridge address and set as MAC in database | ||
$mac_raw = snmp_get($device, 'dot1dBaseBridgeAddress.0', '-Oqv', 'RSTP-MIB'); | ||
|
||
// read Time as timetics (in hundredths of a seconds) since last topology change and convert to seconds | ||
$time_since_change = snmp_get($device, 'dot1dStpTimeSinceTopologyChange.0', '-Ovt', 'RSTP-MIB'); | ||
if ($time_since_change > '100') { | ||
$time_since_change = substr($time_since_change, 0, -2); // convert to seconds since change | ||
} | ||
else { | ||
$time_since_change = '0'; | ||
} | ||
$stp['timeSinceTopologyChange'] = $time_since_change; | ||
|
||
// designated root is stored in format 2 octet bridge priority + MAC address, so we need to normalize it | ||
$dr = str_replace(array(' ', ':', '-'), '', strtolower($stp_raw[0]['dot1dStpDesignatedRoot'])); | ||
$dr = substr($dr, -12); //remove first two octets | ||
$stp['designatedRoot'] = $dr; | ||
|
||
// normalize the MAC | ||
$mac_array = explode(':', $mac_raw); | ||
foreach($mac_array as &$octet) { | ||
if (strlen($octet) < 2) { | ||
$octet = "0" . $octet; // add suppressed 0 | ||
} | ||
} | ||
$stp['bridgeAddress'] = implode($mac_array); | ||
|
||
// I'm the boss? | ||
if ($stp['bridgeAddress'] == $stp['designatedRoot']) { | ||
$stp['rootBridge'] = '1'; | ||
} | ||
else { | ||
$stp['rootBridge'] = '0'; | ||
} | ||
|
||
d_echo($stp); | ||
|
||
if ($stp_raw[0]['version'] == '3') { | ||
echo "RSTP "; | ||
} | ||
else { | ||
echo "STP "; | ||
} | ||
|
||
if (!$stp_db['bridgeAddress'] && $stp['bridgeAddress']) { | ||
dbInsert($stp,'stp'); | ||
log_event('STP added, bridge address: '.$stp['bridgeAddress'], $device, 'stp'); | ||
echo '+'; | ||
} | ||
|
||
if ($stp_db['bridgeAddress'] && !$stp['bridgeAddress']) { | ||
dbDelete('stp','device_id = ?', array($device['device_id'])); | ||
log_event('STP removed', $device, 'stp'); | ||
echo '-'; | ||
} | ||
} | ||
|
||
unset($stp_raw, $stp, $stp_db); | ||
echo "\n"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
<?php | ||
/* | ||
* LibreNMS | ||
* | ||
* Copyright (c) 2015 Vitali Kari <vitali.kari@gmail.com> | ||
* | ||
* 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. Please see LICENSE.txt at the top level of | ||
* the source code distribution for details. | ||
* | ||
* Based on IEEE-802.1D-2004, (STP, RSTP) | ||
* needs RSTP-MIB | ||
*/ | ||
|
||
echo "Spanning Tree: "; | ||
|
||
// Pre-cache existing state of STP for this device from database | ||
$stp_db = dbFetchRow('SELECT * FROM `stp` WHERE `device_id` = ?', array($device['device_id'])); | ||
//d_echo($stp_db); | ||
|
||
$stpprotocol = snmp_get($device, 'dot1dStpProtocolSpecification.0', '-Oqv', 'RSTP-MIB'); | ||
|
||
// FIXME I don't know what "unknown" means, perhaps MSTP? (saw it on some cisco devices) | ||
// But we can try to retrieve data | ||
if ($stpprotocol == 'ieee8021d' || $stpprotocol == 'unknown') { | ||
|
||
// set time multiplier to convert from centiseconds to seconds | ||
// all time values are stored in databese as seconds | ||
$tm = '0.01'; | ||
// some vendors like PBN dont follow the 802.1D implementation and use seconds in SNMP | ||
if ($device['os'] == 'pbn') { | ||
preg_match('/^.* Build (?<build>\d+)/', $device['version'], $version); | ||
if ($version[build] <= 16607) { // Buggy version :-( | ||
$tm = '1'; | ||
} | ||
} | ||
|
||
// read the 802.1D subtree | ||
$stp_raw = snmpwalk_cache_oid($device, 'dot1dStp', array(), 'RSTP-MIB'); | ||
$stp = array( | ||
'protocolSpecification' => $stp_raw[0]['dot1dStpProtocolSpecification'], | ||
'priority' => $stp_raw[0]['dot1dStpPriority'], | ||
'topChanges' => $stp_raw[0]['dot1dStpTopChanges'], | ||
'rootCost' => $stp_raw[0]['dot1dStpRootCost'], | ||
'rootPort' => $stp_raw[0]['dot1dStpRootPort'], | ||
'maxAge' => $stp_raw[0]['dot1dStpMaxAge'] * $tm, | ||
'helloTime' => $stp_raw[0]['dot1dStpHelloTime'] * $tm, | ||
'holdTime' => $stp_raw[0]['dot1dStpHoldTime'] * $tm, | ||
'forwardDelay' => $stp_raw[0]['dot1dStpForwardDelay'] * $tm, | ||
'bridgeMaxAge' => $stp_raw[0]['dot1dStpBridgeMaxAge'] * $tm, | ||
'bridgeHelloTime' => $stp_raw[0]['dot1dStpBridgeHelloTime'] * $tm, | ||
'bridgeForwardDelay' => $stp_raw[0]['dot1dStpBridgeForwardDelay'] * $tm | ||
); | ||
|
||
// set device binding | ||
$stp['device_id'] = $device['device_id']; | ||
|
||
// read the 802.1D bridge address and set as MAC in database | ||
$mac_raw = snmp_get($device, 'dot1dBaseBridgeAddress.0', '-Oqv', 'RSTP-MIB'); | ||
|
||
// read Time as timetics (in hundredths of a seconds) since last topology change and convert to seconds | ||
$time_since_change = snmp_get($device, 'dot1dStpTimeSinceTopologyChange.0', '-Ovt', 'RSTP-MIB'); | ||
if ($time_since_change > '100') { | ||
$time_since_change = substr($time_since_change, 0, -2); // convert to seconds since change | ||
} | ||
else { | ||
$time_since_change = '0'; | ||
} | ||
$stp['timeSinceTopologyChange'] = $time_since_change; | ||
|
||
// designated root is stored in format 2 octet bridge priority + MAC address, so we need to normalize it | ||
$dr = str_replace(array(' ', ':', '-'), '', strtolower($stp_raw[0]['dot1dStpDesignatedRoot'])); | ||
$dr = substr($dr, -12); //remove first two octets | ||
$stp['designatedRoot'] = $dr; | ||
|
||
// normalize the MAC | ||
$mac_array = explode(':', $mac_raw); | ||
foreach($mac_array as &$octet) { | ||
if (strlen($octet) < 2) { | ||
$octet = "0" . $octet; // add suppressed 0 | ||
} | ||
} | ||
$stp['bridgeAddress'] = implode($mac_array); | ||
|
||
// I'm the boss? | ||
if ($stp['bridgeAddress'] == $stp['designatedRoot']) { | ||
$stp['rootBridge'] = '1'; | ||
} | ||
else { | ||
$stp['rootBridge'] = '0'; | ||
} | ||
|
||
d_echo($stp); | ||
|
||
if ($stp_db['bridgeAddress'] && $stp['bridgeAddress']) { | ||
// Logging if designated root changed since last db update | ||
if ($stp_db['designatedRoot'] != $stp['designatedRoot']) { | ||
log_event('STP designated root changed: '.$stp_db['designatedRoot'].' > '.$stp['designatedRoot'], $device, 'stp'); | ||
} | ||
|
||
// Logging if designated root port changed since last db update | ||
if ($stp_db['rootPort'] != $stp['rootPort']) { | ||
log_event('STP root port changed: '.$stp_db['rootPort'].' > '.$stp['rootPort'], $device, 'stp'); | ||
} | ||
|
||
// Logging if topology changed since last db update | ||
if ($stp_db['timeSinceTopologyChange'] > $stp['timeSinceTopologyChange']) { | ||
// FIXME log_event should log really changing time, not polling time | ||
// but upstream function do not care about this at the moment. | ||
// | ||
// saw same problem with this line librenms/includes/polling/system.inc.php | ||
// log_event('Device rebooted after '.formatUptime($device['uptime']), $device, 'reboot', $device['uptime']); | ||
// ToDo fix log_event() | ||
// | ||
//log_event('STP topology changed after: '.formatUptime($stp['timeSinceTopologyChange']), $device, 'stp', $stp['timeSinceTopologyChange']); | ||
log_event('STP topology changed after: '.formatUptime($stp['timeSinceTopologyChange']), $device, 'stp'); | ||
} | ||
// Write to db | ||
dbUpdate($stp,'stp','device_id = ?', array($device['device_id'])); | ||
echo '.'; | ||
} | ||
} | ||
|
||
unset($stp_raw, $stp, $stp_db); | ||
echo "\n"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
CREATE TABLE IF NOT EXISTS `stp` ( | ||
`stp_id` int(11) NOT NULL, | ||
`device_id` int(11) NOT NULL, | ||
`rootBridge` tinyint(1) NOT NULL, | ||
`bridgeAddress` varchar(32) NOT NULL, | ||
`protocolSpecification` varchar(16) NOT NULL, | ||
`priority` mediumint(9) NOT NULL, | ||
`timeSinceTopologyChange` varchar(32) NOT NULL, | ||
`topChanges` mediumint(9) NOT NULL, | ||
`designatedRoot` varchar(32) NOT NULL, | ||
`rootCost` mediumint(9) NOT NULL, | ||
`rootPort` mediumint(9) NOT NULL, | ||
`maxAge` mediumint(9) NOT NULL, | ||
`helloTime` mediumint(9) NOT NULL, | ||
`holdTime` mediumint(9) NOT NULL, | ||
`forwardDelay` mediumint(9) NOT NULL, | ||
`bridgeMaxAge` smallint(6) NOT NULL, | ||
`bridgeHelloTime` smallint(6) NOT NULL, | ||
`bridgeForwardDelay` smallint(6) NOT NULL | ||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; | ||
|
||
ALTER TABLE `stp` | ||
ADD PRIMARY KEY (`stp_id`), ADD KEY `stp_host` (`device_id`); | ||
|
||
ALTER TABLE `stp` | ||
MODIFY `stp_id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=1; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to visually align the => in this section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Paulgear, thank you for hint!