#!/usr/bin/perl package xCAT_plugin::vlan::CiscoSwitch; BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; } use lib "$::XCATROOT/lib/perl"; use strict; use xCAT::MacMap; use Data::Dumper; use xCAT::SwitchHandler; use SNMP; #IF-MIB my $ifName='.1.3.6.1.2.1.31.1.1.1.1'; my $ifDescr='.1.3.6.1.2.1.2.2.1.2'; my $ifOperStatus='.1.3.6.1.2.1.2.2.1.8'; #CISCO-VTP-MIB my $vtpVlanEditTable='.1.3.6.1.4.1.9.9.46.1.4.2'; my $vtpVlanEditOperation='.1.3.6.1.4.1.9.9.46.1.4.1.1.1'; my $vtpVlanEditBufferOwner='.1.3.6.1.4.1.9.9.46.1.4.1.1.3'; my $vtpVlanEditRowStatus='.1.3.6.1.4.1.9.9.46.1.4.2.1.11.1'; my $vtpVlanEditType='.1.3.6.1.4.1.9.9.46.1.4.2.1.3.1'; my $vtpVlanEditName='.1.3.6.1.4.1.9.9.46.1.4.2.1.4.1'; my $vtpVlanEditDot10Said='.1.3.6.1.4.1.9.9.46.1.4.2.1.6.1'; my $vtpVlanState='.1.3.6.1.4.1.9.9.46.1.3.1.1.2'; my $vlanTrunkPortDynamicStatus='.1.3.6.1.4.1.9.9.46.1.6.1.1.14'; my $vlanTrunkPortDynamicState='.1.3.6.1.4.1.9.9.46.1.6.1.1.13'; my $vlanTrunkPortNativeVlan= '.1.3.6.1.4.1.9.9.46.1.6.1.1.5'; my $vlanTrunkPortVlansEnabled='.1.3.6.1.4.1.9.9.46.1.6.1.1.4'; my $vlanTrunkPortEncapsulationType='.1.3.6.1.4.1.9.9.46.1.6.1.1.3'; #CISCO-VLAN-MEMBERSHIP-MIB my $vmVlan='.1.3.6.1.4.1.9.9.68.1.2.2.1.2'; my $vmVlanType='.1.3.6.1.4.1.9.9.68.1.2.2.1.1'; #1:static 2:dynamic 3:trunk #CISCI-STP-EXTENSION-MIB my $stpxRootGuardConfigEnabled='1.3.6.1.4.1.9.9.82.1.5.1.1.2'; #1:enable 2:disable my $stpxFastStartPortBpduGuardMode='1.3.6.1.4.1.9.9.82.1.9.3.1.4'; #1:enable 2:disable 3:default my %HexConv=( 0=>0x80, 1=>0x40, 2=>0x20, 3=>0x10, 4=>0x08, 5=>0x04, 6=>0x02, 7=>0x01); #-------------------------------------------------------------- =head3 filter_string Every switch plugin must implement this subroutine. The return value will be used comare against the string from sysDescr value from snmpget. If the latter contains this string, then this mudule will be used to handle the requests for vlan configuration. =cut #------------------------------------------------------------- sub filter_string { return "Cisco "; } #-------------------------------------------------------------- =head3 get_vlan_ids Every switch plugin must implement this subroutine. It gets the existing vlan IDs for the switch. Returns: an array containing all the vlan ids for the switch =cut #------------------------------------------------------------- sub get_vlan_ids { my $session=shift; my $vlanmap = xCAT::MacMap::walkoid($session, "$vtpVlanState.1", silentfail=>1); my %vlanids=(); foreach(keys(%$vlanmap)) { $vlanids{$_}=1; } my @ret=(sort keys(%vlanids)); print "ids=@ret\n"; return @ret; } #-------------------------------------------------------------- =head3 get_vlanids_for_ports Every switch plugin must implement this subroutine. It returns a hash pointer that contains the vlan ids for each given port. The kay is the port, the vlaue is a pointer to an array. =cut #------------------------------------------------------------- sub get_vlanids_for_ports { my $session=shift; my @ports=@_; my $namemap = xCAT::MacMap::walkoid($session, $ifName); #namemap is the mapping of ifIndex->(human readable name) if ($namemap) { my $ifnamesupport=0; foreach (keys %{$namemap}) { if ($namemap->{$_}) { $ifnamesupport=1; last; } } unless ($ifnamesupport) { $namemap=0; } } unless ($namemap) { #Failback to ifDescr. ifDescr is close, but not perfect on some switches $namemap = xCAT::MacMap::walkoid($session,$ifDescr); } unless ($namemap) { return; } #print "namemap=" . Dumper($namemap) . "\n"; my $iftovlanmap = xCAT::MacMap::walkoid($session, $vmVlan, silentfail=>1); #print "iftovlanmap=" . Dumper($iftovlanmap) . "\n"; my $trunktovlanmap = xCAT::MacMap::walkoid($session, $vlanTrunkPortNativeVlan, silentfail=>1); #for trunk ports, we are interested in the native vlan #print "trunktovlanmap=" . Dumper($trunktovlanmap) . "\n"; my %ret=(); if (defined($iftovlanmap) or defined($trunktovlanmap)) { foreach my $portid (keys %{$namemap}) { my $switchport = $namemap->{$portid}; foreach my $portname (@ports) { unless (xCAT::MacMap::namesmatch($portname,$switchport)) { next; } if (defined $iftovlanmap->{$portid}) { $ret{$portname}=[$iftovlanmap->{$portid}]; } elsif (defined $trunktovlanmap->{$portid}){ $ret{$portname}=[$trunktovlanmap->{$portid}]; } else { $ret{$portname}=['NA']; } } } } return \%ret; } #-------------------------------------------------------------- =head3 create_vlan Every switch plugin must implement this subroutine. Creates a new vlan on the switch Returns an array. (erorcode, errormsg). When errorcode=0, means no error. =cut #------------------------------------------------------------- sub create_vlan { my $session=shift; my $vlan_id=shift; my $vlan_name="xcat_vlan_" . $vlan_id; print "Cisco\n"; #Verify if the edition is in use by another NMS station or device. #The edition is not in use if you see this message: no MIB objects contained under subtree: my $tmp = xCAT::MacMap::walkoid($session,$vtpVlanEditTable); #print "tmp=" . Dumper($tmp) . "\n"; #Set the vtpVlanEditOperation to the copy state(2). This allows to create the VLAN. my @ret= xCAT::SwitchHandler::setoid($session, $vtpVlanEditOperation, 1, 2, 'INTEGER'); #if ($ret[0] != 0) { return @ret; } #set vtpVlanEditBufferOwner in order to makethe current owner of the edit permission visible my $username="xcat"; my @ret=xCAT::SwitchHandler::setoid($session,$vtpVlanEditBufferOwner, 1, $username, 'OCTET'); if ($ret[0] != 0) { $ret[1]="Set vtpVlanEditBufferOwner $vtpVlanEditBufferOwner.1 to $username.\n" .$ret[1]; return @ret; } #my $tmp = xCAT::MacMap::walkoid($session, $vtpVlanEditOperation); #print "tmp=" . Dumper($tmp) . "\n"; #my $tmp = xCAT::MacMap::walkoid($session, $vtpVlanEditBufferOwner); #print "tmp=" . Dumper($tmp) . "\n"; #set vtpVlanEditRowStatus to createAndGo(4) my @ret= xCAT::SwitchHandler::setoid($session, $vtpVlanEditRowStatus, $vlan_id, 4, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set vtpVlanEditRowStatus $vtpVlanEditRowStatus.$vlan_id to 4.\n" . $ret[1]; return @ret; } #set vtpVlanEditType to ethernet (1) my @ret= xCAT::SwitchHandler::setoid($session,$vtpVlanEditType, $vlan_id, 1, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set vtpVlanEditType $vtpVlanEditType.$vlan_id to 1.\n" . $ret[1]; return @ret; } #set vtpVlanEditName to xcat_vlan_# my @ret= xCAT::SwitchHandler::setoid($session,$vtpVlanEditName, $vlan_id, $vlan_name, 'OCTET'); if ($ret[0] != 0) { $ret[1]="Set vtpVlanEditName $vtpVlanEditName.$vlan_id to $vlan_name.\n" . $ret[1]; return @ret; } #Set the vtpVlanEditDot10Said. This is the VLAN number + 100000 translated to hexadecimal. my $num=100000 + $vlan_id; my $hex_num=sprintf("%x", $num); my @ret= xCAT::SwitchHandler::setoid($session,$vtpVlanEditDot10Said, $vlan_id, $hex_num, 'OCTETHEX'); #if ($ret[0] != 0) { # $ret[1]="Set vtpVlanEditDot10Said $vtpVlanEditDot10Said.$vlan_id to $hex_num.\n" . $ret[1]; #return @ret; #} #apply the changes, set vtpVlanEditOperation.1 to apply (3) my @ret= xCAT::SwitchHandler::setoid($session, $vtpVlanEditOperation, 1, 3, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set vtpVlanEditOperation $vtpVlanEditOperation.1 to 3.\n" . $ret[1]; return @ret; } #release the edition buffer, set vtpVlanEditOperation.1 to release (4) my @ret= xCAT::SwitchHandler::setoid($session, $vtpVlanEditOperation, 1, 4, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set vtpVlanEditOperation $vtpVlanEditOperation.1 to 4.\n" . $ret[1]; return @ret; } return (0, ""); } #-------------------------------------------------------------- =head3 add_ports_to_vlan Every switch plugin must implement this subroutine. Adds the given ports to the existing vlan Returns an array. (erorcode, errormsg). When errorcode=0, means no error. =cut #------------------------------------------------------------- sub add_ports_to_vlan { my $session=shift; my $vlan_id=shift; # If portmode is set, we'll set the switchport to untagged(access) mode. my $portmode=shift; my @ports=@_; #print "Add ports @ports to vla $vlan_id.\n"; #add ports to the vlan. if (@ports > 0) { my $namemap = xCAT::MacMap::walkoid($session,$ifName); #namemap is the mapping of ifIndex->(human readable name) if ($namemap) { my $ifnamesupport=0; #Assume broken ifnamesupport until proven good... (Nortel switch) foreach (keys %{$namemap}) { if ($namemap->{$_}) { $ifnamesupport=1; last; } } unless ($ifnamesupport) { $namemap=0; } } unless ($namemap) { #Failback to ifDescr. ifDescr is close, but not perfect on some switches $namemap = xCAT::MacMap::walkoid($session, $ifDescr); } unless ($namemap) { return; } #print "namemap=" . Dumper($namemap) . "\n"; my $trunkportstate = xCAT::MacMap::walkoid($session,$vlanTrunkPortDynamicState); #print "trunkportstate=" . Dumper($trunkportstate) . "\n"; my $iftovlanmap = xCAT::MacMap::walkoid($session, $vmVlan, silentfail=>1); #print "iftovlanmap=" . Dumper($iftovlanmap) . "\n"; my $trunktovlanmap = xCAT::MacMap::walkoid($session, $vlanTrunkPortNativeVlan, silentfail=>1); #print "trunktovlanmap=" . Dumper($trunktovlanmap) . "\n"; #get current vlan type (static, dynamic or trunk) my $vlantypemap = xCAT::MacMap::walkoid($session, $vmVlanType, silentfail=>1); #print "vlantypemap=" . Dumper($vlantypemap) . "\n"; foreach my $portid (keys %{$namemap}) { my $switchport = $namemap->{$portid}; foreach my $portname (@ports) { unless (xCAT::MacMap::namesmatch($portname,$switchport)) { next; } ### set the port to trunk mode print "portid=$portid\n"; # TODO: need testing in cisco's env for how to set a port as access mode through snmpset? # Can setting vlanTrunkPortDot1qTunnel or cltcDot1qTunnelMode helps? if ($trunkportstate->{$portid} != 1) { my @ret = xCAT::SwitchHandler::setoid($session, $vlanTrunkPortEncapsulationType, $portid, 4, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set vlanTrunkPortEncapsulationType $vlanTrunkPortEncapsulationType.$portid to 4(dot1Q).\n" . $ret[1]; return @ret; } my @ret = xCAT::SwitchHandler::setoid($session, $vlanTrunkPortDynamicState, $portid, 1, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set vlanTrunkPortDynamicState $vlanTrunkPortDynamicState.$portid to 1.\n" . $ret[1]; return @ret; } } ### set trunk native vlan id my $native_vlanid=1; if ((exists($vlantypemap->{$portid})) && ($vlantypemap->{$portid} < 3)) { #the port was set to access mode before if ((exists($iftovlanmap->{$portid})) && ($iftovlanmap->{$portid} > 0)) { $native_vlanid=$iftovlanmap->{$portid}; } } else { #the port is originally set to trunk mode if ((exists($trunktovlanmap->{$portid})) && ($trunktovlanmap->{$portid} > 0)) { $native_vlanid=$trunktovlanmap->{$portid}; } elsif ((exists($iftovlanmap->{$portid})) && ($iftovlanmap->{$portid} > 0)) { $native_vlanid=$iftovlanmap->{$portid}; } } print "*** native_vlanid=$native_vlanid\n"; my @ret = xCAT::SwitchHandler::setoid($session, $vlanTrunkPortNativeVlan, $portid, $native_vlanid, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set native vlan for port $portid to $native_vlanid ($vlanTrunkPortNativeVlan.$portid to $native_vlanid).\n" . $ret[1]; return @ret; } ### allow this vlan on the port my $data = $session->get([$vlanTrunkPortVlansEnabled, $portid]); my @a = split(//, $data); foreach (@a) { my $num=unpack("C*", $_); $_= sprintf ("%02x",$num); } if ((exists($vlantypemap->{$portid})) && ($vlantypemap->{$portid} < 3)) { #if originally this port was in access or dynamic mode, only enable original vlan and this tagged vlan #reset the matrix #print "***was access mode\n"; foreach (@a) { $_="00"; } #add native vlan id in the matrix my $index = int($native_vlanid / 8); my $offset = $native_vlanid % 8; my $num = hex($a[$index]) | $HexConv{$offset}; $a[$index] = sprintf("%02x", $num); #print "index=$index, offset=$offset\n"; #add current vlan id in the matrix $index = int($vlan_id / 8); $offset = $vlan_id % 8; $num = hex($a[$index]) | $HexConv{$offset}; $a[$index] = sprintf("%02x", $num); } else { #if this port was trunk mode before, add this tagged vlan in the matrix # print "***was trunk mode\n"; my $index = int($vlan_id / 8); my $offset = $vlan_id % 8; my $num = hex($a[$index]) | $HexConv{$offset}; $a[$index] = sprintf("%02x", $num); } #print "a=@a\n"; foreach (@a) { $_=hex($_); $_=pack("C*", $_); } my $s = join(//, @a); my @ret = xCAT::SwitchHandler::setoid($session, $vlanTrunkPortVlansEnabled, $portid, $s, 'OCTET'); #print "**** ret=@ret\n"; if ($ret[0] != 0) { $ret[1]="Allow vlan on port $portid ($vlanTrunkPortVlansEnabled.$portid=$s).\n" . $ret[1]; return @ret; } ### security feature if ($::XCATSITEVALS{vlansecurity} eq '1') { #set root guard on the port my @ret = xCAT::SwitchHandler::setoid($session, $stpxRootGuardConfigEnabled, $portname, 1, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set root guard for port $portname to enable ($stpxRootGuardConfigEnabled.$portname=1).\n" . $ret[1]; return @ret; } #set bpdu guard on the port my @ret = xCAT::SwitchHandler::setoid($session, $stpxFastStartPortBpduGuardMode, $portname, 1, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set bpdu guard for port $portname to enalbe ($stpxFastStartPortBpduGuardMode.$portname=1).\n" . $ret[1]; return @ret; } } last; } } } return (0, ""); } #------------------------------------------------------- =head3 add_crossover_ports_to_vlan It enables the vlan on the cross-over links. Returns an array. (erorcode, errormsg). When errorcode=0, means no error. =cut #------------------------------------------------------- sub add_crossover_ports_to_vlan { my $session=shift; my $vlan_id=shift; my $switch=shift; my @switches=@_; my @ret=(0, ""); my $msg; if (@switches == 0 ) { return (0, ""); } #get the ports that are connects to the switches my $switchestab=xCAT::Table->new('switches',-create=>0); my $ent = $switchestab->getNodeAttribs($switch, [qw(switch linkports)]); if ((!$ent) || (! $ent->{linkports})) { return (0, $msg); } my %linkports=(); foreach my $item (split(',',$ent->{linkports})) { my @a=split(':', $item); if (@a>1) { $linkports{$a[1]}=$a[0]; } } #print Dumper(%linkports); my @ports=(); foreach my $sw (@switches) { if (exists($linkports{$sw})) { push(@ports, $linkports{$sw}); } } #print "ports=@ports\n"; #now add the ports to the vlan if (@ports > 0) { my ($code, $msg1) = add_ports_to_vlan($session, $vlan_id, @ports); if ($msg) { $msg1 = $msg . $msg1; } return ($code, $msg1); } return (0, $msg); } #-------------------------------------------------------------- =head3 remove_vlan Every switch plugin must implement this subroutine. Remove a vlan from the switch Returns an array. (erorcode, errormsg). When errorcode=0, means no error. =cut #------------------------------------------------------------- sub remove_vlan { my $session=shift; my $vlan_id=shift; #Verify if the edition is in use by another NMS station or device. #The edition is not in use if you see this message: no MIB objects contained under subtree: my $tmp = xCAT::MacMap::walkoid($session,$vtpVlanEditTable); #print "tmp=" . Dumper($tmp) . "\n"; #Set the vtpVlanEditOperation to the copy state(2). This allows to delete the VLAN. my @ret= xCAT::SwitchHandler::setoid($session, $vtpVlanEditOperation, 1, 2, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set vtpVlanEditOperation $vtpVlanEditOperation.1 to 3 (copy state).\n" . $ret[1]; return @ret; } #set vtpVlanEditBufferOwner in order to makethe current owner of the edit permission visible my $username="xcat"; my @ret=xCAT::SwitchHandler::setoid($session,$vtpVlanEditBufferOwner, 1, $username, 'OCTET'); if ($ret[0] != 0) { $ret[1]="Set vtpVlanEditBufferOwner $vtpVlanEditBufferOwner.1 to $username.\n" . $ret[1]; return @ret; } #set vtpVlanEditRowStatus to destroy(6) my @ret= xCAT::SwitchHandler::setoid($session, $vtpVlanEditRowStatus, $vlan_id, 6, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set vtpVlanEditRowStatus $vtpVlanEditRowStatus.$vlan_id to 6 (destroy).\n" . $ret[1]; return @ret; } #apply the changes, set vtpVlanEditOperation.1 to apply (3) my @ret= xCAT::SwitchHandler::setoid($session, $vtpVlanEditOperation, 1, 3, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set $vtpVlanEditOperation.1 to 3 (apply).\n" . $ret[1]; return @ret; } #release the edition buffer, set vtpVlanEditOperation.1 to release (4) my @ret= xCAT::SwitchHandler::setoid($session, $vtpVlanEditOperation, 1, 4, 'INTEGER'); if ($ret[0] != 0) { $ret[1]="Set vtpVlanEditOperation $vtpVlanEditOperation.1 to 4 (release).\n" . $ret[1]; return @ret; } #my $iftovlanmap = xCAT::MacMap::walkoid($session, $vmVlan, silentfail=>1); #print "iftovlanmap=" . Dumper($iftovlanmap) . "\n"; #foreach (keys(%$iftovlanmap)) { # if($iftovlanmap->{$_} == $vlan_id) { #my @ret= xCAT::SwitchHandler::setoid($session, $vmVlan, $_, 1, 'INTEGER'); #if ($ret[0] != 0) { # $ret[1]="Set $vmVlan.$_ to 1.\n" . $ret[1]; # return @ret; #} # } #} #my $trunktovlanmap = xCAT::MacMap::walkoid($session, $vlanTrunkPortNativeVlan, silentfail=>1); #for trunk ports, we are interested in the native vlan #print "trunktovlanmap=" . Dumper($trunktovlanmap) . "\n"; #foreach (keys(%$trunktovlanmap)) { # if($trunktovlanmap->{$_} == $vlan_id) { # my @ret= xCAT::SwitchHandler::setoid($session, $vlanTrunkPortNativeVlan, $_, 1, 'INTEGER'); # if ($ret[0] != 0) { # $ret[1]="Set $vlanTrunkPortNativeVlan.$_ to 1.\n" . $ret[1]; # return @ret; # } # } #} return (0, "");; } #-------------------------------------------------------------- =head3 remove_ports_from_vlan Every switch plugin must implement this subroutine. Remove ports from a vlan Returns an array. (erorcode, errormsg). When errorcode=0, means no error. =cut #------------------------------------------------------------- sub remove_ports_from_vlan { my $session=shift; my $vlan_id=shift; my @ports = @_; if (@ports > 0) { my $namemap = xCAT::MacMap::walkoid($session,$ifName); #namemap is the mapping of ifIndex->(human readable name) if ($namemap) { my $ifnamesupport=0; foreach (keys %{$namemap}) { if ($namemap->{$_}) { $ifnamesupport=1; last; } } unless ($ifnamesupport) { $namemap=0; } } unless ($namemap) { #Failback to ifDescr. ifDescr is close, but not perfect on some switches $namemap = xCAT::MacMap::walkoid($session, $ifDescr); } unless ($namemap) { return; } #print "namemap=" . Dumper($namemap) . "\n"; foreach my $portid (keys %{$namemap}) { my $switchport = $namemap->{$portid}; foreach my $portname (@ports) { unless (xCAT::MacMap::namesmatch($portname,$switchport)) { next; } #remove the vlan id from the vlanTrunkPortVlansEnabled.port matrix my $data = $session->get([$vlanTrunkPortVlansEnabled, $portid]); my @a = split(//, $data); foreach (@a) { my $num=unpack("C*", $_); $_= sprintf ("%02x",$num); } #print "a=@a\n"; my $index = int($vlan_id / 8); my $offset = $vlan_id % 8; my $num = hex($a[$index]) & (~$HexConv{$offset}); $a[$index] = sprintf("%02x", $num); #print "a=@a\n"; foreach (@a) { $_=hex($_); $_=pack("C*", $_); } my $s = join(//, @a); my @ret = xCAT::SwitchHandler::setoid($session, $vlanTrunkPortVlansEnabled, $portid, $s, 'OCTET'); if ($ret[0] != 0) { $ret[1]="Set vlanTrunkPortVlansEnabled $vlanTrunkPortVlansEnabled.$portid to $s.\n" . $ret[1]; return @ret; } last; } } } return (0, "");; } 1;