Welcome to the VanDyke Software Forums

Join the discussion today!


Go Back   VanDyke Software Forums > Scripting

Notices

Reply
 
Thread Tools Rate Thread Display Modes
  #1  
Old 07-23-2021, 08:30 AM
Rhudi's Avatar
Rhudi Rhudi is offline
Registered User
 
Join Date: Jul 2013
Location: Greenville, SC
Posts: 97
Router Paste Script (with error handling) - Critique?

I'd like to share a script I wrote yesterday. I'd appreciate any 'best practice' pointers. Plus, if you like this and find it useful, use it.

We all need a reliable way to paste commands into a router. Really, what else does any of use us SecreCRT for anyway?

I've written a program that expects a certain level of connectivity into a Cisco or Juniper device. Once you are at the correct prompt in your device, this program will take whatever you put in the Copy Buffer and dump that into the command line. It does rudimentary error checking and should just stop if there are any errors.

The biggest challenge was figuring out what device prompt to use for my 'WaitFor' commands. Pasting Cisco config makes the prompt change.

Pasting 'show' commands can create delays whilst the device is processing the command.

So, here it is for your enjoyment (please feel free to tell me how silly I am and where I need to improve this):
Code:
# $language = "VBScript"
# $interface = "1.0"
'
Option Explicit
Dim objTab
Dim nStartTime  ' Timing
Dim sPrompt1    ' Device Prompt at cursor line
Dim vLines      ' Array for Looping
Dim sLine       ' String to Send
Dim nLineNumber ' Line Count during Send
Dim nTotalLines ' Total lines to send
Dim sConnectionType
Dim vShellPrompts
Dim bCiscoConnected
Dim bJunosConnected
Const szTimeout = 120

'
' This script is used to paste multiple show commands into an open Cisco or Juniper session
'
' 2021-07-21 - New Script
'
crt.Screen.Synchronous = True
crt.Screen.IgnoreEscape = True
Set objTab = crt.GetScriptTab
'
Sub Main()
    Select Case crt.Session.Connected
        Case False
            crt.Dialog.MessageBox "Sending data requires an active connection."
        Case True
            Select Case Trim(crt.Clipboard.Text)
                Case ""
                    crt.Dialog.MessageBox "No text found in the Windows Clipboard."
                Case Else
                    sPrompt1 = Trim(objTab.Screen.Get(objTab.Screen.CurrentRow, 1, objTab.Screen.CurrentRow, objTab.Screen.CurrentColumn - 1))         ' This is the command prompt
                    Select Case Right(Trim(sPrompt1),5)
                        Case "-re0>", "-re1>", "-re0#", "-re1#"
                            sPrompt1 = Right(sPrompt1, 5) ' "-re0>" or "-re1>"
                            objTab.Screen.Send "set cli screen-length 0" & vbCr
                            sConnectionType = "JUNIPER"
                            bJunosConnected = True
                            bCiscoConnected = False
                        Case Else
                            Select Case Right(Trim(sPrompt1), 1)
                                Case "$" ' Possible JumpHost shell
                                    crt.Dialog.MessageBox "Session does not appear to be a Cisco device."
                                    Exit Sub
                                Case ">" ' Cisco needing 'enable'
                                    crt.Dialog.MessageBox "Session does not appear to be in Cisco 'enable' mode."
                                    Exit Sub
                                Case "#" ' Cisco
                                    sConnectionType = "CISCO"
                                    bCiscoConnected = True
                                    bJunosConnected = False
                                    sPrompt1 = Right(sPrompt1, 2)
                                    objTab.Screen.Send "terminal length 0" & vbCr
                                    Select Case sPrompt1
                                        Case ")#" ' Config mode
                                            ' NOOP
                                        Case Else
                                            sPrompt1 = Right(sPrompt1, 1)
                                    End Select
                                Case Else ' Neither - Bail
                                    crt.Dialog.MessageBox "Not connected to expected device type (by prompt)."
                                    Exit Sub
                            End Select
                    End Select
                    objTab.Screen.WaitForString sPrompt1
