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 ( ...@@ -894,6 +894,7 @@ CREATE TABLE switch_stack_types (
stack_id varchar(10) NOT NULL default '', stack_id varchar(10) NOT NULL default '',
stack_type varchar(10) default NULL, stack_type varchar(10) default NULL,
supports_private tinyint(1) NOT NULL default '0', supports_private tinyint(1) NOT NULL default '0',
single_domain tinyint(1) NOT NULL default '1',
PRIMARY KEY (stack_id) PRIMARY KEY (stack_id)
) TYPE=MyISAM; ) TYPE=MyISAM;
......
...@@ -407,7 +407,8 @@ foreach my $command (@commands) { ...@@ -407,7 +407,8 @@ foreach my $command (@commands) {
# #
my @stacks; my @stacks;
foreach my $stack_id (keys %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 # Safety check - make sure the stack supports private VLANs if -y was
# given # given
...@@ -421,7 +422,7 @@ foreach my $command (@commands) { ...@@ -421,7 +422,7 @@ foreach my $command (@commands) {
/cisco/ && do { /cisco/ && do {
require snmpit_cisco_stack; require snmpit_cisco_stack;
$stack = new snmpit_cisco_stack($stack_id,$debug,$COMMUNITY, $stack = new snmpit_cisco_stack($stack_id,$debug,$COMMUNITY,
$supports_private, @{$stacks{$stack_id}}); $supports_private, $single_domain, @{$stacks{$stack_id}});
last; last;
}; # /cisco/ }; # /cisco/
/intel/ && do { /intel/ && do {
......
...@@ -532,10 +532,11 @@ sub findVlan($$;$) { ...@@ -532,10 +532,11 @@ sub findVlan($$;$) {
# #
# Create a VLAN on this switch, with the given identifier (which comes from # 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, # usage: createVlan($self, $vlan_id, $vlan_number, [,$private_type
# $private_port]]) # [,$private_primary, $private_port]])
# returns the new VLAN number on success # returns the new VLAN number on success
# returns 0 on failure # returns 0 on failure
# if $private_type is given, creates a private VLAN - if private_type # if $private_type is given, creates a private VLAN - if private_type
...@@ -545,6 +546,7 @@ sub findVlan($$;$) { ...@@ -545,6 +546,7 @@ sub findVlan($$;$) {
sub createVlan($$;$$$) { sub createVlan($$;$$$) {
my $self = shift; my $self = shift;
my $vlan_id = shift; my $vlan_id = shift;
my $vlan_number = shift;
my ($private_type,$private_primary,$private_port); my ($private_type,$private_primary,$private_port);
if (@_) { if (@_) {
...@@ -565,6 +567,16 @@ sub createVlan($$;$$$) { ...@@ -565,6 +567,16 @@ sub createVlan($$;$$$) {
my $VlanSAID = 'vtpVlanEditDot10Said'; # vlan # is index my $VlanSAID = 'vtpVlanEditDot10Said'; # vlan # is index
my $VlanRowStatus = 'vtpVlanEditRowStatus'; # 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 # 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 # Cisco give no errors, but fail to actually create the VLAN. So, we'll
...@@ -573,7 +585,6 @@ sub createVlan($$;$$$) { ...@@ -573,7 +585,6 @@ sub createVlan($$;$$$) {
# #
my $max_tries = 3; my $max_tries = 3;
my $tries_remaining = $max_tries; my $tries_remaining = $max_tries;
my $vlan_number;
while ($tries_remaining) { while ($tries_remaining) {
# #
# Try to wait out transient failures # Try to wait out transient failures
...@@ -589,27 +600,29 @@ sub createVlan($$;$$$) { ...@@ -589,27 +600,29 @@ sub createVlan($$;$$$) {
next; next;
} }
# if (!$vlan_number) {
# 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) {
# #
# 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"; $vlan_number = 2; # We need to start at 2
next; 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"); $self->debug("Using Row $vlan_number\n");
...@@ -626,7 +639,7 @@ sub createVlan($$;$$$) { ...@@ -626,7 +639,7 @@ sub createVlan($$;$$$) {
# Perform the actual creation. Yes, this next line MUST happen all in # Perform the actual creation. Yes, this next line MUST happen all in
# one set command.... # one set command....
# #
$RetVal = $self->{SESS}->set([[$VlanRowStatus,"1.$vlan_number", my $RetVal = $self->{SESS}->set([[$VlanRowStatus,"1.$vlan_number",
"createAndGo","INTEGER"], "createAndGo","INTEGER"],
[$VlanType,"1.$vlan_number","ethernet","INTEGER"], [$VlanType,"1.$vlan_number","ethernet","INTEGER"],
[$VlanName,"1.$vlan_number",$vlan_id,"OCTETSTR"], [$VlanName,"1.$vlan_number",$vlan_id,"OCTETSTR"],
......
...@@ -45,6 +45,7 @@ sub new($$$#@) { ...@@ -45,6 +45,7 @@ sub new($$$#@) {
my $debuglevel = shift; my $debuglevel = shift;
my $community = shift; my $community = shift;
my $supports_private = shift; my $supports_private = shift;
my $uses_vtp = shift;
my @devicenames = @_; my @devicenames = @_;
# #
...@@ -71,6 +72,11 @@ sub new($$$#@) { ...@@ -71,6 +72,11 @@ sub new($$$#@) {
# #
@{$self->{DEVICENAMES}} = @devicenames; @{$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 # Make a device-dependant object for each switch
# #
...@@ -269,10 +275,40 @@ sub createVlan($$$;$$$) { ...@@ -269,10 +275,40 @@ sub createVlan($$$;$$$) {
my @otherargs = @_; 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;
my $okay = ($vlan_number != 0); 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 # We need to add the ports to VLANs at the stack level, since they are
...@@ -422,27 +458,39 @@ sub removeVlan($@) { ...@@ -422,27 +458,39 @@ sub removeVlan($@) {
} }
$errors += $device->removePortsFromVlan(@existant_vlans); $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++; }
}
} }
# if ($self->{VTP}) {
# For efficiency, we remove all VLANs from the leader in one function
# call. This can save a _lot_ of locking and unlocking.
#
if (!$errors) {
# #
# 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; if (!$errors) {
my ($key, $value); #
while (($key, $value) = each %vlan_numbers) { # Make a list of all the VLANs that really did exist
if ($value) { #
push @vlan_numbers, $value; 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); my $ok = $self->{LEADER}->removeVlan(@vlan_numbers);
if (!$ok) { if (!$ok) {
$errors++; $errors++;
}
} }
} }
......
...@@ -332,19 +332,20 @@ sub getSwitchStack($) { ...@@ -332,19 +332,20 @@ sub getSwitchStack($) {
# #
# Returns the type of the given stack_id. If called in list context, also # 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($) { sub getStackType($) {
my $stack = shift; my $stack = shift;
my $result = DBQueryFatal("SELECT stack_type, supports_private " . my $result = DBQueryFatal("SELECT stack_type, supports_private, " .
"FROM switch_stack_types WHERE stack_id='$stack'"); " single_domain FROM switch_stack_types WHERE stack_id='$stack'");
if (!$result->numrows()) { if (!$result->numrows()) {
print STDERR "No stack found called $stack\n"; print STDERR "No stack found called $stack\n";
return undef; return undef;
} else { } else {
my ($stack_type,$supports_private) = ($result->fetchrow()); my ($stack_type,$supports_private,$single_domain) = ($result->fetchrow());
if (defined wantarray) { if (defined wantarray) {
return ($stack_type,$supports_private); return ($stack_type,$supports_private,$single_domain);
} else { } else {
return $stack_type; 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