From eb4691d2d04b7a2227c7c7cc7e1a6dc56f0bff9c Mon Sep 17 00:00:00 2001 From: Robert Ricci Date: Sat, 8 Feb 2003 01:41:25 +0000 Subject: [PATCH] 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. --- sql/database-create.sql | 1 + tbsetup/snmpit.in | 5 ++- tbsetup/snmpit_cisco.pm | 61 +++++++++++++++---------- tbsetup/snmpit_cisco_stack.pm | 84 +++++++++++++++++++++++++++-------- tbsetup/snmpit_lib.pm | 11 ++--- 5 files changed, 113 insertions(+), 49 deletions(-) diff --git a/sql/database-create.sql b/sql/database-create.sql index 9614d23a7..a256d6dc1 100644 --- a/sql/database-create.sql +++ b/sql/database-create.sql @@ -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; diff --git a/tbsetup/snmpit.in b/tbsetup/snmpit.in index d5ece4a62..29babbe79 100755 --- a/tbsetup/snmpit.in +++ b/tbsetup/snmpit.in @@ -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 { diff --git a/tbsetup/snmpit_cisco.pm b/tbsetup/snmpit_cisco.pm index 300d72275..034f17853 100644 --- a/tbsetup/snmpit_cisco.pm +++ b/tbsetup/snmpit_cisco.pm @@ -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"], diff --git a/tbsetup/snmpit_cisco_stack.pm b/tbsetup/snmpit_cisco_stack.pm index 7eed1d9a5..70510fd10 100644 --- a/tbsetup/snmpit_cisco_stack.pm +++ b/tbsetup/snmpit_cisco_stack.pm @@ -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++; + } } } diff --git a/tbsetup/snmpit_lib.pm b/tbsetup/snmpit_lib.pm index e43be14b5..179d4488d 100644 --- a/tbsetup/snmpit_lib.pm +++ b/tbsetup/snmpit_lib.pm @@ -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; } -- GitLab