Cisco CBWFQ support
Moderators: Developers, Moderators
Cisco CBWFQ support
One template with underlying support that I would love to see is support for Cisco's Classed Based Queueing. Being able to graph an interface separated by classes would really aid in being able to determine how close one would be getting to oversubscribing a particular class off traffic. I think that the underlying problem is that the index number change per configuration change so simply mapping the OUI values statically would not work.
I've done some work internally developing query tools to get the data, and I've seen this tool on sourceforge
http://sourceforge.net/docman/display_d ... p_id=25401
Has any thought been placed on adding this?
I've done some work internally developing query tools to get the data, and I've seen this tool on sourceforge
http://sourceforge.net/docman/display_d ... p_id=25401
Has any thought been placed on adding this?
Sorry i forgot to login. It's me, JannoAnonymous wrote:I am also looking for some solution but have done almost no testing till now. I think i will be writing some extension of sourceforge script and maybe modify it into cacty templates. What index is changing after each config change and what did you find out more testing with the cbwfq mib.
I have been programming a bit and it seems i can find and combine all info for cbwfq with snmp. But i wonder if i can get it working with Cacti. It is mabe my lack of cacti knowledge but the data which should be reported is so diverse that i think a simple data query will not work.
For every (sub)interface where qos is enabled i get a service policy
For every service policy i get some classes
For every class i get a bunch of policy's (shaping/queuing etc)
Depending in the type of policy i need different data collection and graph's
I think i can not do this with Cacti. Just using plain rrd will work but i would rather use Cacti.
Maybe i can do the opposite. Make different templates for every type of policy. Like a template for queuing, another template for policying etc. But this would mean a lot of work configuring a device and it would not be logical for the user. A user will always think starting from interfaces->service-policy->classes->policies->counters
In fact, all above is more complicated because it is also possible to use hiarchical service-policy's. Maybe it is possible to setup all measurements/graphs outside of cacti but managing them from whithin cacti.
To be continued...
For every (sub)interface where qos is enabled i get a service policy
For every service policy i get some classes
For every class i get a bunch of policy's (shaping/queuing etc)
Depending in the type of policy i need different data collection and graph's
I think i can not do this with Cacti. Just using plain rrd will work but i would rather use Cacti.
Maybe i can do the opposite. Make different templates for every type of policy. Like a template for queuing, another template for policying etc. But this would mean a lot of work configuring a device and it would not be logical for the user. A user will always think starting from interfaces->service-policy->classes->policies->counters
In fact, all above is more complicated because it is also possible to use hiarchical service-policy's. Maybe it is possible to setup all measurements/graphs outside of cacti but managing them from whithin cacti.
To be continued...
Agree... it is quite complicated hierarchy. You can't do it using just simple SNMP query because every object is indexed with two values so the only solution is script based. I was involved some time ago with related task (external script). I found another problem which is even more important. Every change you do to your QoS settings implicates change in object SNMP indexes. In my network at least such changes are quite frequent. The script then should use interface name and calss map name as quite fixed parameters. Then do all mappings every poll and gather actual data. Small example follows (sorry... can't share the code ).
- Piotr
- Piotr
- Attachments
-
- QoS graph example
- qos_graph.gif (24.03 KiB) Viewed 15407 times
Thanks for the feedback. I am already working on the script. Did you only have problems with changing interface id's or also with the policy's and classes? I think i will be using ccmHistoryRunningLastChanged (1.3.6.1.4.1.9.9.43.1.1.1) as a trigger for reindexing the id's. Any other tips or code to share?
I am refering to changing QoS objects IDs only - whatever you change in parameters Cisco internaly deletes the object instance and creates a new one with new index and new parameters. I avoided interfaces IDs changing by using "snmp-server ifindex persist" command. Monitoring ccmHistoryRunningLastChanged status is very good idea... I was doing reindexing before every poll what was not too efficient. Since you will fetch quite a lot of tables during this process I would suggest using snmpbulkwalk which is much faster than snmpwalk. Good luck!
- Piotr
- Piotr
CBWFQ on Cisco Routers
Seems that "Get SNMP Data" is the easiest way to read out the CBWFQ results. I find out the right indexes in 3 steps:
snmpwalk -c public -v 2c 10.10.10.10 ifdesc
snmpwalk -c public -v 2c 10.10.10.10 .1.3.6.1.4.1.9.9.166.1.1.1.1.4
snmpwalk -c public -v 2c 10.10.10.10 .1.3.6.1.4.1.9.9.166.1.15.1.1.10.x
First step: Look for the IF-index on which CBWFQ resides. e.g. "9"
Second: Search the CBWFQ-index "x" e.g. for interface 9
.1.3.6.1.4.1.9.9.166.1.1.1.1.4.x = 9
Third: Do some "snmp-walk" with .1.3.6.1.4.1.9.9.166.1.15.1.1.10.x and get as a result e.g.:
.1.3.6.1.4.1.9.9.166.1.15.1.1.10.1133.1137
.1.3.6.1.4.1.9.9.166.1.15.1.1.10.1133.1143
.1.3.6.1.4.1.9.9.166.1.15.1.1.10.1133.1149
.1.3.6.1.4.1.9.9.166.1.15.1.1.10.1133.1155
.1.3.6.1.4.1.9.9.166.1.15.1.1.10.1133.1161
where x = 1133.
For every of theses indexes an "SNMP GET"-Data source has to be created. Thats all.
In our CBWFQ - implementation we have the following index meanings (it´s a top-level policy)
1. index = voice
2. index = delay
3. index = best_effort
4. index = low_loss
5. index = summarized traffic
Indexes are listed by snmpwalk in the same order as shown on the Cisco Command Line Interface when typing "show policy interface" and can by easily adopted.
You guys are right, that a special php-script has to be written for that process.
Suddenly I´m not the php expert for that, but I´ll try to help you further on....
snmpwalk -c public -v 2c 10.10.10.10 ifdesc
snmpwalk -c public -v 2c 10.10.10.10 .1.3.6.1.4.1.9.9.166.1.1.1.1.4
snmpwalk -c public -v 2c 10.10.10.10 .1.3.6.1.4.1.9.9.166.1.15.1.1.10.x
First step: Look for the IF-index on which CBWFQ resides. e.g. "9"
Second: Search the CBWFQ-index "x" e.g. for interface 9
.1.3.6.1.4.1.9.9.166.1.1.1.1.4.x = 9
Third: Do some "snmp-walk" with .1.3.6.1.4.1.9.9.166.1.15.1.1.10.x and get as a result e.g.:
.1.3.6.1.4.1.9.9.166.1.15.1.1.10.1133.1137
.1.3.6.1.4.1.9.9.166.1.15.1.1.10.1133.1143
.1.3.6.1.4.1.9.9.166.1.15.1.1.10.1133.1149
.1.3.6.1.4.1.9.9.166.1.15.1.1.10.1133.1155
.1.3.6.1.4.1.9.9.166.1.15.1.1.10.1133.1161
where x = 1133.
For every of theses indexes an "SNMP GET"-Data source has to be created. Thats all.
In our CBWFQ - implementation we have the following index meanings (it´s a top-level policy)
1. index = voice
2. index = delay
3. index = best_effort
4. index = low_loss
5. index = summarized traffic
Indexes are listed by snmpwalk in the same order as shown on the Cisco Command Line Interface when typing "show policy interface" and can by easily adopted.
You guys are right, that a special php-script has to be written for that process.
Suddenly I´m not the php expert for that, but I´ll try to help you further on....
I have hacked the CBWFQ script from sourceforge so that it outputs in what I think is in a cacti compatible way. I removed some of the spurious stuff, like the ability to monitor more than one router and output to rrd.
The output looks like this:
The code:
The output looks like this:
Code: Select all
# ./nmiscbwfq.pl 192.168.1.1 public
class-default:73257 class1:2803130392 class2:3988434683
Code: Select all
#!/usr/bin/perl
#Class-based weighted fair queue interrogation module.
#This module maps class names to policy-id, object-id pairs and
#does an SNMP get on the bytes transmitted OID.
#written by Andy Brauer
#initial release 07/05/2002
#current release 10/05/2002
#current version 0.51
use warnings;
use strict;
my @cb_config1;
my @arraypol;
my @arraypol1;
my @arrayconf;
my @arrayconf1;
my @arrayname;
my @arraypol3;
my @arrayobj;
my @array1stresult;
my @array2ndresult;
my @options;
my $line;
my $name_cell;
my $config_id1_cell;
my $config_id2_cell;
my $pol_id_cell;
my $obj_id_cell;
my $database;
my $node;
my $community;
my $class;
my @name;
my $newline;
#open, read and close config file;
@cb_config1 = @ARGV;
$node = $cb_config1[0];
$community = $cb_config1[1];
if ($node) {
if ($community) {
chomp $node;
chomp $community;
#call external app snmpwalk
#walk the CBWFQ config mib
#place result in "arrayconfig" array
@arrayconf = `/usr/bin/snmpwalk -v 2c $node -c $community 1.3.6.1.4.1.9.9.166.1.7.1.1.1`;
#call external app snmpwalk
#walk the CBWFQ policy mib
#place result in "arraypol" array
@arraypol = `/usr/bin/snmpwalk -v 2c $node -c $community 1.3.6.1.4.1.9.9.166.1.5.1.1.2`;
#step through "arrayconf" array and set each instance to the
foreach $line (@arrayconf)
{
#replace the ""new-line" character combination with a "new-line character".
#this removes the trailing double-apostrophe from the result in the
#"array-conf" array.
$line =~ s/\"\n/\n/g;
#replace double-apostrophe character with a period.
#this removes the leading double-apostrophe from the result in the
#"array-conf" array.
$line =~ s/\"/./g;
#replace all spaces with a period.
$line =~ s/ /./g;
#create a new array, "arrayconf1", with each child variable initialized to the value
#of the "$line value" variable split by the period.
@arrayconf1 = split(/\./,$line);
$newline = $line;
$newline =~ s/\.//g;
@name = split (/:/, $newline);
$class = $name[-1];
chomp $class;
print "$class:";
#set the variable of the 13th "arrayconf1" array child variable to the "$name_cell"
#variable.
$name_cell = $arrayconf1[12];
#remove the "new-line character"
chomp $name_cell;
#set the variable of the 10th "arrayconf1" array child variable to the "$config_id1_cell"
#variable.
$config_id1_cell = $arrayconf1[9];
#step through the "arraypol" array and set each instance to the variable "$line"
foreach $line (@arraypol)
{
#replace all spaces with a period.
$line =~ s/ /./g;
#create a new array, "arraypol1", with each child variable initialized to the value
#of the "$line value" variable split by the period.
@arraypol1 = split(/\./,$line);
#set the variable of the 10th "arraypol1" array child variable to the "$pol_id_cell"
#variable.
$pol_id_cell = $arraypol1[9];
#set the variable of the 11th "arraypol1" array child variable to the "$obj_id_cell"
#variable.
$obj_id_cell = $arraypol1[10];
#set the variable of the 14th "arraypol1" array child variable to the "$conf_id2_cell"
#variable.
$config_id2_cell = $arraypol1[13];
#compare the value of variables "$config_id1_cell" and "$config_id2_cell".
if($config_id1_cell == $config_id2_cell)
{
#if the config_ids match then add the name to the name array, the pol_id
#to the pol_id array and the obj_id to the obj_id array.
@arrayname = $name_cell;
@arraypol3 = $pol_id_cell;
@arrayobj = $obj_id_cell;
#Step through the "@arrayname" array and set each instance to the "$name_cell"
#variable
foreach $name_cell (@arrayname)
{
#call external app snmpget and pass the values of the polid and objid variables. place the
#result in @array1stresult.
@array1stresult = `/usr/bin/snmpget -v 2c $node -c $community 1.3.6.1.4.1.9.9.166.1.15.1.1.9.$arraypol3[0].$arrayobj[0]`;
foreach $line (@array1stresult)
{
#replace all spaces with a period.
$line =~ s/ /./g;
#create a new array, "array2ndresult", with each child variable initialized to the value
#of the "$line" variable split by the period.
@array2ndresult = split(/\./,$line);
$newline = $line;
$newline =~ s/\.//g;
@name = split (/:/, $newline);
$class = $name[-1];
chomp $class;
print "$class ";
}
}
}
}
};
};
}
I have rewritten the script to be a little more readable, and it follows the cacti output standard.
Code: Select all
#!/usr/bin/perl
#Class-based weighted fair queue interrogation module.
#This module maps class names to policy-id, object-id pairs and
#does an SNMP get on the bytes transmitted OID.
#written by Andy Brauer
#initial release 07/05/2002
#current release 10/05/2002
#current version 0.51
#
# 25/04/2005 - Paul Wozney
#
# Completely rewritten by Paul Wozney
# There was nothing really wrong with the script that Andy wrote,
# but I extended it to respond to index, query and get requests
# and made seperate subroutines for these
#
# I did this so I could integrate the script into cacti, as this
# was the only script available - and I really couldn't follow
# the code that Andy wrote so I figured it was easier just to
# do it my way. :)
#
# I'm sure there are bugs. Reach me here:
#
# One caveat, as I'm currently applying DSCP and policing QoS on
# the same router I named my QoS classes like this "QoSnnnn" and
# this script is written to see those.
#use warnings;
use strict;
my $ip = $ARGV[0];
my $community = $ARGV[1];
my $action = $ARGV[2];
my $class = $ARGV[3];
my $filterstring = "QoS"; # use this to filter out unwanted classes
#my $filterstring = ".*"; # or to match everything
my $snmppath = "/usr/bin/snmpwalk";
my $version = "2c";
my $index_oid = "1.3.6.1.4.1.9.9.166.1.7.1.1.1"; # lists the classes
my $ilist_oid = "1.3.6.1.4.1.9.9.166.1.5.1.1.2"; # lists the references
my $bytes_oid = "1.3.6.1.4.1.9.9.166.1.15.1.1.9"; # lists the pol/obj id refs
sub retrieve_index {
# lists the classes
# this is used in the other subroutines, so if a third variable is passed "index"
# it lists the references along with the classes
my $node = shift;
my $comm = shift;
my $ind = shift;
my @classes;
my @index = `$snmppath -v $version $node -c $comm $index_oid`;
# should output this:
# SNMPv2-SMI::enterprises.9.9.166.1.7.1.1.1.1025 = STRING: "CLASS"
foreach (@index) {
s/"//g;
my @littlearray = split / /;
# if you've set $filterstring it will only display those classes you want
next unless ($littlearray[3] =~ /$filterstring/);
my @values = split /\./, $littlearray[0];
if ($ind eq "index") { push @classes, "$littlearray[3]:$values[9]" ; }
else { push @classes, $littlearray[3]; }
}
return @classes;
}
sub retrieve_query {
# retrieves the bytes for all classes
my $node = shift;
my $comm = shift;
my @classes = @_; # gets the remaining variables passed
my @bytes;
my @ilist = `$snmppath -v $version $node -c $comm $ilist_oid`;
# should output this:
# SNMPv2-SMI::enterprises.9.9.166.1.5.1.1.2.1073.1073 = Gauge32: 1025
# note that the variable part matches the oid for the class above
foreach my $entry (@ilist) {
my @littlearray = split / /, $entry;
my @values = split /\./, $littlearray[0];
chomp $littlearray[3];
foreach (@classes) {
my ($class,$refer) = split /:/;
chomp $refer;
chomp $class;
# if you've set $filterstring it will only display those classes you want
if (($littlearray[3] eq $refer) && ($class =~ /$filterstring/)) {
my $byte_count = `$snmppath -v $version $node -c $comm $bytes_oid.$values[9].$values[10]`;
my @byte_array = split / /, $byte_count;
push @bytes, "$class:$byte_array[3]";
}
}
}
return @bytes;
}
sub retrieve_get {
# gets the bytes for a single class
# this executes in about 10 seconds on my box, it could be
# way faster if there was some kind of caching done, maybe
# expire after an hour considering most people poll every 5 mins
my $node = shift;
my $comm = shift;
my $colour = shift; # just another variable for class, but
# I hate scope problems. :)
chomp $colour;
my @classes = @_;
my $ival;
my $indexval;
my @ilist = `$snmppath -v $version $node -c $comm $ilist_oid`;
# first figure out the reference for the class
foreach (@classes) {
my ($class,$refer) = split /:/;
chomp $class;
chomp $refer;
if ($colour eq $class) { $ival = $refer; }
}
# then figure out the oid
foreach my $entry (@ilist) {
my @littlearray = split / /, $entry;
my @values = split /\./, $littlearray[0];
chomp $littlearray[3];
if ($ival eq $littlearray[3]) { $indexval = "$values[9].$values[10]" ; }
}
# and POW! it's done.
my $byte_count = `$snmppath -v $version $node -c $comm $bytes_oid.$indexval`;
my @byte_array = split / /, $byte_count;
return "$class:$byte_array[3]";
}
ARGUMENTS: {
if ($action eq "index") { print retrieve_index($ip,$community) ; last ARGUMENTS; }
if ($action eq "query") { print retrieve_query($ip,$community,retrieve_index($ip,$community,"index")) ; last ARGUMENTS; }
if ($action eq "get") { print retrieve_get($ip,$community,$class,retrieve_index($ip,$community,"index")) ; last ARGUMENTS; }
print "usage:\n\n./nmiscbwfq.pl IP COMMUNITY index\n./nmiscbwfq.pl IP COMMUNITY query {bytes}\n./nmiscbwfq.pl IP COMMUNITY get {bytes} CLASS\n";
}
exit;
Last edited by pwozney on Tue Apr 26, 2005 11:40 am, edited 1 time in total.
Success!
Now all I need to do is put the lines on the same graph.
Now all I need to do is put the lines on the same graph.
- Attachments
-
- cacti_data_query_cisco_get_cbwfq_classes.xml
- (10.5 KiB) Downloaded 486 times
-
- cacti_data_template_cbwfq_throughput.xml
- (4.33 KiB) Downloaded 556 times
-
- cacti_graph_template_cisco_bits_output_from_qos_class.xml
- (9 KiB) Downloaded 612 times
The last template and the updated script - I have made changes since yesterday.
- Attachments
-
- cacti_host_template_cisco_router.xml
- (123.84 KiB) Downloaded 655 times
-
- cbwfq.txt
- (6.06 KiB) Downloaded 774 times
Since I have managed to get this working, I recommend that you follow the thread here if you want to hear about my work on this little problem:
http://forums.cacti.net/viewtopic.php?t=7401
http://forums.cacti.net/viewtopic.php?t=7401
Who is online
Users browsing this forum: No registered users and 5 guests