|
#1
|
||||
|
||||
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 |
#2
|
|||
|
|||
Hiya Rhudi-
Quote:
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 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 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 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 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 |
#3
|
||||
|
||||
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. |
#4
|
|||
|
|||
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. |
![]() |
Tags |
router paste |
Thread Tools | |
Display Modes | Rate This Thread |
|
|