'                    crt.Dialog.MessageBox "Current Prompt:" & vbCrLf & _
'                                          sPrompt1
                    nStartTime = Timer
                    vLines = Split(Replace(Replace(crt.Clipboard.Text, _
                                                   vbCrLf, vbCr), _
                                           vbLf, vbCr), _
                                   vbCr) ' Normalise Line Breaks and split into array
                    nLineNumber = 0
                    nTotalLines = UBound(vLines) + 1
                    objTab.Screen.SendSpecial("MENU_CLEAR_SCREEN_AND_SCROLLBACK")
                    objTab.Screen.Send vbCr
                    objTab.Screen.WaitForString sPrompt1
                    vShellPrompts = Array(_
                        sPrompt1 , _
                        "^") ' The caret will indicate the previous command failed
                    '
                    For Each sLine In vLines
                        Select Case sLine
                            Case "" ' Don't send blank lines
                                ' NOOP
                            Case Else
                                Select Case Left(sLine,1)
                                    Case "#", "/", "\", "!", "*" ' Skip any lines that start with these
                                        crt.Sleep 250
                                    Case Else ' Send line
                                        If InStr(1, sLine, "# ") > 0 Then ' When commands have been run before, the line may have an added comment to strip
                                            sLine = Left(sLine, InStr(1, sLine, "# ") - 1)
                                        End If
                                        crt.Session.SetStatusText(sConnectionType & " | Send ( " & nLineNumber & " of " & nTotalLines & " : " & sLine)
                                        objTab.Screen.Send sLine & vbcr
                                        '
                                        Do
                                            Select Case objTab.Screen.WaitForStrings(vShellPrompts, szTimeout)
                                                Case 0 ' The router went away?
                                                    crt.Dialog.MessageBox "NO RESPONSE FROM HOST (in " & szTimeout & ")" & vbLF & sLine
                                                    Exit Sub
                                                Case 1 ' We got back the system prompt (sPrompt)
                                                    nLineNumber = nLineNumber + 1
                                                    Exit Do ' Exit the loop so we can send the next line
                                                Case 2 ' Error detected
                                                    crt.Session.SetStatusText("Input Error: " & sLine)
                                                    Exit Sub
                                            End Select
                                        Loop
                                End Select
                        End Select
                    Next
                    crt.Dialog.MessageBox _
                        nLineNumber & " lines have been sent." & _
                        vbcrlf & vbcrlf & _
                        "Time elapsed: " & GetMinutesAndSeconds(Timer - nStartTime)
                        objTab.Screen.SendSpecial("MENU_SELECT_ALL")
                        objTab.Screen.SendSpecial("MENU_COPY")
                        objTab.Screen.Send vbcr
                        crt.Session.SetStatusText("Ready")
            End Select
    End Select
End Sub
'
'
'
Function GetMinutesAndSeconds(nTotalSecondsElapsed)
Dim nMinutes, nSeconds, nMSeconds
Dim nMinutesElapsed, nSecondsValue, nSecondsElapsed
    Select Case nTotalSecondsElapsed
        Case 0
            GetMinutesAndSeconds = "less than a millisecond."
            Exit Function
        Case Else
            nMinutesElapsed = nTotalSecondsElapsed / 60
            nSecondsValue = nMinutesElapsed - Fix(nMinutesElapsed)
            nSecondsElapsed = Fix(nSecondsValue * 60)
            nMinutesElapsed = Fix(nMinutesElapsed)
            nMSeconds = fix(1000 * (nTotalSecondsElapsed - Fix(nTotalSecondsElapsed)))
            nSeconds = nSecondsElapsed
            nMinutes = nMinutesElapsed
            GetMinutesAndSeconds = nMinutesElapsed & " minutes, " & _
                nSecondsElapsed & " seconds, and " & _
                nMSeconds & " ms"
    End Select
End Function
Reply With Quote
  #2  
Old 08-03-2021, 01:23 PM
gregg gregg is offline
Registered User
 
Join Date: Oct 2010
Posts: 75
Hiya Rhudi-

Quote:
We all need a reliable way to paste commands into a router. Really, what else does any of use us SecreCRT for anyway?
haha- i may or maynot work for Cisco, and i rarely if ever connect to networking devices.

Anyway, my critiques are these.

The code presented is too monolithic and should really be broken down into easily digestible parts, aka more subs/functions with good names.

