Commit eb4691d2 authored by Robert Ricci's avatar Robert Ricci

Add support for Cisco stacks that do not use VTP to maintain a single

VLAN domain. This means that we have to create and delete VLANs on
all switches, instead of just the 'leader'. This behavior is
controlled through the new single_domain column in the
switch_stack_types table.
parent 60563956
......@@ -894,6 +894,7 @@ CREATE TABLE switch_stack_types (
stack_id varchar(10) NOT NULL default '',
stack_type varchar(10) default NULL,
supports_private tinyint(1) NOT NULL default '0',
single_domain tinyint(1) NOT NULL default '1',
PRIMARY KEY (stack_id)
) TYPE=MyISAM;
......
......@@ -407,7 +407,8 @@ foreach my $command (@commands) {
#
my @stacks;
foreach my $stack_id (keys %stacks) {
my ($stack_type, $supports_private) = getStackType($stack_id);
my ($stack_type, $supports_private, $single_domain)
= getStackType($stack_id);
#
# Safety check - make sure the stack supports private VLANs if -y was
# given
......@@ -421,7 +422,7 @@ foreach my $command (@commands) {
/cisco/ && do {
require snmpit_cisco_stack;
$stack = new snmpit_cisco_stack($stack_id,$debug,$COMMUNITY,
$supports_private, @{$stacks{$stack_id}});
$supports_private, $single_domain, @{$stacks{$stack_id}});
last;
}; # /cisco/
/intel/ && do {
......
......@@ -532,10 +532,11 @@ sub findVlan($$;$) {
#
# Create a VLAN on this switch, with the given identifier (which comes from
# the database.) Picks its own switch-specific VLAN number to use.
# the database.) If $vlan_number is given, attempts to use it when creating
# the vlan - otherwise, picks its own Cisco-specific VLAN number.
#
# usage: createVlan($self, $vlan_id [,$private_type [,$private_primary,
# $private_port]])
# usage: createVlan($self, $vlan_id, $vlan_number, [,$private_type
# [,$private_primary, $private_port]])
# returns the new VLAN number on success
# returns 0 on failure
# if $private_type is given, creates a private VLAN - if private_type
......@@ -545,6 +546,7 @@ sub findVlan($$;$) {
sub createVlan($$;$$$) {
my $self = shift;
my $vlan_id = shift;
my $vlan_number = shift;
my ($private_type,$private_primary,$private_port);
if (@_) {
......@@ -565,6 +567,16 @@ sub createVlan($$;$$$) {
my $VlanSAID = 'vtpVlanEditDot10Said'; # vlan # is index
my $VlanRowStatus = 'vtpVlanEditRowStatus'; # vlan # is index
#
# If they gave a VLAN number, make sure it exists
#
if ($vlan_number) {
if ($self->vlanNumberExists($vlan_number)) {
print STDERR "ERROR: VLAN $vlan_number already exists\n";
return 0;
}
}
#
# We may have to do this multiple times - a few times, we've had the
# Cisco give no errors, but fail to actually create the VLAN. So, we'll
......@@ -573,7 +585,6 @@ sub createVlan($$;$$$) {
#
my $max_tries = 3;
my $tries_remaining = $max_tries;
my $vlan_number;
while ($tries_remaining) {
#
# Try to wait out transient failures
......@@ -589,27 +600,29 @@ sub createVlan($$;$$$) {
next;
}
#
# Find a free VLAN number to use. Since 1 is the default VLAN on
# Ciscos, we start with number 2.
# XXX: The maximum VLAN number is hardcoded at 1000
#
$vlan_number = 2; # We need to start at 2
my $RetVal = snmpitGetFatal($self->{SESS},
[$VlanRowStatus,"1.$vlan_number"]);
$self->debug("Row $vlan_number got '$RetVal'\n",2);
while (($RetVal ne 'NOSUCHINSTANCE') && ($vlan_number <= 1000)) {
$vlan_number += 1;
$RetVal = snmpitGetFatal($self->{SESS},
[$VlanRowStatus,"1.$vlan_number"]);
$self->debug("Row $vlan_number got '$RetVal'\n",2);
}
if ($vlan_number > 1000) {
if (!$vlan_number) {
#
# We must have failed to find one
# Find a free VLAN number to use. Since 1 is the default VLAN on
# Ciscos, we start with number 2.
# XXX: The maximum VLAN number is hardcoded at 1000
#
print STDERR "ERROR: Failed to find a free VLAN number\n";
next;
$vlan_number = 2; # We need to start at 2
my $RetVal = snmpitGetFatal($self->{SESS},
[$VlanRowStatus,"1.$vlan_number"]);
$self->debug("Row $vlan_number got '$RetVal'\n",2);
while (($RetVal ne 'NOSUCHINSTANCE') && ($vlan_number <= 1000)) {
$vlan_number += 1;
$RetVal = snmpitGetFatal($self->{SESS},
[$VlanRowStatus,"1.$vlan_number"]);
$self->debug("Row $vlan_number got '$RetVal'\n",2);
}
if ($vlan_number > 1000) {
#
# We must have failed to find one
#
print STDERR "ERROR: Failed to find a free VLAN number\n";
next;
}
}
$self->debug("Using Row $vlan_number\n");
......@@ -626,7 +639,7 @@ sub createVlan($$;$$$) {
# Perform the actual creation. Yes, this next line MUST happen all in
# one set command....
#
$RetVal = $self->{SESS}->set([[$VlanRowStatus,"1.$vlan_number",
my $RetVal = $self->{SESS}->set([[$VlanRowStatus,"1.$vlan_number",
"createAndGo","INTEGER"],
[$VlanType,"1.$vlan_number","ethernet","INTEGER"],
[$VlanName,"1.$vlan_number",$vlan_id,"OCTETSTR"],
......
......@@ -45,6 +45,7 @@ sub new($$$#@) {
my $debuglevel = shift;
my $community = shift;
my $supports_private = shift;
my $uses_vtp = shift;
my @devicenames = @_;
#
......@@ -71,6 +72,11 @@ sub new($$$#@) {
#
@{$self->{DEVICENAMES}} = @devicenames;
#
# Whether or not this stack uses VTP to keep the VLANs synchronized
#
$self->{VTP} = $uses_vtp;
#
# Make a device-dependant object for each switch
#
......@@ -269,10 +275,40 @@ sub createVlan($$$;$$$) {
my @otherargs = @_;
#
# We just need to create the VLAN on the stack leader
# What we do here depends on whether this stack uses VTP to synchronize
# VLANs or not
#
my $vlan_number = $self->{LEADER}->createVlan($vlan_id,@otherargs);
my $okay = ($vlan_number != 0);
my $okay;
if ($self->{VTP}) {
#
# We just need to create the VLAN on the stack leader
#
#
my $vlan_number = $self->{LEADER}->createVlan($vlan_id,undef,@otherargs);
$okay = ($vlan_number != 0);
} else {
#
# We need to create the VLAN on all devices
# XXX - should we do the leader first?
#
my $vlan_number = undef;
foreach my $devicename (sort {tbsort($a,$b)} keys %{$self->{DEVICES}}) {
my $device = $self->{DEVICES}{$devicename};
my $res = $self->{LEADER}->createVlan($vlan_id,undef,@otherargs);
if (!$res) {
#
# Ooops, failed. Don't try any more
#
$okay = 0;
last;
} else {
#
# Use the VLAN number we just got back for the other switches
#
$vlan_number = $res;
}
}
}
#
# We need to add the ports to VLANs at the stack level, since they are
......@@ -422,27 +458,39 @@ sub removeVlan($@) {
}
$errors += $device->removePortsFromVlan(@existant_vlans);
#
# If this stack doesn't use VTP, delete the VLAN, too, while
# we're at it. If it does, we remove all VLANs down below (we can't
# do it until the ports have been cleared from all switches.)
#
if (!$self->{VTP}) {
my $ok = $device->removeVlan(@existant_vlans);
if (!$ok) { $errors++; }
}
}
#
# For efficiency, we remove all VLANs from the leader in one function
# call. This can save a _lot_ of locking and unlocking.
#
if (!$errors) {
if ($self->{VTP}) {
#
# Make a list of all the VLANs that really did exist
# For efficiency, we remove all VLANs from the leader in one function
# call. This can save a _lot_ of locking and unlocking.
#
my @vlan_numbers;
my ($key, $value);
while (($key, $value) = each %vlan_numbers) {
if ($value) {
push @vlan_numbers, $value;
if (!$errors) {
#
# Make a list of all the VLANs that really did exist
#
my @vlan_numbers;
my ($key, $value);
while (($key, $value) = each %vlan_numbers) {
if ($value) {
push @vlan_numbers, $value;
}
}
}
my $ok = $self->{LEADER}->removeVlan(@vlan_numbers);
if (!$ok) {
$errors++;
my $ok = $self->{LEADER}->removeVlan(@vlan_numbers);
if (!$ok) {
$errors++;
}
}
}
......
......@@ -332,19 +332,20 @@ sub getSwitchStack($) {
#
# Returns the type of the given stack_id. If called in list context, also
# returns whether or not the stack supports private VLANs
# returns whether or not the stack supports private VLANs, and whether it
# uses a single VLAN domain
#
sub getStackType($) {
my $stack = shift;
my $result = DBQueryFatal("SELECT stack_type, supports_private " .
"FROM switch_stack_types WHERE stack_id='$stack'");
my $result = DBQueryFatal("SELECT stack_type, supports_private, " .
" single_domain FROM switch_stack_types WHERE stack_id='$stack'");
if (!$result->numrows()) {
print STDERR "No stack found called $stack\n";
return undef;
} else {
my ($stack_type,$supports_private) = ($result->fetchrow());
my ($stack_type,$supports_private,$single_domain) = ($result->fetchrow());
if (defined wantarray) {
return ($stack_type,$supports_private);
return ($stack_type,$supports_private,$single_domain);
} else {
return $stack_type;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment