Know first, I am pretty new at Powershell but I am here to hopefully learn and get better.
I am working on a script that will log into Cisco devices (routers for the moment) via SSH. The script will then run a command and take the output and save it to a text file. All of this works running just a single command (it’s a work in progress, working towards running more commands). This is not for device configuration, simply to run some show commands (and eventually a ping command). For the moment I am just running a ‘show BGP summary’ command. However, I want to run a show BGP neighbor command and be able to parse the output to read the BGP state (Idle, Active, Established, etc.).
And that is where I am hitting a wall and not sure where to start. When the command 'show bgp neighbor 1.2.3.4 is run, I want the entire output saved to the text file, which I can do, but where do I start in trying to parse that output?
I can share what I have at the moment (if needed) just be warned, its probably going to make some PowerShell expert cringe, but I did not think it would be needed in order to get started on parsing the output.
Parsing output highly depends on how the output is formatted. Usually the output is in columns, and there you often just split the lines on spaces and translate that to psobjects.
Here is an example of how you can parse a simple output. I am saving output of Get-Process (ps) as a string, and then parsing it back to powershell objects.
Router# show ip bgp summary
BGP router identifier 172.16.1.1, local AS number 100
BGP table version is 199, main routing table version 199
37 network entries using 2850 bytes of memory
59 path entries using 5713 bytes of memory
18 BGP path attribute entries using 936 bytes of memory
2 multipath network entries and 4 multipath paths
10 BGP AS-PATH entries using 240 bytes of memory
7 BGP community entries using 168 bytes of memory
0 BGP route-map cache entries using 0 bytes of memory
0 BGP filter-list cache entries using 0 bytes of memory
90 BGP advertise-bit cache entries using 1784 bytes of memory
36 received paths for inbound soft reconfiguration
BGP using 34249 total bytes of memory
Dampening enabled. 4 history paths, 0 dampened paths
BGP activity 37/2849 prefixes, 60/1 paths, scan interval 15 secs
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
10.100.1.1 4 200 26 22 199 0 0 00:14:23 23
10.200.1.1 4 300 21 51 199 0 0 00:13:40 0
Router# show ip bgp summary
BGP router identifier 192.168.3.1, local AS number 45000
BGP table version is 1, main routing table version 1
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
*192.168.3.2 4 50000 2 2 0 0 0 00:00:37 0
* Dynamically created based on a listen range command
Dynamically created neighbors: 1/(200 max), Subnet ranges: 1
BGP peergroup group192 listen range group members:
192.168.0.0/16
Router# show ip bgp summary
BGP router identifier 172.17.1.99, local AS number 65538
BGP table version is 1, main routing table version 1
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down Statd
192.168.1.2 4 65536 7 7 1 0 0 00:03:04 0
192.168.3.2 4 65550 4 4 1 0 0 00:00:15 0
Router# show ip bgp summary
BGP router identifier 172.17.1.99, local AS number 1.2
BGP table version is 1, main routing table version 1
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down Statd
192.168.1.2 4 1.0 9 9 1 0 0 00:04:13 0
192.168.3.2 4 1.14 6 6 1 0 0 00:01:24 0
and this script would parse a file like that:
#Requires -Version 5
#Requires -RunAsAdministrator
# Script to parse 'show ip bgp summary' output - Sam Boutros - 21 November 2017
#region input
$SourceFile = '.\SampleCisco1.txt'
$NewRecordMarker = 'show ip bgp summary'
#endregion
#region Process
# Read input file
$Lines = Get-Content $SourceFile
# Breakdown input file into records based on $NewRecordMarker
$i=0; $RecordStartLines = $Lines | % { if ($_ -match $NewRecordMarker) { $i }; $i++ }
"Identified $($RecordStartLines.Count) records at lines: $($RecordStartLines -join ', ' )"
# Parse each record to create the output PS object
$myOutput = 0..($RecordStartLines.Count-1) | % {
$RecordStartLine = $RecordStartLines[$_]
$RecordEndLine = $(
if ($_ -eq $RecordStartLines.Count-1) {
$Lines.Count-1
} else {
$RecordStartLines[$_+1]-1
}
)
Remove-Variable PastNeighborline -EA 0
foreach ($Line in $Lines[$RecordStartLine..$RecordEndLine]) {
# Get RouterID from 'BGP router identifier' line
if ($Line -match 'BGP router identifier') { $RouterID = $Line.Split(' ')[4].Replace(',','').Trim() }
# We're only interested in the lines after 'Neighbor' line that has a '.'
if ($Line -match 'Neighbor') { $PastNeighborline = $true }
if ($Line -match 'Dynamically created') { $PastNeighborline = $false }
if ($PastNeighborline -and $Line -match '\.') {
$Neighbor = ($Line.Split(' ') | ? { $_ })[0]
if ($Neighbor -match '\*') { $Neighbor = $Neighbor.Replace('*',''); $Dynamic = $true } else { $Dynamic = $false }
[PSCustomObject][Ordered]@{
RouterID = $RouterID
Neighbor = $Neighbor
Dynamic = $Dynamic
State = ($Line.Split(' ') | ? { $_ })[9]
} # PSCustomObject
} # if $PastNeighborline
} # foreach $Line
} # foreach 0..($RecordStartLines.Count-1)
#endregion
#region output
# Dedup the output
$myOutput | group RouterID,Neighbor,Dynamic,State | % {
$_.Group | select RouterID,Neighbor,Dynamic,State -First 1
}
#endregion
Thank you all for the great replies thus far. Sorry, it has taken so long to respond. 10-hour work days in a NOC on minimal holiday staffing doesn’t leave a lot of time to freeload on the web
I did realize something today in my approach to this (I told you I was new). My thinking was that I could parse the console output in real-time. Apparently, I was a bit off base in my thinking. So in that regard, I learned something today. It appears I will have to read the output into a file and/or a variable and then pick it apart there, most likely using the suggestion of ConvertFrom-String.
Sam, I can’t thank you enough for the example you gave, but you went so far over my head I will probably spend the next week just trying to pick through and understand your code. However, it looks like you doing pretty much exactly what I am wanting to do.