The indentation is too deep which makes code really difficult to follow.

Code:
    Select Case crt.Session.Connected
        Case False
            crt.Dialog.MessageBox "Sending data requires an active connection."
        Case True
can just become

Code:
If crt.Session.Connected = False Then
    crt.Dialog.MessageBox "Sending data requires an active connection."
    Exit Sub
End If

If Trim(crt.Clipboard.Text) = "" Then
    crt.Dialog.MessageBox "No text found in the Windows Clipboard."
    Exit Sub
End If
That immediately kills multiple levels of indentation. Just get out early if nothing else to do.

As far as breaking parts down into Subs and Functions, here's an example.

Code:
                        Case "-re0>", "-re1>", "-re0#", "-re1#"
                            sPrompt1 = Right(sPrompt1, 5) ' "-re0>" or "-re1>"
                            objTab.Screen.Send "set cli screen-length 0" & vbCr
                            sConnectionType = "JUNIPER"
                            bJunosConnected = True
                            bCiscoConnected = False
Something like this, I would just:

Code:
Function InitJuniperConnection()
    sPrompt1 = Right(sPrompt1, 5) ' "-re0>" or "-re1>"
    objTab.Screen.Send "set cli screen-length 0" & vbCr
    sConnectionType = "JUNIPER"
    bJunosConnected = True
    bCiscoConnected = False
    InitJuniperConnection = sPrompt1
End Function
...
        Case "-re0>", "-re1>", "-re0#", "-re1#"
            sPrompt1 = InitJuniperConnection
Same for Cisco. Though I wonder why you need 3 variables to indicate the connection type. I guess the booleans are unused, but if you needed them you could:

Code:
Dim sConnectionType
sConnectionType = "UNKNOWN"

Function isJuniper()
    ' in a sane language, this would be written as
    ' return sConnectionType == "JUNIPER"
    ' to distinguish assignment from comparison that returns a bool
    isJuniper = sConnectionType = "JUNIPER"
End Function

Function isCisco()
    isCisco = sConnectionType = "CISCO"
End Function

' and to use one:
If isJuniper() Then
    ' juniper specific code
End If
Anyway, those are some of my immediate thoughts on how i would start the refactor.
Reply With Quote
  #3  
Old 08-06-2021, 09:49 AM
Rhudi's Avatar
Rhudi Rhudi is offline
Registered User
 
Join Date: Jul 2013
Location: Greenville, SC
Posts: 97
Great Feedback - Thanks!

The idea to break this into Sub Routines makes sense.

I have a personal preference for the 'Select Case' structure. I find it way more flexible than "If...ElseIf...End". I read in an Excel Forum that "Select Case" compiles and runs faster.

Using functions would let me indent less. I use indents to clearly delineate what is happening within a 'Case ...'.

I am curious as to the optimization of calling numerous Subs or Functions.

In general, I find what I have written to be rock solid, as to reliability. I have team mates that prefer to copy/paste into live router session and trust that Line Pacing will work. My code lets me paste any length of commands into a router.

My work environment consists of moving large amounts of config between Provider-Level Internet Edge Routers. 100% reliability is crucial.

Thanks for the feedback. I will try to post a version with some of your suggestions.
Reply With Quote
  #4  
Old 08-06-2021, 01:44 PM
gregg gregg is offline
Registered User
 
Join Date: Oct 2010
Posts: 75
The limiting speed factor here is the communication channel to the remote device. The difference between a switch/case and if/else will be greatly overshadowed by the communication.

A switch/case of a boolean value would only ever have 2 possible cases. And in the case of this code, the options are "continue working" or "error, get out of here", except for your switch/case "get out of here," the reader has to parse the entire sub all the way to the bottom to see that this is what is happening.

Unless absolutely critical, I lean towards code readability/maintainability over execution speed. Especially for things that just run once or minimal times. If you start looping over a piece of code 100 thousand times, then we can talk about optimization.

Indents are important, but being nested so many levels deep hinders readability "where's the end of this thing, oh wayyyy down here."

These observations are of course personal preference from years of experience. Though, there are tons of books and talks on the subject.
Reply With Quote
Reply

Tags
router paste

Thread Tools
Display Modes Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -6. The time now is 07:28 PM.