Commit f66b9dbf authored by Robert Ricci's avatar Robert Ricci
Browse files

A whole ball of snmpit improvements from Keith Sklower at Berkeley.

parent 10f1e248
...@@ -101,6 +101,8 @@ VLAN Control: ...@@ -101,6 +101,8 @@ VLAN Control:
-N <name> Print out the VLAN number for the named VLAN -N <name> Print out the VLAN number for the named VLAN
-c Delete ALL VLANs, and recreate from the database. ** USE -c Delete ALL VLANs, and recreate from the database. ** USE
WITH EXTREME CAUTION ** WITH EXTREME CAUTION **
-F Create all vlans in the given stack on the leader for
use in leader->{ALLVLANSONLEADER} (Internal use only)
Port Control: Port Control:
-s List all ports, and show configuration information -s List all ports, and show configuration information
...@@ -112,6 +114,7 @@ Port Control: ...@@ -112,6 +114,7 @@ Port Control:
-u <half|full> <ports> Set duplex of <ports> to half or full -u <half|full> <ports> Set duplex of <ports> to half or full
-T <port> <names> Enable trunking on the given <port>, and allow VLANs -T <port> <names> Enable trunking on the given <port>, and allow VLANs
with the given <names> across it with the given <names> across it
-E <port> <names> Like -T, but "Equal" mode; PVID is also tagged
-U <port> Turn off trunking for the given <port> -U <port> Turn off trunking for the given <port>
-b <ports> Print out port status for a set of ports -b <ports> Print out port status for a set of ports
-B <statstring> Pass in a stat string from -b to restore status -B <statstring> Pass in a stat string from -b to restore status
...@@ -128,8 +131,8 @@ END ...@@ -128,8 +131,8 @@ END
my %opt = (); my %opt = ();
Getopt::Long::Configure("no_ignore_case"); Getopt::Long::Configure("no_ignore_case");
GetOptions(\%opt, 'a','c','d','e','b','B=s@','g','h','i=s@','l','m=s@','M','n', GetOptions(\%opt, 'a','c','d','e','b','B=s@','g','h','i=s@','l','m=s@','M','n',
'N=s@','o=s@','p=s','q','r','s', 'S=s@', 't','T=s','u=s','U','v=s','w', 'N=s@','o=s@','p=s','q','r','s', 'S=s@','t','E=s','T=s','u=s','U','v=s','w',
'y=s','x=s','z=s'); 'y=s','x=s','z=s','F');
# Unused: f,j # Unused: f,j
if ($opt{h}) { if ($opt{h}) {
...@@ -152,6 +155,7 @@ my $pid; ...@@ -152,6 +155,7 @@ my $pid;
my $eid; my $eid;
my @ports; my @ports;
my @optvlanids = (); my @optvlanids = ();
my $equaltrunking = 0;
# #
# Some operations have mandatory agruments - for others, make sure that # Some operations have mandatory agruments - for others, make sure that
...@@ -176,12 +180,13 @@ if ($opt{t} || $opt{r}) { ...@@ -176,12 +180,13 @@ if ($opt{t} || $opt{r}) {
# Options that take a list of ports # Options that take a list of ports
# #
@ports = @ARGV; @ports = @ARGV;
} elsif ($opt{T}) { } elsif ($opt{T} || $opt{E}) {
# #
# Options that take both a port and a list of VLANs - we require at least # Options that take both a port and a list of VLANs - we require at least
# one VLAN to be given # one VLAN to be given
# #
if (!@ARGV) { if ($opt{E}) { $opt{T} = $opt{E}; $equaltrunking = 1;}
elsif (!@ARGV) {
tberror "At least one VLAN required"; tberror "At least one VLAN required";
exit &usage; exit &usage;
} }
...@@ -219,9 +224,10 @@ if ($opt{r}) { push @commands, ["reset"]; } ...@@ -219,9 +224,10 @@ if ($opt{r}) { push @commands, ["reset"]; }
if ($opt{c}) { push @commands, ["recreate"]; } if ($opt{c}) { push @commands, ["recreate"]; }
if ($opt{U}) { push @commands, ["trunkdisable"]; } if ($opt{U}) { push @commands, ["trunkdisable"]; }
if ($opt{b}) { push @commands, ["portstatus"]; } if ($opt{b}) { push @commands, ["portstatus"]; }
if ($opt{F}) { push @commands, ["synchleader"]; }
# #
# Commands that can appear once, and take an agurment # Commands that can appear once, and take an argument
# #
if ($opt{d}) { push @commands, ["portcontrol","disable"]; } if ($opt{d}) { push @commands, ["portcontrol","disable"]; }
if ($opt{e}) { push @commands, ["portcontrol","enable"]; } if ($opt{e}) { push @commands, ["portcontrol","enable"]; }
...@@ -457,7 +463,7 @@ foreach my $command (@commands) { ...@@ -457,7 +463,7 @@ foreach my $command (@commands) {
my @devicenames; my @devicenames;
my @vlans; my @vlans;
SWITCH: for ($operation) { SWITCH: for ($operation) {
(/listvlans/ || /getstats/ || /make/ || /remove/ || /vlannumber/) && do { (/listvlans/ || /getstats/ || /make/ || /remove/ || /vlannumber/ || /synchleader/) && do {
@devicenames = $supplied_switches? @devicenames = $supplied_switches?
@supplied_switches : getTestSwitches(); @supplied_switches : getTestSwitches();
last; last;
...@@ -581,12 +587,6 @@ foreach my $command (@commands) { ...@@ -581,12 +587,6 @@ foreach my $command (@commands) {
@{$stacks{$stack_id}}); @{$stacks{$stack_id}});
last; last;
}; };
/nortel/ && do {
require snmpit_nortel;
$stack = new snmpit_nortel($stack_id,$debug,
@{$stacks{$stack_id}});
last;
};
/generic/ && do { /generic/ && do {
require snmpit_stack; require snmpit_stack;
$stack = new snmpit_stack($stack_id,$debug, $stack = new snmpit_stack($stack_id,$debug,
...@@ -648,6 +648,10 @@ foreach my $command (@commands) { ...@@ -648,6 +648,10 @@ foreach my $command (@commands) {
$exitval += doListVlans(\@stacks); $exitval += doListVlans(\@stacks);
last; last;
}; # /listvlans/ && do }; # /listvlans/ && do
/synchleader/ && do {
$exitval += doSynchLeader(\@stacks);
last;
}; # /listvlans/ && do
/listports/ && do { /listports/ && do {
$exitval += doListPorts(\@stacks); $exitval += doListPorts(\@stacks);
last; last;
...@@ -689,7 +693,7 @@ foreach my $command (@commands) { ...@@ -689,7 +693,7 @@ foreach my $command (@commands) {
last; last;
}; # /trunkenable/ && do }; # /trunkenable/ && do
/trunkdisable/ && do { /trunkdisable/ && do {
$exitval += doTrunkDisable(\@stacks,@ports); $exitval += doTrunkDisable(\@stacks,$ports[0]);
last; last;
}; # /trunkdisable/ && do }; # /trunkdisable/ && do
/portstatus/ && do { /portstatus/ && do {
...@@ -856,6 +860,35 @@ $vlan_id,$ddep, $pideid, $vname, $members ...@@ -856,6 +860,35 @@ $vlan_id,$ddep, $pideid, $vname, $members
return 0; return 0;
} }
#
# This routine does a number of illegal things. It is used
# to make sure the leader of a stack contains all vlans on all switches
# in the stack and is only useful for moving a stack back and forth
# between types "cisco" and "generic" (where $stack->{ALLVLANSONLEADER} must be
# set to 1.
#
sub doSynchLeader ($) {
my $stacks = shift;
my $errors = 0;
if (!TBAdmin()) {
die "Only admins are allowed to synchronize stacks\n";
}
foreach my $stack (@$stacks) {
my $leader = $stack->{LEADER};
my %vlans = $stack->findVlans();
while (my ($id,$num) = each %vlans) {
if (($num >= $leader->{MIN_VLAN}) &&
($num <= $leader->{MAX_VLAN}) &&
!($leader->vlanNumberExists($num))) {
$errors += $leader->createVlan($id, $num);
}
}
}
return $errors;
}
# #
# Lists all ports on all stacks # Lists all ports on all stacks
# #
...@@ -1110,6 +1143,8 @@ sub doVlansFromTables($@) { ...@@ -1110,6 +1143,8 @@ sub doVlansFromTables($@) {
my @vlans = @_; my @vlans = @_;
my $errors = 0; my $errors = 0;
my $vlan_number;
my $oEopt = $equaltrunking;
if (@$stacks > 1) { if (@$stacks > 1) {
die "VLAN creation accross multiple stacks is not yet supported\n" . die "VLAN creation accross multiple stacks is not yet supported\n" .
...@@ -1124,6 +1159,13 @@ sub doVlansFromTables($@) { ...@@ -1124,6 +1159,13 @@ sub doVlansFromTables($@) {
return RemoteDoVlansFromTables(@vlans); return RemoteDoVlansFromTables(@vlans);
} }
my @trunkedPorts = getExperimentTrunks($eid,$pid);
$equaltrunking = 1;
foreach my $port (@trunkedPorts) {
$errors += doTrunkEnable($stacks,$port);
}
$equaltrunking = $oEopt;
foreach my $vlan (@vlans) { foreach my $vlan (@vlans) {
my @ports = getVlanPorts($vlan); my @ports = getVlanPorts($vlan);
if ($stack->vlanExists($vlan)) { if ($stack->vlanExists($vlan)) {
...@@ -1131,13 +1173,14 @@ sub doVlansFromTables($@) { ...@@ -1131,13 +1173,14 @@ sub doVlansFromTables($@) {
if (!$quiet); if (!$quiet);
$errors += $stack->setPortVlan($vlan,@ports); $errors += $stack->setPortVlan($vlan,@ports);
} else { } else {
if (!$stack->createVlan($vlan,\@ports)) { $vlan_number = $stack->createVlan($vlan,\@ports);
if (!$vlan_number) {
tberror "Failed to create VLAN with id $vlan"; tberror "Failed to create VLAN with id $vlan";
# #
# Don't try to put ports in a VLAN if it couldn't be created # Don't try to put ports in a VLAN if it couldn't be created
# #
$errors++; $errors++;
} } else { setVlanTag($vlan, $vlan_number); }
} }
# #
...@@ -1188,6 +1231,10 @@ sub doReset($@) { ...@@ -1188,6 +1231,10 @@ sub doReset($@) {
} }
my $errors = 0; my $errors = 0;
my @trunkedPorts = getExperimentTrunks($eid,$pid);
foreach my $port (@trunkedPorts) {
$errors += doTrunkDisable($stacks,$port);
}
# #
# Just remove the VLAN from evey stack on which it exists. We keep a # Just remove the VLAN from evey stack on which it exists. We keep a
# list and do them all at once for efficiency. # list and do them all at once for efficiency.
...@@ -1197,6 +1244,9 @@ sub doReset($@) { ...@@ -1197,6 +1244,9 @@ sub doReset($@) {
if (!$stack->removeVlan(@existant_vlans)) { if (!$stack->removeVlan(@existant_vlans)) {
$errors++; $errors++;
} }
foreach my $vlan (@existant_vlans) {
setVlanTag($vlan, 0);
}
} }
return $errors; return $errors;
} }
...@@ -1453,7 +1503,7 @@ sub doTrunkEnable($$@) { ...@@ -1453,7 +1503,7 @@ sub doTrunkEnable($$@) {
my $stack = $$stacks[0]; my $stack = $$stacks[0];
print "Enabling trunking on $port ...\n" print "Enabling trunking on $port ...\n"
if (!$quiet); if (!$quiet);
return !($stack->enableTrunking($port,@vlans)); return !($stack->enableTrunking2($port,$equaltrunking,@vlans));
} }
# #
...@@ -1476,12 +1526,10 @@ sub doTrunkDisable($$) { ...@@ -1476,12 +1526,10 @@ sub doTrunkDisable($$) {
# #
my $stack = $$stacks[0]; my $stack = $$stacks[0];
my $errors = 0; my $errors = 0;
foreach my $port (@ports) {
print "Disabling trunking on port $port ...\n" print "Disabling trunking on port $port ...\n"
if (!$quiet); if (!$quiet);
if (!$stack->disableTrunking($port)) { if (!$stack->disableTrunking($port)) {
$errors++; $errors++;
} }
}
return $errors; return $errors;
} }
...@@ -22,6 +22,7 @@ use English; ...@@ -22,6 +22,7 @@ use English;
use SNMP; use SNMP;
use snmpit_lib; use snmpit_lib;
use Socket; use Socket;
use libtestbed;
# #
# These are the commands that can be passed to the portControl function # These are the commands that can be passed to the portControl function
...@@ -898,7 +899,8 @@ sub setPortVlan($$@) { ...@@ -898,7 +899,8 @@ sub setPortVlan($$@) {
my $errors = 0; my $errors = 0;
if (!$self->vlanNumberExists($vlan_number)) { if (!$self->vlanNumberExists($vlan_number)) {
print STDERR "ERROR: VLAN $vlan_number does not exist\n"; print STDERR "ERROR: VLAN $vlan_number does not exist on switch"
. $self->{NAME} . "\n";
return 1; return 1;
} }
...@@ -935,33 +937,59 @@ sub setPortVlan($$@) { ...@@ -935,33 +937,59 @@ sub setPortVlan($$@) {
$format = $PORT_FORMAT_IFINDEX; $format = $PORT_FORMAT_IFINDEX;
} }
#
# Convert ports from the format the were passed in to the correct format
#
my @portlist = $self->convertPortFormat($format,@ports);
# #
# We'll keep track of which ports suceeded, so that we don't try to # We'll keep track of which ports suceeded, so that we don't try to
# enable/disable, etc. ports that failed. # enable/disable, etc. ports that failed.
# #
my @okports = (); my @okports = ();
foreach my $port (@portlist) { my ($index, $retval);
my %BumpedVlans = ();
foreach my $port (@ports) {
$self->debug("Putting port $port in VLAN $vlan_number\n");
#
# Check to see if it's a trunk ....
#
($index) = $self->convertPortFormat($PORT_FORMAT_IFINDEX, $port);
$retval = snmpitGetWarn($self->{SESS},
["vlanTrunkPortDynamicState",$index]);
if (!$retval) {
$errors++;
next;
}
if (!(($retval eq "on") || ($retval eq "onNoNegotiate"))) {
#
# Convert ports to the correct format
#
($index) = $self->convertPortFormat($format, $port);
# #
# Make sure the port didn't get mangled in conversion # Make sure the port didn't get mangled in conversion
# #
if (!defined $port) { if (!defined $index) {
print STDERR "Port not found, skipping\n"; print STDERR "Port not found, skipping\n";
$errors++; $errors++;
next; next;
} }
$self->debug("Putting port $port in VLAN $vlan_number\n"); my $snmpvar = [$PortVlanMemb,$index,$vlan_number,'INTEGER'];
#
# Check to see if we are already in a VLAN
#
$retval = snmpitGet($self->{SESS},[$PortVlanMemb,$index]);
if (($retval ne "NOSUCHINSTANCE") &&
("$retval" ne "$vlan_number") && ("$retval" ne "1")) {
$BumpedVlans{$retval} = 1;
}
# #
# Do the acutal SNMP command # Do the acutal SNMP command
# #
my $snmpvar = [$PortVlanMemb,$port,$vlan_number,'INTEGER']; $retval = snmpitSetWarn($self->{SESS},$snmpvar);
my $retval = snmpitSetWarn($self->{SESS},$snmpvar); } else {
#
# We're here if it a trunk
#
$retval = $self->setVlansOnTrunk($port, 1, $vlan_number);
}
if (!$retval) { if (!$retval) {
$errors++; $errors++;
next; next;
...@@ -988,6 +1016,15 @@ sub setPortVlan($$@) { ...@@ -988,6 +1016,15 @@ sub setPortVlan($$@) {
} }
} }
# When removing things from the control vlan for a firewall,
# need to tell stack to shake things up to flush FDB on neighboring
# switches.
#
my @bumpedlist = keys ( %BumpedVlans );
if (@bumpedlist) {
@{$self->{DISPLACED_VLANS}} = @bumpedlist;
}
return $errors; return $errors;
} }
...@@ -1257,6 +1294,38 @@ sub listVlans($) { ...@@ -1257,6 +1294,38 @@ sub listVlans($) {
} }
} }
#
# Walk trunks for the VLAN members
#
($rows) = snmpitBulkwalkFatal($self->{SESS},["vlanTrunkPortDynamicStatus"]);
$self->debug("Trunk members walk returned " . scalar(@$rows) . " rows\n");
foreach my $rowref (@$rows) {
my ($name,$ifIndex,$status) = @$rowref;
$self->debug("Got $name $ifIndex $status\n",3);
if ($status ne "trunking") { next;}
my ($node) = $self->convertPortFormat($PORT_FORMAT_NODEPORT,$ifIndex);
if (!$node) {
my ($modport) = $self->convertPortFormat($PORT_FORMAT_MODPORT,$ifIndex);
$modport =~ s/\./\//;
$node = $self->{NAME} . ".$modport";
}
# Get the existing bitfield for allowed VLANs on this trunk
my $vlan_number = -1;
my $bitfield = snmpitGetFatal($self->{SESS},
["vlanTrunkPortVlansEnabled",$ifIndex]);
my $unpacked = unpack("B*",$bitfield);
# Put this into an array of 1s and 0s for easy manipulation
foreach my $bit (split //,$unpacked) {
$vlan_number++;
if ($bit == 0) { next; }
$self->debug("got vlan $vlan_number on trunk $node\n",3);
if ($Names{$vlan_number}) {
push @{$Members{$vlan_number}}, $node;
}
}
}
# #
# Build a list from the name and membership lists # Build a list from the name and membership lists
# #
...@@ -1500,7 +1569,14 @@ sub setVlansOnTrunk($$$$) { ...@@ -1500,7 +1569,14 @@ sub setVlansOnTrunk($$$$) {
# #
# Get the existing bitfield for allowed VLANs on the trunk # Get the existing bitfield for allowed VLANs on the trunk
# # If two snmpit process were manipulating trunks at the same
# time there is a potential race in which they would both
# retrieve the same bit vector, compute different results
# and then the trunk would not be correct, so we lock
# in the file system on boss, as that is likely to be faster
# than using the vlan lock buffer on the switch.
#
$self->lock();
my $bitfield = snmpitGetFatal($self->{SESS}, my $bitfield = snmpitGetFatal($self->{SESS},
["vlanTrunkPortVlansEnabled",$ifIndex]); ["vlanTrunkPortVlansEnabled",$ifIndex]);
my $unpacked = unpack("B*",$bitfield); my $unpacked = unpack("B*",$bitfield);
...@@ -1521,6 +1597,8 @@ sub setVlansOnTrunk($$$$) { ...@@ -1521,6 +1597,8 @@ sub setVlansOnTrunk($$$$) {
# And save it back... # And save it back...
my $rv = snmpitSetFatal($self->{SESS}, my $rv = snmpitSetFatal($self->{SESS},
["vlanTrunkPortVlansEnabled",$ifIndex,$bitfield,"OCTETSTR"]); ["vlanTrunkPortVlansEnabled",$ifIndex,$bitfield,"OCTETSTR"]);
$self->unlock();
if ($rv) { if ($rv) {
return 1; return 1;
} else { } else {
...@@ -1594,8 +1672,7 @@ sub clearAllVlansOnTrunk($$) { ...@@ -1594,8 +1672,7 @@ sub clearAllVlansOnTrunk($$) {
# return value currently ignored. # return value currently ignored.
sub resetVlanIfOnTrunk($$$) { sub resetVlanIfOnTrunk($$$) {
my $self = shift; my ($self, $modport, $vlan_number) = @_;
my ($modport, $value, $vlan_number) = @_;
my ($ifIndex) = $self->convertPortFormat($PORT_FORMAT_IFINDEX,$modport); my ($ifIndex) = $self->convertPortFormat($PORT_FORMAT_IFINDEX,$modport);
...@@ -1619,6 +1696,7 @@ sub resetVlanIfOnTrunk($$$) { ...@@ -1619,6 +1696,7 @@ sub resetVlanIfOnTrunk($$$) {
# #
# Get the exisisting bitfield for allowed VLANs on the trunk # Get the exisisting bitfield for allowed VLANs on the trunk
# #
$self->lock();
my $bitfield = snmpitGetFatal($self->{SESS}, my $bitfield = snmpitGetFatal($self->{SESS},
["vlanTrunkPortVlansEnabled",$ifIndex]); ["vlanTrunkPortVlansEnabled",$ifIndex]);
my $unpacked = unpack("B*",$bitfield); my $unpacked = unpack("B*",$bitfield);
...@@ -1663,20 +1741,21 @@ sub resetVlanIfOnTrunk($$$) { ...@@ -1663,20 +1741,21 @@ sub resetVlanIfOnTrunk($$$) {
# And save it back... # And save it back...
$rv = $self->{SESS}->set(["vlanTrunkPortVlansEnabled",$ifIndex,$bitfield, $rv = $self->{SESS}->set(["vlanTrunkPortVlansEnabled",$ifIndex,$bitfield,
"OCTETSTR"]); "OCTETSTR"]);
$self->unlock();
return 0; return 0;
} }
# #
# Enable trunking on a port # Enable trunking on a port
# #
# usage: enablePortTrunking(self, modport, nativevlan) # usage: enablePortTrunking2(self, modport, nativevlan, equaltrunking)
# modport: module.port of the trunk to operate on # modport: module.port of the trunk to operate on
# nativevlan: VLAN number of the native VLAN for this trunk # nativevlan: VLAN number of the native VLAN for this trunk
# Returns 1 on success, 0 otherwise # Returns 1 on success, 0 otherwise
# #
sub enablePortTrunking($$$) { sub enablePortTrunking2($$$$) {
my $self = shift; my ($self,$port,$native_vlan,$equaltrunking) = @_;
my ($port,$native_vlan) = @_; my $trunking_vlan = ($equaltrunking ? 1 : $native_vlan);
my ($ifIndex) = $self->convertPortFormat($PORT_FORMAT_IFINDEX,$port); my ($ifIndex) = $self->convertPortFormat($PORT_FORMAT_IFINDEX,$port);
...@@ -1703,7 +1782,7 @@ sub enablePortTrunking($$$) { ...@@ -1703,7 +1782,7 @@ sub enablePortTrunking($$$) {
# #
# Set the native VLAN for this trunk # Set the native VLAN for this trunk
# #
my $nativeVlan = ["vlanTrunkPortNativeVlan",$ifIndex,$native_vlan,"INTEGER"]; my $nativeVlan = ["vlanTrunkPortNativeVlan",$ifIndex,$trunking_vlan,"INTEGER"];
$rv = snmpitSetWarn($self->{SESS},$nativeVlan); $rv = snmpitSetWarn($self->{SESS},$nativeVlan);
if (!$rv) { if (!$rv) {
warn "ERROR: Unable to set native VLAN on trunk\n"; warn "ERROR: Unable to set native VLAN on trunk\n";
...@@ -1713,13 +1792,15 @@ sub enablePortTrunking($$$) { ...@@ -1713,13 +1792,15 @@ sub enablePortTrunking($$$) {
# #
# Finally, enable trunking! # Finally, enable trunking!
# #
my $trunkEnable = ["vlanTrunkPortDynamicState",$ifIndex,"on","INTEGER"]; my $trunkEnable = ["vlanTrunkPortDynamicState",$ifIndex,"onNoNegotiate","INTEGER"];
$rv = snmpitSetWarn($self->{SESS},$trunkEnable); $rv = snmpitSetWarn($self->{SESS},$trunkEnable);
if (!$rv) { if (!$rv) {
warn "ERROR: Unable to enable trunking\n"; warn "ERROR: Unable to enable trunking\n";
return 0; return 0;
} }
if ($equaltrunking) { return 1; }