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

Backend snmpit module changes from Keith Sklower

These are mostly code cleanup, in preparation for some larger frontend
changes that will come soon. Also gives an option for the Nortel
to be configured through the CLI rather than SNMP, which Keith says
is much, much faster on it.
parent 8ebe5433
......@@ -56,6 +56,11 @@ my $PORT_FORMAT_IFINDEX = 1;
my $PORT_FORMAT_MODPORT = 2;
my $PORT_FORMAT_NODEPORT = 3;
#
# used by vlanTrunkUtil()
#
my ($VOP_CLEAR, $VOP_SET, $VOP_CLEARALL, $VOP_CHECK) = (0, 1, 2, 3);
#
# Creates a new object.
#
......@@ -950,8 +955,9 @@ sub createVlan($$;$$$) {
# returns 0 on sucess.
# returns the number of failed ports on failure.
#
sub setPortVlan($$@) {
sub opPortVlan($$$@) {
my $self = shift;
my $remove = shift;
my $vlan_number = shift;
my @ports = @_;
......@@ -1032,11 +1038,16 @@ sub setPortVlan($$@) {
$errors++;
next;
}
my $snmpvar = [$PortVlanMemb,$index,$vlan_number,'INTEGER'];
my $snmpvar = [$PortVlanMemb,$index, ($remove? 1 :$vlan_number),
'INTEGER'];
#
# Check to see if we are already in a VLAN
#
$retval = snmpitGet($self->{SESS},[$PortVlanMemb,$index]);
if ($remove && ($retval ne $vlan_number)) {
$errors++;
next;
}
if (($retval ne "NOSUCHINSTANCE") &&
("$retval" ne "$vlan_number") && ("$retval" ne "1")) {
$BumpedVlans{$retval} = 1;
......@@ -1049,7 +1060,8 @@ sub setPortVlan($$@) {
#
# We're here if it a trunk
#
$retval = $self->setVlansOnTrunk($port, 1, $vlan_number);
$retval = $self->setVlansOnTrunk($port,!$remove, $vlan_number);
next if ($retval); # should not enable or disable existing trunks
}
if (!$retval) {
$errors++;
......@@ -1063,7 +1075,7 @@ sub setPortVlan($$@) {
# Ports going into VLAN 1 are being taken out of circulation, so we
# disable them. Otherwise, we need to make sure they get enabled.
#
if ($vlan_number == 1) {
if (($vlan_number == 1) || $remove) {
$self->debug("Disabling " . join(',',@okports) . "...");
if ( my $rv = $self->portControl("disable",@okports) ) {
print STDERR "Port disable had $rv failures.\n";
......@@ -1089,6 +1101,11 @@ sub setPortVlan($$@) {
return $errors;
}
sub setPortVlan($$@) {
my ($self, $vlan_number, @ports) = @_;
return opPortVlan($self, 0, $vlan_number, @ports);
}
#
# Remove all ports from the given VLANs, which are given as Cisco-specific
# VLAN numbers
......@@ -1100,6 +1117,7 @@ sub setPortVlan($$@) {
sub removePortsFromVlan($@) {
my $self = shift;
my @vlan_numbers = @_;
my $errors = 0;
#
# Make sure the VLANs actually exist
......@@ -1107,7 +1125,8 @@ sub removePortsFromVlan($@) {
foreach my $vlan_number (@vlan_numbers) {
if (!$self->vlanNumberExists($vlan_number)) {
print STDERR "ERROR: VLAN $vlan_number does not exist\n";
return 1;
$errors++;
next;
}
}
......@@ -1126,23 +1145,49 @@ sub removePortsFromVlan($@) {
} elsif ($self->{OSTYPE} eq "IOS") {
$VlanPortVlan = "vmVlan"; #index is ifIndex
}
my @ports;
my %ports;
#
# Walk the tree to find VLAN membership
#
my ($rows) = snmpitBulkwalkFatal($self->{SESS},[$VlanPortVlan]);
foreach my $rowref (@$rows) {
my ($name,$modport,$port_vlan_number) = @$rowref;
$self->debug("Got $name $modport $port_vlan_number\n");
my ($name,$ifIndex,$port_vlan_number) = @$rowref;
$self->debug("Got $name $ifIndex $port_vlan_number\n");
if ($vlan_numbers{$port_vlan_number}) {
push @ports, $modport;
push @{$ports{$port_vlan_number}}, $ifIndex;
}
}
my %trunks;
#
# Walk trunks for the VLAN members
#
($rows) = snmpitBulkwalkFatal($self->{SESS},["vlanTrunkPortDynamicStatus"]);
foreach my $rowref (@$rows) {
my ($name,$ifIndex,$status) = @$rowref;
$self->debug("Got $name $ifIndex $status\n",3);
if ($status ne "trunking") { next;}
# Get the allowed VLANs on this trunk
my @trunklans = $self->vlanTrunkUtil
($VOP_CHECK, $ifIndex, @vlan_numbers);
foreach my $vlan_number (@trunklans) {
$self->debug("got vlan $vlan_number on trunk $ifIndex\n",3);
push @{$trunks{$vlan_number}}, $ifIndex;
}
}
while (my ($number, $plist) = each %trunks) {
foreach my $ifIndex (@$plist)
{ $errors += !$self->setVlansOnTrunk($ifIndex, 0, $number); }
}
$self->debug("About to remove ports " . join(",",@ports) . "\n");
if (@ports) {
return $self->setPortVlan(1,@ports);
$self->debug("About to remove ports " . join(",",(%ports)) . "\n");
if (%ports) {
while (my ($port_vlan_number, $plist) = each %ports)
{ $errors += $self->opPortVlan(1,$port_vlan_number,@$plist); }
return $errors;
} else {
return 0;
}
......@@ -1150,7 +1195,7 @@ sub removePortsFromVlan($@) {
#
# Remove some ports from the given VLAN, which are given as Cisco-specific
# VLAN numbers. Do not specify trunked ports here.
# VLAN numbers.
#
# usage: removeSomePortsFromVlan(self,int vlan, ports)
# returns 0 on sucess.
......@@ -1161,55 +1206,7 @@ sub removeSomePortsFromVlan($$@) {
my $vlan_number = shift;
my @ports = @_;
#
# Make sure the VLANs actually exist
#
if (!$self->vlanNumberExists($vlan_number)) {
print STDERR "ERROR: VLAN $vlan_number does not exist\n";
return 1;
}
#
# Make a hash of the ports for easy lookup later.
#
my %ports = ();
@ports{@ports} = @ports;
#
# Get a list of the ports in the VLAN
#
my $VlanPortVlan;
if ($self->{OSTYPE} eq "CatOS") {
$VlanPortVlan = "vlanPortVlan"; #index is ifIndex
} elsif ($self->{OSTYPE} eq "IOS") {
$VlanPortVlan = "vmVlan"; #index is ifIndex
}
my @remports;
#
# Walk the tree to find VLAN membership
#
my ($rows) = snmpitBulkwalkFatal($self->{SESS},[$VlanPortVlan]);
foreach my $rowref (@$rows) {
my ($name,$modport,$port_vlan_number) = @$rowref;
my ($trans) = convertPortFormat($PORT_FORMAT_NODEPORT,$modport);
if (!defined $trans) {
$trans = ""; # Guard against some uninitialized value warnings
}
$self->debug("Got $name $modport ($trans) $port_vlan_number\n");
push(@remports, $modport)
if ("$port_vlan_number" eq "$vlan_number" &&
exists($ports{$trans}));
}
$self->debug("About to remove ports " . join(",",@remports) . "\n");
if (@remports) {
return $self->setPortVlan(1,@remports);
} else {
return 0;
}
return opPortVlan($self, 1, $vlan_number, @ports);
}
#
......@@ -1228,6 +1225,7 @@ sub removeVlan($@) {
my $errors = 0;
removePortsFromVlan($self, @vlan_numbers);
foreach my $vlan_number (@vlan_numbers) {
#
# Need to lock the VLAN edit buffer
......@@ -1386,7 +1384,6 @@ my %vtrunkOIDS = (
# precompute 1k 0 bits as bitfield
my $p1k = pack("x128");
my ($VOP_CLEAR, $VOP_SET, $VOP_CLEARALL, $VOP_CHECK) = (0, 1, 2, 3);
#
# vlanTrunkUtil($self, $op, $ifIndex, @vlans)
......@@ -1730,11 +1727,8 @@ sub getChannelIfIndex($@) {
# single port channel.
#
if (!$ifindex) {
if (@ifIndexes == 1) {
$ifindex = $ifIndexes[0];
}
}
return $ifindex;
}
......@@ -1762,6 +1756,7 @@ sub setVlansOnTrunk($$$$) {
}
my ($ifIndex) = $self->convertPortFormat($PORT_FORMAT_IFINDEX,$port);
$ifIndex = $self->getChannelIfIndex($ifIndex);
@vlan_numbers = $self->vlanTrunkUtil($value, $ifIndex, @vlan_numbers);
return (scalar(@vlan_numbers) != 0);
}
......@@ -1778,18 +1773,7 @@ sub clearAllVlansOnTrunk($$) {
my ($modport) = @_;
my ($ifIndex) = $self->convertPortFormat($PORT_FORMAT_IFINDEX,$modport);
#
# If this is part of an EtherChannel, we have to find the ifIndex for the
# channel.
# TODO: Perhaps this should be general - ie. $self{IFINDEX} should have
# the channel ifIndex the the port is in a channel. Not sure that
# this is _always_ beneficial, though
#
my $channel = snmpitGetFatal($self->{SESS},["pagpGroupIfIndex",$ifIndex]);
if (($channel =~ /^\d+$/) && ($channel != 0)) {
$ifIndex = $channel;
}
$ifIndex = $self->getChannelIfIndex($ifIndex);
my @vlan_numbers = $self->vlanTrunkUtil($VOP_CLEARALL, $ifIndex);
return (scalar(@vlan_numbers) != 0);
}
......@@ -1806,26 +1790,8 @@ sub clearAllVlansOnTrunk($$) {
sub resetVlanIfOnTrunk($$$) {
my ($self, $modport, $vlan_number) = @_;
my ($ifIndex) = $self->convertPortFormat($PORT_FORMAT_IFINDEX,$modport);
#
# If this is part of an EtherChannel, we have to find the ifIndex for the
# channel.
# TODO: Perhaps this should be general - ie. $self{IFINDEX} should have
# the channel ifIndex the the port is in a channel. Not sure that
# this is _always_ beneficial, though
# NOTE: This 'conversion' is no longer needed, since we call
# getChannelIfIndex on the port before passing it into this function
#
#my $channel = snmpitGetFatal($self->{SESS},["pagpGroupIfIndex",$ifIndex]);
#if (!($channel =~ /^\d+$/) || ($channel == 0)) {
# print "WARNING: resetVlanIfOnTrunk got bad channel ($channel) for $self->{NAME}.$modport\n";
# return 0;
#}
#if (($channel =~ /^\d+$/) && ($channel != 0)) {
# $ifIndex = $channel;
#}
$ifIndex = $self->getChannelIfIndex($ifIndex);
my @vlan_numbers = $self->vlanTrunkUtil($VOP_CHECK, $ifIndex, $vlan_number);
if (@vlan_numbers) {
......@@ -1933,7 +1899,7 @@ sub disablePortTrunking($$) {
my $trunkDisable = ["vlanTrunkPortDynamicState",$ifIndex,"off","INTEGER"];
$rv = snmpitSetWarn($self->{SESS},$trunkDisable);
if (!$rv) {
warn "ERROR: Unable to enable trunking\n";
warn "ERROR: Unable to disable trunking\n";
return 0;
}
......@@ -2274,7 +2240,7 @@ sub enableOpenflow($$) {
#
# Cisco switch doesn't support Openflow yet.
#
warn "ERROR: Cisco swith doesn't support Openflow now";
warn "ERROR: Cisco switch doesn't support Openflow now";
return 0;
}
......@@ -2289,7 +2255,7 @@ sub disableOpenflow($$) {
#
# Cisco switch doesn't support Openflow yet.
#
warn "ERROR: Cisco swith doesn't support Openflow now";
warn "ERROR: Cisco switch doesn't support Openflow now";
return 0;
}
......@@ -2305,7 +2271,7 @@ sub setOpenflowController($$$) {
#
# Cisco switch doesn't support Openflow yet.
#
warn "ERROR: Cisco swith doesn't support Openflow now";
warn "ERROR: Cisco switch doesn't support Openflow now";
return 0;
}
......@@ -2321,7 +2287,7 @@ sub setOpenflowListener($$$) {
#
# Cisco switch doesn't support Openflow yet.
#
warn "ERROR: Cisco swith doesn't support Openflow now";
warn "ERROR: Cisco switch doesn't support Openflow now";
return 0;
}
......@@ -2332,7 +2298,7 @@ sub getUsedOpenflowListenerPorts($) {
my $self = shift;
my %ports = ();
warn "ERROR: Cisco swith doesn't support Openflow now\n";
warn "ERROR: Cisco switch doesn't support Openflow now\n";
return %ports;
}
......
......@@ -3,7 +3,7 @@
#
# EMULAB-LGPL
# Copyright (c) 2000-2010 University of Utah and the Flux Group.
# Copyright (c) 2004-2009 Regents, University of California.
# Copyright (c) 2004-2010 Regents, University of California.
# All rights reserved.
#
......@@ -838,12 +838,12 @@ sub setPortVlan($$@) {
# b. the ports is not allocated
# c. the port is a trunk
# This is a dual port, so it doesn't have to leave its PVID.
# case a: This is a dual port, so it doesn't have to leave its PVID.
if (@{@$defaultInfo[0]}[$portIndex - 1]) {
push @newTaggedPorts, $portIndex;
next;
}
# Unallocated untrunked port.
# case b: Unallocated untrunked port.
if (@{@$defaultInfo[1]}[$portIndex - 1]) {
$pvid = 1;
} else {
......@@ -970,48 +970,15 @@ sub removePortsFromVlan($@) {
my $id = $self->{NAME} . "::removePortsFromVlan";
foreach my $vlan_number (@vlan_numbers) {
#
# Do two passes: 1st untrunk any dual-mode ports on this vlan.
# 2nd, remove all ports.
#
my $dualPorts = mirvPortSet($self->get1($forbidOID, 1)); # array
my @portlist = $self->portSetToList
($self->get1($normOID, $vlan_number));
foreach my $portIndex (@portlist) {
if (@$dualPorts[$portIndex-1])
{ $self->disablePortTrunking($portIndex);}
}
$self->lock();
my $defaultLists = $self->getVlanLists(1);
my $vLists = $self->getVlanLists($vlan_number);
@portlist = bitSetToList(@$vLists[2]);
$self->debug("$id $vlan_number: @portlist\n");
foreach my $portIndex (@portlist) {
#
# If this port is not listed as a tagged member of the vlan,
# then it could have only gotten here either as a trunk
# or dual-mode port from another vlan, so don't disable it.
#
if (@{@$vLists[1]}[$portIndex - 1]) {
@{@$defaultLists[1]}[$portIndex - 1] = 1;
@{@$defaultLists[2]}[$portIndex - 1] = 1;
$self->debug("disabling port $portIndex "
. "from vlan $vlan_number \n" );
$self->set(["ifAdminStatus",$portIndex,"down","INTEGER"],$id);
}
@{@$vLists[1]}[$portIndex - 1] = 0;
@{@$vLists[2]}[$portIndex - 1] = 0;
}
$errors += $self->setVlanLists($vlan_number, $vLists, 1, $defaultLists);
$self->unlock();
my @ports = $self->portSetToList($self->get1($egressOID, $vlan_number));
$errors += $self->removeSomePortsFromVlan($vlan_number, @ports);
}
return $errors;
}
#
# Removes and disables some ports in a given VLAN. The VLAN is given as a VLAN
# 802.1Q tag value. Ports are known to be regular ports and not trunked.
# Removes and disables some ports in a given VLAN.
# The VLAN is given as a VLAN 802.1Q tag value.
#
# usage: removeSomePortsFromVlan(self,vlan,@ports)
# returns 0 on sucess.
......@@ -1024,7 +991,18 @@ sub removeSomePortsFromVlan($$@) {
@ports = $self->convertPortFormat($PORT_FORMAT_IFINDEX,@ports);
@porthash{@ports} = @ports;
my $dualPorts = mirvPortSet($self->get1($forbidOID, 1)); # array
# First, any dual ports whose PVID is this vlan become equaltrunks
foreach my $portIndex (@ports) {
if (@$dualPorts[$portIndex-1]) {
my $pvid = $self->get1("dot1qPvid", $portIndex);
$self->enablePortTrunking2($portIndex,1,1)
if ($pvid && ($pvid eq "$vlan_number"));
}
}
# Now, remove the ports from the vlan.
$self->lock();
my $defaultLists = $self->getVlanLists(1);
my $vLists = $self->getVlanLists($vlan_number);
......@@ -1034,7 +1012,7 @@ sub removeSomePortsFromVlan($$@) {
foreach my $portIndex (@portlist) {
next unless exists($porthash{$portIndex});
if (@{@$vLists[1]}[$portIndex - 1]) {
# otherwise, port is tagged, or dual; maybe should complain.
# otherwise, port is tagged.
@{@$defaultLists[1]}[$portIndex - 1] = 1;
@{@$defaultLists[2]}[$portIndex - 1] = 1;
......@@ -1054,8 +1032,7 @@ sub removeSomePortsFromVlan($$@) {
#
# Remove the given VLANs from this switch. Removes all ports from the VLAN,
# It's necessary to call removePortsFromVlan() first on HP's. The VLAN is
# given as a VLAN identifier from the database.
# The VLAN is given as a VLAN identifier from the database.
#
# usage: removeVlan(self,int vlan)
# returns 1 on success
......@@ -1068,6 +1045,7 @@ sub removeVlan($@) {
my $errors = 0;
my $name = $self->{NAME};
$self->removePortsFromVlan(@vlan_numbers);
foreach my $vlan_number (@vlan_numbers) {
#
# Perform the actual removal
......@@ -1331,91 +1309,47 @@ sub listPorts($) {
#
# Get statistics for ports on the switch
#
# usage: getPorts($self)
# usage: getStats($self)
# see snmpit_cisco_stack.pm for a description of return value format
#
#
sub getStats ($) {
sub getStats() {
my $self = shift;
my $ifTable = ["ifInOctets",0];
my %inOctets=();
my %inUcast=();
my %inNUcast=();
my %inDiscard=();
my %inErr=();
my %inUnkProt=();
my %outOctets=();
my %outUcast=();
my %outNUcast=();
my %outDiscard=();
my %outErr=();
my %outQLen=();
my ($varname, $port, $value);
#
# Walk the whole stats tree, and fill these ha
# Walk the tree for the VLAN members
#
$self->{SESS}->getnext($ifTable);
do {
($varname,$port,$value) = @{$ifTable};
$self->debug("getStats: Got $varname, $port, $value\n");
if ($port > $self->{MAXPORT}) {
# do nothing. There are entries for vlan interfaces, etc.
} elsif ($varname =~ /InOctets/) {
$inOctets{$port} = $value;
} elsif ($varname =~ /InUcast/) {
$inUcast{$port} = $value;
} elsif ($varname =~ /InNUcast/) {
$inNUcast{$port} = $value;
} elsif ($varname =~ /InDiscard/) {
$inDiscard{$port} = $value;
} elsif ($varname =~ /InErrors/) {
$inErr{$port} = $value;
} elsif ($varname =~ /InUnknownP/) {
$inUnkProt{$port} = $value;
} elsif ($varname =~ /OutOctets/) {
$outOctets{$port} = $value;
} elsif ($varname =~ /OutUcast/) {
$outUcast{$port} = $value;
} elsif ($varname =~ /OutNUcast/) {
$outNUcast{$port} = $value;
} elsif ($varname =~ /OutDiscard/) {
$outDiscard{$port} = $value;
} elsif ($varname =~ /OutErrors/) {
$outErr{$port} = $value;
} elsif ($varname =~ /OutQLen/) {
$outQLen{$port} = $value;
}
$self->{SESS}->getnext($ifTable);
} while ( $varname =~ /^i[f](In|Out)/) ;
my $vars = new SNMP::VarList(['ifInOctets'],['ifInUcastPkts'],
['ifInNUcastPkts'],['ifInDiscards'],
['ifInErrors'],['ifInUnknownProtos'],
['ifOutOctets'],['ifOutUcastPkts'],
['ifOutNUcastPkts'],['ifOutDiscards'],
['ifOutErrors'],['ifOutQLen']);
my @stats = $self->{SESS}->bulkwalk(0,32,$vars);
#
# Put all of the data gathered in the loop into a list suitable for
# returning
# We need to flip the two-dimentional array we got from bulkwalk on
# its side, and convert ifindexes into node:port
#
my @rv = ();
foreach my $id ( keys %inOctets ) {
my $modport = $self->{IFINDEX}{$id};
$port = portnum($self->{NAME} . ":" . $modport);
if (!$port && $self->{DOALLPORTS}) {
$modport =~ s/\./\//;
$port = $self->{NAME} . ".$modport";
}
#
# Skip ports that don't seem to have anything interesting attached
#
if (!$port) {
$self->debug("$id does not seem to be connected, skipping\n");
next;
}
push @rv, [$port,$inOctets{$id},$inUcast{$id},$inNUcast{$id},
$inDiscard{$id},$inErr{$id},$inUnkProt{$id},$outOctets{$id},
$outUcast{$id},$outNUcast{$id},$outDiscard{$id},$outErr{$id},
$outQLen{$id}];
my $i = 0;
my %stats;
foreach my $array (@stats) {
while (@$array) {
my ($name,$ifindex,$value) = @{shift @$array};
# See comments in walkTable above.
if (! defined $self->{IFINDEX}{$ifindex}) { next; }
my $port = portnum("$self->{NAME}:$ifindex")
|| portnum("$self->{NAME}:".$self->{IFINDEX}{$ifindex});
if (! defined $port) { next; } # Skip if we don't know about it
${$stats{$port}}[$i] = $value;
}
$i++;
}
return @rv;
return map [$_,@{$stats{$_}}], sort {tbsort($a,$b)} keys %stats;
}
#
......@@ -1535,43 +1469,42 @@ sub setVlansOnTrunk($$$$) {
#
sub enablePortTrunking2($$$$) {
my ($self,$port,$native_vlan,$equaltrunking) = @_;
my ($curvlans, $initvlan, $errors) = ([], $native_vlan, 0);
if ($equaltrunking) {
if (!$native_vlan) { $native_vlan = 1; }
$initvlan = 1;
push @$curvlans, $native_vlan if ($native_vlan && $native_vlan ne "1");
} elsif (!defined($native_vlan) || ($native_vlan <= 1)) {
warn "Error: innappropriate or missing PVID for trunk\n";
return 0;
}
my ($ifIndex) = $self->convertPortFormat($PORT_FORMAT_IFINDEX,$port);
#
# portSetVlan will clear out all other vlans and set the PVID
# (Temporarily) clear out all other vlans and set the PVID
#
$self->disablePortTrunking($ifIndex,$curvlans);
my $rv = $self->setPortVlan($native_vlan, $port);
if ($rv) {
warn "ERROR: Unable to add Trunk $port to PVID $native_vlan\n";
return 0;
}
#
# Set port type apropriately.
#
$self->lock();
my $defLists = $self->getVlanLists(1);
if ($equaltrunking) {
my $portType = [$aftOID,$ifIndex,"admitOnlyVlanTagged","INTEGER"];
$rv = $self->{SESS}->set($portType);
if (!defined($rv)) {