Skip to content
On this page

Script - Save Running Config to txt file

Run the script and it will format the output, run show commands and output the data into a .txt file.

py
import re
import datetime
import os
import platform
import shutil
import sys
import time
import subprocess

# Define aliases for CRT dialog functions
MsgBox = crt.Dialog.MessageBox
Prompt = crt.Dialog.Prompt

# Define global variables
global g_strConfigToSave
g_strConfigToSave = "running"
global g_strAdditionalArgs
g_strAdditionalArgs = ""

# Get the user's home directory
strHome = os.path.expanduser("~")
global g_strMyDocs
g_strMyDocs = strHome.replace("\\", "/") + "/Documents"

global objTab
objTab = crt.GetScriptTab()
objTab.Screen.Synchronous = True

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def Main():
    global g_strConfigToSave

    # Check if command-line arguments were provided
    if crt.Arguments.Count > 0:
        strArg = str(crt.Arguments.GetArg(0)).lower()
    else:
        strArg = g_strConfigToSave

    # Validate the provided argument
    if not (strArg == "running" or strArg == "startup"):
        MsgBox(
            "Unrecognized config type: '" + strArg + "'\r\n" +
            "Expected either 'running' or 'startup'.\r\n\r\n" +
            "Exiting.")
        return

    g_strConfigToSave = strArg

    global g_strAdditionalArgs

    # Check for additional command-line arguments
    if crt.Arguments.Count > 1:
        for nArgIndex in range(1, crt.Arguments.Count):
            strAddArg = str(crt.Arguments.GetArg(nArgIndex))
            if g_strAdditionalArgs == "":
                g_strAdditionalArgs = strAddArg
            else:
                g_strAdditionalArgs = "{0} {1}".format(g_strAdditionalArgs, strAddArg)

    if not objTab.Session.Connected:
        strMessage = "You must first be connected to run this script!"
        MsgBox(strMessage)
        return

    # Detect the shell prompt by getting all the text to the left of the cursor.
    strPrompt = GetTextLeftOfCursor()

    # Handle cases where no prompt is detected
    if len(strPrompt) < 1:
        objTab.Screen.Send("\r")
        while objTab.Screen.WaitForCursor(1):
            objTab.Session.SetStatusText("Waiting for prompt...")
        strPrompt = GetTextLeftOfCursor()
        if len(strPrompt) < 1:
            FlashStatusText("No prompt detected! Press Enter first.")
            return

    # Check if we're in privileged/enable mode
    if strPrompt.rstrip()[-1] != '#':
        FlashStatusText("Must 'enable' first!")
        return

    # Ensure we're not in (config) mode
    strHostname = GetHostname()
    if "config)" in strPrompt:
        FlashStatusText("Not in priv EXEC mode... exiting.")
        return

    bIsASADevice = False
    nColsOrig = crt.Session.Config.GetOption("Cols")

    objTab.Session.SetStatusText("Getting term info...")

    # Get terminal size information
    objTab.Screen.Send("sh term\r")
    objTab.Screen.WaitForString("sh term")

    while True:
        objTab.Screen.WaitForStrings([strPrompt, "Width: ", "Length: ", "Width = ", "--More--", "-- More --", "--more--", "-- more --"])

        if objTab.Screen.MatchIndex == 1:
            # Found strPrompt. We're done looping
            break

        elif objTab.Screen.MatchIndex == 2:
            # Found "Width: "
            nCols = objTab.Screen.ReadString(" columns")

        elif objTab.Screen.MatchIndex == 3:
            # Found "Length: "
            nRows = objTab.Screen.ReadString(" lines")

        elif objTab.Screen.MatchIndex == 4:
            # Found "Width = ". This means we're on an ASA device
            bIsASADevice = True
            # Read the number of columns up to the ',' character
            nCols = objTab.Screen.ReadString(",")

        elif objTab.Screen.MatchIndex > 4:
            # This means that anything but the first 4 elements of vWaitFors was seen,
            # so we need to press SPACE to continue receiving output.
            objTab.Screen.Send(" ")
            continue

    if bIsASADevice:
        # ASA devices appear not to support modifying columns/width, so we'll
        # just turn off the pager...
        objTab.Session.SetStatusText("Setting term pager 0...")
        objTab.Screen.Send("term pager 0\r")
        objTab.Screen.WaitForString("term pager 0\r")
        objTab.Screen.WaitForString(strPrompt)

        objTab.Session.SetStatusText("Getting " + g_strConfigToSave + "-config...")

        # Then, we'll run the 'show ...' command...
        strCmd = "more system:{0}-config\r".format(g_strConfigToSave)
        objTab.Screen.Send(strCmd)

        # Also, ASA devices seem to output the config without extraneous data like size, etc.,
        # so all we have to do is read up to where the prompt appears...
        # First, wait for the command we sent to appear as echoed from the remote...
        objTab.Screen.WaitForString(strCmd)
        objTab.Screen.WaitForString("\n")

        # Now, let's read/capture up to the point where the shell prompt appears...
        objTab.Session.SetStatusText("Reading " + g_strConfigToSave + "-config...")
        strConfig = objTab.Screen.ReadString(strPrompt)

        # Restore the pager, set it to what SecureCRT has as rows...
        objTab.Session.SetStatusText("Setting pager back to full rows...")
        objTab.Screen.Send("term pager {0}\r".format(objTab.Screen.Rows))
        objTab.Screen.WaitForString(strPrompt)
    else:
        # Cisco 881W, etc. case
        objTab.Session.SetStatusText("Setting term len 0...")
        if int(nRows) > 0:
            objTab.Screen.Send("term len 0\r")
            objTab.Screen.WaitForString(strPrompt)
        if int(nCols) < 132:
            objTab.Session.Config.SetOption("Cols", 132)
            objTab.Screen.Send("term width 132\r")
            objTab.Screen.WaitForString(strPrompt)

        objTab.Session.SetStatusText("Getting " + g_strConfigToSave + "-config...")

        # Command will either be 'show running-config' or 'show startup-config'
        strCmd = "show " + g_strConfigToSave + "-config"

        # Accommodate any additional arguments to the sh startup-config or sh running-config
        # that were passed in as arguments to the script
        if g_strAdditionalArgs == "":
            strCmd += "\r"
        else:
            strCmd += " " + g_strAdditionalArgs + "\r"

        objTab.Screen.Send(strCmd)

        if g_strConfigToSave == "running":
            objTab.Screen.WaitForStrings(["Current configuration", "Invalid input"])
            if objTab.Screen.MatchIndex > 1:
                FlashStatusText("Invalid command")
                return
            nBytes = objTab.Screen.ReadString("\r\n")
        elif g_strConfigToSave == "startup":
            objTab.Screen.WaitForStrings(["Using", "Invalid input"])
            if objTab.Screen.MatchIndex > 1:
                FlashStatusText("Invalid command")
                return
            nBytes = objTab.Screen.ReadString(" out of ")
            objTab.Screen.WaitForString("bytes\r\n")
        else:
            MsgBox("Unknown config type: '" + g_strConfigToSave + "'. Exiting.")
            return

        strConfig = objTab.Screen.ReadString(strPrompt)
        objTab.Session.SetStatusText("Restoring original terminal size...")
        objTab.Screen.Send("term len " + nRows + "\r")
        objTab.Screen.WaitForString(strPrompt)
        objTab.Session.Config.SetOption("Cols", nColsOrig)
        objTab.Screen.Send("term width " + nCols + "\r")
        objTab.Screen.WaitForString(strPrompt)

    objTab.Session.SetStatusText("Saving configuration to local file system...")
    strDateTimeTag = datetime.datetime.now().strftime("%Y%m%d_%H%M%S.%f")[:19]

    strSessionPath = objTab.Session.Path
    strSessionPath = strSessionPath.replace("\\", "/")
    objMatch = re.match(r'(.*)[/]([^/]+)$', strSessionPath)
    strSessionName = strSessionPath
    strSavedConfigsFolder = "Config-Saves/"

    if objMatch:
        strSavedConfigsFolder += objMatch.group(1)
        strSessionName = objMatch.group(2)

    if strSavedConfigsFolder[-1] != "/":
        strSavedConfigsFolder += "/"

    strActualFolder = g_strMyDocs + "/" + strSavedConfigsFolder

    if not os.path.exists(strActualFolder):
        os.makedirs(strActualFolder)

    if strActualFolder[-1] != "/":
        strActualFolder += "/"

    strActualFilePath = strActualFolder + strSessionName + "_" + objTab.Session.RemoteAddress + "_" + g_strConfigToSave + "-config_" + strDateTimeTag + ".txt"

    strFilename = Browse("Choose where to save your " + g_strConfigToSave + "-config", "Save", strActualFilePath, "Text Files (*.txt)|*.txt||")

    if strFilename != "":
        with open(strFilename, "wb") as objFile:
            objFile.write(strConfig)

        # If on Windows platform, bring up explorer with the file selected
        if sys.platform == "win32":
            subprocess.call("explorer /e,/select,\"" + strFilename + "\"")

        FlashStatusText("Script Completed")
    else:
        FlashStatusText("Script Cancelled")

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def GetHostname():
    strPrompt = GetTextLeftOfCursor()
    objMatch = re.match(r'^([0-9a-zA-Z\_\-\.]+)', strPrompt)
    if objMatch:
        return objMatch.group(1)
    else:
        FlashStatusText("No match on hostname pattern!")
        return

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def GetTextLeftOfCursor():
    global objTab
    nRow = objTab.Screen.CurrentRow
    nCol = objTab.Screen.CurrentColumn - 1
    strTextLeftOfCursor = objTab.Screen.Get(nRow, 1, nRow, nCol)
    return strTextLeftOfCursor

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def FlashStatusText(strMsg):
    global objTab
    nShortPause = 200
    nLongPause = 400

    for i in range(1, 5):
        objTab.Session.SetStatusText(strMsg)
        crt.Sleep(nLongPause)
        objTab.Session.SetStatusText("")
        crt.Sleep(nShortPause)

    objTab.Session.SetStatusText(strMsg)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def Browse(strMessage, strButtonCaption, strDefaultPath, strFilter):
    strPlatform = sys.platform

    # Windows version of SecureCRT allows FileOpenDialog to return a path to a file that doesn't yet exist.
    # But Linux/Mac versions of FileOpenDialog() require an existing file. So, use the nicer interface in Windows,
    # and on Linux/Mac, simply present an input box that will allow for the path to be changed, if desired.
    # If you are on Linux/Mac and don't like the default path, simply change the value of the g_strMyDocs variable
    # (globally defined) as well as the value of the strSavedConfigsFolder variable defined in Main() above.

    if strPlatform == "win32":
        return crt.Dialog.FileOpenDialog(strMessage, strButtonCaption, strDefaultPath, strFilter)
    else:
        return crt.Dialog.Prompt(strMessage, strButtonCaption, strDefaultPath)

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Main()