08-23-2018 02:58 AM - edited 03-08-2019 03:58 PM
How do I conclusively identify if a VLAN is applied to a trunk port?
I'm trying to configure a compliance policy in PRIME Infrastructure 3.2. I can base it on the running-config, or the output of a command (I'm a bit new to PRIME compliance policies/profiles... there might be other ways but this is all I've figured out so far).
The specific policy I'm trying to determine, is if there is a VLAN defined, but not applied to any physical switchport (I'm only concerning myself with transparent bridges, specifically Catalyst 3850 and 9300 running 16.6).
I'm not worried about removing VLANs from a port that's no longer in-use (either the port no longer in use, or the VLAN no longer needed on a trunk). That will be a compliance profile developed later.
I've pretty much got access ports licked; I'm not worrying about nuances regarding ports configured with switchport monitor or switchport capture; I'm not worrying about the trunk native VLANs or voice VLANs; and I'm assuming zero private vlans on any campus.
But how do I identify trunk ports that use a VLAN?
Specifically, if I were to issue any combination of "show" command (though I'm open to any suggestion including non-show commands, or a different approach within PRIME compliance), I want to accept VLAN 700 as "in-use" by these three ports. How would I do that?
interface GigabitEthernet1/0/1
switchport mode trunk
switchport trunk allowed vlan 699-701
shutdown
!
interface GigabitEthernet1/0/2
description Port is either not patched, or other end is turned off
switchport mode trunk
switchport trunk allowed vlan 699-701
no shutdown
!
interface GigabitEthernet1/0/3
description Port is in state "up/up"
switchport mode trunk
switchport trunk allowed vlan 699-701
no shutdown
Any ideas?
weylin
Solved! Go to Solution.
08-23-2018 05:40 AM
Or, you can write a tcl script to perform a "show" in for loop and ... do something.
Regards.
11-23-2019 12:28 PM
My first attempt I would compare to software that a high schooler might produce. I decided I didn't like it, so I re-wrote it (also, a software upgrade broke the script, so I needed to anyway).
This also catches the issue about this interface config:
! switchport access 1 is default, so not shown ! therefor this is an access port on vlan 1, and the "switchport trunk" command is non-functional interface Gi0/0/1 switchport mode access switchport trunk vlan allowed 17-19
shutdown
This script to me feels like 2nd- or 3rd-year undergrad quality.
#!/usr/bin/tclsh # no idea why: hast-tags at the beginning of the line is treated as an invalid command in Cisco's TCL shell # luckily it has effect on the actual execution of the script and the error can be safely ignored ########################################### ########################################### ### ATTENTION USERS OF THIS SCRIPT ### ATTENTION USERS OF THIS SCRIPT ### ATTENTION USERS OF THIS SCRIPT ### ### change this list of VLANs to define search criteria ### this should (in theory) be the only change necessary ### all other criteria should be auto-detected ### ### example: ### set VLAN_MATCH_LIST {367 368 369 703} ########################################### ########################################### set VLAN_MATCH_LIST {27 367 368 369 703} ########################################### ########################################### ### ATTENTION USERS OF THIS SCRIPT ### ATTENTION USERS OF THIS SCRIPT ### ATTENTION USERS OF THIS SCRIPT ### ### once VLAN_MATCH_LIST is defined, ### copy/paste this entire script into tclsh of any NX-OS device ### it will then auto-execute with no further input required ########################################### ########################################### # return the access vlan applied to interface # this will happily execute on a non-access interface and return results, it's incumbent on the calling function to check for that proc getaccessvlan {iFace} { set FullOpState [cli "show interface $iFace switchport"] regexp -line -all {Access Mode VLAN.*} $FullOpState OpStatement set AccessVLAN [regsub {.*Access Mode VLAN: ([[:digit:]]*).*} $OpStatement {\1}] return $AccessVLAN } # return a list of VLANs in vlan_list that are applied trunk-mode interface # this will happily execute on a non-trunk interface and return results, it's incumbent on the calling function to check for that proc gettrunkvlanlist {interface vlan_list} { set return_list {} # get list of VLANs in Cisco-format set FullOpState [cli "show interface $interface switchport"] regexp -line -all {Trunking VLANs Allowed.*} $FullOpState OpStatement set InterfaceVlans [regsub {.*Trunking VLANs Allowed: ([[:digit:]\,\-]*).*} $OpStatement {\1}] # expand VLAN ranges into individual VLANs # end result is comma-delimited list of VLANs applied to the interface foreach VlanGroup [split [string trim $InterfaceVlans] ","] { if {[llength [split $VlanGroup "-"]] == 1} { # individual VLAN lappend InterfaceVlansExpanded $VlanGroup } else { # VLAN range set FirstVLAN [lindex [split $VlanGroup -] 0] set LastVLAN [lindex [split $VlanGroup -] 1] for {set i $FirstVLAN} {$i <= $LastVLAN} {incr i} { lappend InterfaceVlansExpanded $i } } } # InterfaceVlansExpanded is now a list of VLANs (1 VLAN per list element) foreach VLAN $InterfaceVlansExpanded { if {[lsearch $vlan_list $VLAN] != -1} { lappend return_list $VLAN } } return $return_list } puts [gettrunkvlanlist Eth1/48 $VLAN_MATCH_LIST] # get the operational mode of an interface proc GetInterfaceMode {iFace} { set FullOpState [cli "show interface $iFace switchport"] regexp -line -all {Operational Mode.*} $FullOpState ModeStatement set OpMode [regsub {Operational Mode: (.*)} $ModeStatement {\1}] return $OpMode } # get the list of physical interfaces on the system, return them as a list proc GetInterfaces {} { set RunningConfig [cli "show running-config"] foreach InterfaceInConfig [regexp -inline -line -all {^interface Eth.*} $RunningConfig] { lappend ListOfInterfaces [regsub {.*(Ethernet\w*)} $InterfaceInConfig {\1}] } return $ListOfInterfaces } # begin main function set output {"================\nSTART EVALUATION HERE\n================"} foreach interface [GetInterfaces] { puts "evaluating interface $interface\n" set int_mode [GetInterfaceMode $interface] puts "Interface mode: $int_mode" if {$int_mode eq "access"} { set access_vlan [getaccessvlan $interface] puts "Access VLAN: $access_vlan" if {[lsearch $VLAN_MATCH_LIST $access_vlan] != -1} { lappend output "$interface is an access port on VLAN of concern: $access_vlan" } } elseif {$int_mode eq "trunk"} { set trunk_matched_vlan_list [gettrunkvlanlist $interface $VLAN_MATCH_LIST] puts "VLANs matched on trunk: $trunk_matched_vlan_list" if {[llength $trunk_matched_vlan_list] > 0} { lappend output "$interface is a trunk port, that includes these VLANs of concern: $trunk_matched_vlan_list" } } elseif {$int_mode eq "fex-fabric"} { continue } elseif {$int_mode eq "FabricPath"} { continue } else { puts "$interface indeterminate mode" lappend output "$interface is indeterminate" } } #puts $output foreach line $output { puts "$line" }
08-23-2018 03:40 AM
Hello
@weylin.piegorsch wrote:
How do I conclusively identify if a VLAN is applied to a trunk port?
I'm not worried about removing VLANs from a port that's no longer in-use (either the port no longer in use, or the VLAN no longer needed on a trunk).
But how do I identify trunk ports that use a VLAN?
sh interface trunk <--- will show you what vlans are allowed to traverse the, that are currently active and the trunk ports relative spanning-tree state
sh vlan brief <----- will show what ports are assigned to each vlan
sh interface switchport | in Trunk|Prun <---- What vlans are allowed to traverse the trunk and what vlans are edgible to be pruned.
08-23-2018 04:30 AM
Any interesting start, but incomplete. Your suggestion could be made even easier by testing if there are any interfaces on that VLAN in the STP domain.
nseg-lab-es15#show spanning-tree vlan 700
VLAN0700
Spanning tree enabled protocol rstp
Root ID Priority 12988
Address 001e.f6e2.c400
Cost 20000
Port 25 (GigabitEthernet1/1/1)
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Bridge ID Priority 33468 (priority 32768 sys-id-ext 700)
Address 6899.cde6.6780
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Aging Time 300 sec
Interface Role Sts Cost Prio.Nbr Type
------------------- ---- --- --------- -------- --------------------------------
Gi1/0/7 Desg FWD 20000 128.7 P2p Edge
Gi1/0/8 Desg FWD 20000 128.8 P2p Edge
Gi1/1/1 Root FWD 20000 128.25 P2p
nseg-lab-es15#
That's an easy test for ports that are in an "up/up" state. But what about ports that are shutdown? They won't be in the spanning-tree domain. This next example is on the same switch, you can see above that Gi1/1/2 is not in spanning-tree on this VLAN.
nseg-lab-es15#show running-config interface GigabitEthernet 1/1/2
Building configuration...
Current configuration : 388 bytes
!
interface GigabitEthernet1/1/2
switchport trunk native vlan 4
switchport trunk allowed vlan 699-701
switchport mode trunk
switchport nonegotiate
device-tracking attach-policy DT_NETWORK
shutdown
history BPS all
udld port aggressive
no vtp
spanning-tree guard root
service-policy input qos-ingress-marking
service-policy output qos-egress-uplink
ip dhcp snooping trust
end
nseg-lab-es15#
And, that same port doesn't show anything in "show interface trunk":
nseg-lab-es15#show interfaces GigabitEthernet 1/1/2 trunk
Port Mode Encapsulation Status Native vlan
Gi1/1/2 on 802.1q other 4
Port Vlans allowed on trunk
Gi1/1/2 none
Port Vlans allowed and active in management domain
Gi1/1/2 none
Port Vlans in spanning tree forwarding state and not pruned
Gi1/1/2 none
nseg-lab-es15#
"show vlan brief" doesn't highlight this either:
nseg-lab-es15#show vlan brief | section ^700
700 VLAN0700 active Gi1/0/2, Gi1/0/3, Gi1/0/7, Gi1/0/8
nseg-lab-es15#
"show interface switchport" shows this, unfortunately while I can VISUALLY see it, I need something I can parse against a Regular Expression to test true/false if the VLAN is applied to the port. "700" doesn't appear in the output (see highlighted line).
nseg-lab-es15#show interfaces GigabitEthernet 1/1/2 switchport | include Trunk|Prun
Administrative Trunking Encapsulation: dot1q
Negotiation of Trunking: Off
Trunking Native Mode VLAN: 4 (Unused_Ports, Suspended)
Trunking VLANs Enabled: 699-701
Pruning VLANs Enabled: 2-1001
nseg-lab-es15#
I'm flummoxed at this point, and am open to any ideas.
weylin
08-23-2018 05:21 AM
What about "show vlan" ?
The output is similar to this:
VLAN Name Status Ports
---- -------------------------------- --------- -------------------------------
1 default active Fa0/8, Fa0/11, Fa0/17
7 comm active Fa0/25
37 voice active
40 VOIP2 active Fa0/8, Fa0/11, Fa0/17, Fa0/18, Fa0/19, Fa0/20, Fa0/21, Fa0/22, Fa0/40
In this way you can see global configured vlan not used by any ports (in tmy example is the vlan 37).
Regards.
08-23-2018 05:33 AM
If you want an output with the list of active vlan configured on a port, you can use this statement:
show vlan brief | include ^3 | active .
(there are 4 space between "active" and the dot)
show vlan brief | include ^3 | active [A-Z]
(there are 4 space between "active" and the square)
show vlan brief | include ^3 | active.*[0-9]
Regards.
08-23-2018 05:40 AM
Or, you can write a tcl script to perform a "show" in for loop and ... do something.
Regards.
05-20-2019 07:17 AM
I ended up doing exactly that - writing a TCL script. It's not perfect, but it got a similar job done that I pressingly needed it for.
That other task: write a change, that would be covered by my organization's formal change control process, to convert set of VLANs from {set a} to {set b}, where {set a} and {set b} had a strict 1:1 mapping. This was for a Nexus environment using FEX and FabricPath, so the available switchport modes I needed to contend with were access, trunk, fabricpath, and fex-fabric. It helped that all VLANs 1-4094 were configured, and pre-defined as mode fabricpath.
For this, I didn't need the script to identify the old -> new configuration, I just needed to identify mode trunk and mode access ports that were using VLANs in {set a}, and which VLANs each port used; and I could ignore ports that were mode fabricpath or mode fex-fabric.
Here's the script I rote. Again - there's issues with the script and it would need to be cleaned up to use on a recurring basis. But I only needed it one-time, and I haven't written "real" code in the 20years since I graduated college, so whatever.
This will output all the intermediary processing, since this is an in-line script. invoking tclsh with a URL would drop all that, but due to the restrictions organizationally imposed for formal change control, I needed to avoid reliance on a remote (tftp:// or scp:// or whatever) or local (sup-bootdisk:// etc) datastore.
The command "cli" could be replaced with "exec" to make it more portable... eh good enough for here.
# no idea why, but every time a CLI output is captured, the first line never evaluates properly # this script accounts for that by including a single extra line of input, and discarding it. # # the "counter" trick used here is ugly, but works since the loops always re-start when counter=1 set VLAN_SET_A [list 102 122 232 242 243 246 259 283 286 288 334 341 346 356 358 373 375 385 405 406 410 411 421] proc getaccessvlan {interface} { set counter 0 foreach line [cli "show interface $interface switchport | inc Switchport:|Access.Mode.VLAN | cut -d ' ' -f 6"] { if {$counter == 0} { set counter 1 continue } return [string trim $line] } } proc gettrunkvlanlist {interface} { global VLAN_SET_A set return_list {} set counter 0 foreach line [cli "show interface $interface switchport | inc Switchport:|Trunking.VLANs.Allowed | cut -d ' ' -f 6"] { if {$counter == 0} { set counter 1 continue } foreach vlan_in_list [split [string trim $line] ","] { if {[llength [split $vlan_in_list "-"]] == 1} { if {[lsearch $VLAN_SET_A $vlan_in_list] != -1} { lappend return_list $vlan_in_list } } else { set vlan_to_add [lindex [split $vlan_in_list "-"] 0] while {$vlan_to_add <= [lindex [split $vlan_in_list "-"] 1]} { if {[lsearch $VLAN_SET_A $vlan_to_add] != -1} { lappend return_list $vlan_to_add } incr vlan_to_add } } } } return $return_list } set output {"================\nSTART EVALUATION HERE\n================"} # begin main function set counter 0 foreach interface [cli "sh int status | inc ^\[EP\] | egrep inv \"f-path| 1 \" | cut -d ' ' -f 1"] { if {$counter == 0} { set counter 1 continue } set counter 0 puts "evaluating interface $interface" foreach int_mode [cli "show interface $interface switchport | inc \"Operational.Mode:|Switchport:\" | cut -d ' ' -f 5"] { if {$counter == 0} { set counter 1 continue } else { set int_mode [string trim "$int_mode"] if {$int_mode eq "access"} { set access_vlan [getaccessvlan "$interface"] if {[lsearch $VLAN_SET_A $access_vlan] != -1} { lappend output "$interface is an access port on VLAN of concern: $access_vlan" } } elseif {$int_mode eq "trunk"} { set trunk_vlan_list [gettrunkvlanlist "$interface"] if {[llength $trunk_vlan_list] > 0} { lappend output "$interface is a trunk port, that includes these VLANs of concern: $trunk_vlan_list" } } else { lappend output "$interface is indeterminate" } } } } foreach line $output { puts "$line" }
11-23-2019 12:28 PM
My first attempt I would compare to software that a high schooler might produce. I decided I didn't like it, so I re-wrote it (also, a software upgrade broke the script, so I needed to anyway).
This also catches the issue about this interface config:
! switchport access 1 is default, so not shown ! therefor this is an access port on vlan 1, and the "switchport trunk" command is non-functional interface Gi0/0/1 switchport mode access switchport trunk vlan allowed 17-19
shutdown
This script to me feels like 2nd- or 3rd-year undergrad quality.
#!/usr/bin/tclsh # no idea why: hast-tags at the beginning of the line is treated as an invalid command in Cisco's TCL shell # luckily it has effect on the actual execution of the script and the error can be safely ignored ########################################### ########################################### ### ATTENTION USERS OF THIS SCRIPT ### ATTENTION USERS OF THIS SCRIPT ### ATTENTION USERS OF THIS SCRIPT ### ### change this list of VLANs to define search criteria ### this should (in theory) be the only change necessary ### all other criteria should be auto-detected ### ### example: ### set VLAN_MATCH_LIST {367 368 369 703} ########################################### ########################################### set VLAN_MATCH_LIST {27 367 368 369 703} ########################################### ########################################### ### ATTENTION USERS OF THIS SCRIPT ### ATTENTION USERS OF THIS SCRIPT ### ATTENTION USERS OF THIS SCRIPT ### ### once VLAN_MATCH_LIST is defined, ### copy/paste this entire script into tclsh of any NX-OS device ### it will then auto-execute with no further input required ########################################### ########################################### # return the access vlan applied to interface # this will happily execute on a non-access interface and return results, it's incumbent on the calling function to check for that proc getaccessvlan {iFace} { set FullOpState [cli "show interface $iFace switchport"] regexp -line -all {Access Mode VLAN.*} $FullOpState OpStatement set AccessVLAN [regsub {.*Access Mode VLAN: ([[:digit:]]*).*} $OpStatement {\1}] return $AccessVLAN } # return a list of VLANs in vlan_list that are applied trunk-mode interface # this will happily execute on a non-trunk interface and return results, it's incumbent on the calling function to check for that proc gettrunkvlanlist {interface vlan_list} { set return_list {} # get list of VLANs in Cisco-format set FullOpState [cli "show interface $interface switchport"] regexp -line -all {Trunking VLANs Allowed.*} $FullOpState OpStatement set InterfaceVlans [regsub {.*Trunking VLANs Allowed: ([[:digit:]\,\-]*).*} $OpStatement {\1}] # expand VLAN ranges into individual VLANs # end result is comma-delimited list of VLANs applied to the interface foreach VlanGroup [split [string trim $InterfaceVlans] ","] { if {[llength [split $VlanGroup "-"]] == 1} { # individual VLAN lappend InterfaceVlansExpanded $VlanGroup } else { # VLAN range set FirstVLAN [lindex [split $VlanGroup -] 0] set LastVLAN [lindex [split $VlanGroup -] 1] for {set i $FirstVLAN} {$i <= $LastVLAN} {incr i} { lappend InterfaceVlansExpanded $i } } } # InterfaceVlansExpanded is now a list of VLANs (1 VLAN per list element) foreach VLAN $InterfaceVlansExpanded { if {[lsearch $vlan_list $VLAN] != -1} { lappend return_list $VLAN } } return $return_list } puts [gettrunkvlanlist Eth1/48 $VLAN_MATCH_LIST] # get the operational mode of an interface proc GetInterfaceMode {iFace} { set FullOpState [cli "show interface $iFace switchport"] regexp -line -all {Operational Mode.*} $FullOpState ModeStatement set OpMode [regsub {Operational Mode: (.*)} $ModeStatement {\1}] return $OpMode } # get the list of physical interfaces on the system, return them as a list proc GetInterfaces {} { set RunningConfig [cli "show running-config"] foreach InterfaceInConfig [regexp -inline -line -all {^interface Eth.*} $RunningConfig] { lappend ListOfInterfaces [regsub {.*(Ethernet\w*)} $InterfaceInConfig {\1}] } return $ListOfInterfaces } # begin main function set output {"================\nSTART EVALUATION HERE\n================"} foreach interface [GetInterfaces] { puts "evaluating interface $interface\n" set int_mode [GetInterfaceMode $interface] puts "Interface mode: $int_mode" if {$int_mode eq "access"} { set access_vlan [getaccessvlan $interface] puts "Access VLAN: $access_vlan" if {[lsearch $VLAN_MATCH_LIST $access_vlan] != -1} { lappend output "$interface is an access port on VLAN of concern: $access_vlan" } } elseif {$int_mode eq "trunk"} { set trunk_matched_vlan_list [gettrunkvlanlist $interface $VLAN_MATCH_LIST] puts "VLANs matched on trunk: $trunk_matched_vlan_list" if {[llength $trunk_matched_vlan_list] > 0} { lappend output "$interface is a trunk port, that includes these VLANs of concern: $trunk_matched_vlan_list" } } elseif {$int_mode eq "fex-fabric"} { continue } elseif {$int_mode eq "FabricPath"} { continue } else { puts "$interface indeterminate mode" lappend output "$interface is indeterminate" } } #puts $output foreach line $output { puts "$line" }
11-23-2019 02:29 PM
Nice script...
11-23-2019 03:21 PM
Thanks :-)
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide