Use Python Script to Create New SecureCRT Sessions from Data File

Problem description

Every time I configure a network in my virtual environment I have to manually:

  • create a special folder for the project
  • create each session (copy-paste)
  • rename each session
  • setup the logon actions on session properties

Solution

On SecureCRT website, I have found a useful article named Importing SecureCRT® Sessions from a Data File. This script creates simple sessions. It also gives me the possibility to define a logon script for each session. Therefore, I will create a script that takes the session name and use it as variable to introduce my first cli commands:

root@MX% cli
root@MX> set cli logical-system SessionName
Logical system: SessionName

root@MX:SessionName> 

Next, I will take as an example the network created to Carrier-of-carriers setup created in a recent post.

My Carrier-of-carriers.csv file:

session_name,hostname,protocol,username,folder,emulation,logon_script
21-CE,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,/Users/silvia/Dropbox/Scripting/For SecureCRT/LogicalSystemsLogonScript.py
31-CE,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,/Users/silvia/Dropbox/Scripting/For SecureCRT/LogicalSystemsLogonScript.py
11-PE,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,/Users/silvia/Dropbox/Scripting/For SecureCRT/LogicalSystemsLogonScript.py
12-P,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,/Users/silvia/Dropbox/Scripting/For SecureCRT/LogicalSystemsLogonScript.py
13-ASBR,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,/Users/silvia/Dropbox/Scripting/For SecureCRT/LogicalSystemsLogonScript.py
14-ASBR,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,/Users/silvia/Dropbox/Scripting/For SecureCRT/LogicalSystemsLogonScript.py
15-P,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,/Users/silvia/Dropbox/Scripting/For SecureCRT/LogicalSystemsLogonScript.py
16-PE,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,/Users/silvia/Dropbox/Scripting/For SecureCRT/LogicalSystemsLogonScript.py
22-CE,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,/Users/silvia/Dropbox/Scripting/For SecureCRT/LogicalSystemsLogonScript.py
32-CE,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,/Users/silvia/Dropbox/Scripting/For SecureCRT/LogicalSystemsLogonScript.py
vMX-mng,192.168.83.11,SSH2,root,Local Labs/Interpovider L3VPN,XTerm,

The modified Python script ImportArbitraryDataFromFileToSecureCRTSessions-Silvia-v1.0.py:

# $language = "Python"
# $interface = "1.0"

# ImportArbitraryDataFromFileToSecureCRTSessions.py
#   (Designed for use with SecureCRT 7.2 and later)
#
#   Last Modified: 23 Feb, 2018
#      - Blurb about sessions that were created during the import was
#        missing from the results log
#     - If running on Windows, and unable to write to results log, make
#       sure clipboard data containing the results log info is formatted
#       with \r\n instead of just \n so that it's legible in Notepad, for
#       example, when pasted.
#
#   Last Modified: 21 Dec, 2017
#      - Allow multiple 'description' fields on the same line. All will be
#        compounded together with each one ending up on a separate line in
#        the Session's Description session option.
#      - Allow 'username' field to be defaulted in the header line
#      - Duplicate sessions are now imported with unique time-stamped
#        names (for each additional duplicate). Earlier versions of this
#        script would overwrite the first duplicate with any subsequent
#        duplicates that were found in the data file.
#      - Allow header fields to be case-insentive so that "Description"
#        and "HostName", etc. work just as well as "description" and "hostname"
#
#   Last Modified: 18 Dec, 2017
#      - Remove unused (commented out) code block left in from the
#        20 Apr, 2017 changes.
#      - Fix required header line message to no longer reference
#        'protocol' field as required.
#      - Add fallback locations where the script will attempt to
#        write summary log of script's activities/errors/warnings.
#        This attempts to facilitate running this script in environments
#        where SecureCRT may not have access to a "Documents" folder
#        (such as when SecureCRT is being launched through VDI publishing).
#         --> First try Documents,
#         --> Then try Desktop,
#         --> Then try SecureCRT's config folder.
#         --> If none of the above are accessible for writing, the
#             script will copy the summary report to the clipboard,
#             providing the user with a way to see the summary report
#             if pasted into a text editor.
#      - Added support for defaulting the "folder" header so that all
#        new entries could be imported into a folder w/o having to
#        specify the folder on each line. Example header line for
#        CSV file with only hostname data would be:
#            hostname,folder=default_import_folder_name
#
#   Last Modified: 17 Nov, 2017
#      - No longer attempt to use platform to determine OS ver info,
#        as it's no longer needed.
#
#   Last Modified: 20 Apr, 2017
#      - No longer require protocol in header. Use the Default session's
#        protocol if the protocol field is not present in the header line.
#      - Conform to python join() method requiring only one argument.
#      - Prompt for delimiter character if it isn't found in the header line.
#      - Allow delimiter character to be NONE, so that a single field (hostname)
#        and corresponding data can be used to import sessions (say for example
#        if you have a file that just contains hostnames, one per line).
#      - [Bug Fix]: can't use + to concatenate str and int, so use format()
#        instead.
#      - [Bug Fix]: "Procotol" typo fixed to "Protocol" in error case where
#        protocol header field not found/set.
#
#   Last Modified: 04 Jan, 2017
#      - Added support for specifying logon script file to be set for
#        imported sessions.
#
#   Last Modified: 02 Jul, 2015
#      - Display status bar info for each line we're processing so that if
#        there's an error, the individual running the script might have
#        better information about why the error might have occurred.
#      - Handle cases where a line in the data file might have more fields
#        in it than the number of header fields designated for import. This
#        fixes an error reported by forum user wixxyl here:
#           https://forums.vandyke.com/showthread.php?t=12021
#        If a line has too many fields, create a warning to be displayed
#        later on, and move on to the next line -- skipping the current line
#        because it's unknown whether the data is even valid for import.
#
#   Last Modified: 20 Jan, 2015
#      - Combined TAPI protocol handling (which is no longer
#        supported for mass import) with Serial protocol
#        import errors.
#      - Enhanced example .csv file data to show subfolder specification.
#
#   Last Modified: 21 Mar, 2012
#      - Initial version for public forums
#
# DESCRIPTION
# This sample script is designed to create sessions from a text file (.csv
# format by default, but this can be edited to fit the format you have).
#
# To launch this script, map a button on the button bar to run this script:
#    http://www.vandyke.com/support/tips/buttonbar.html
#
# The first line of your data file should contain a comma-separated (or whatever
# you define as the g_strDelimiter below) list of supported "fields" designated
# by the following keywords:
# -----------------------------------------------------------------------------
# session_name: The name that should be used for the session. If this field
#               does not exist, the hostname field is used as the session_name.
#       folder: Relative path for session as displayed in the Connect dialog.
#     hostname: The hostname or IP for the remote server.
#     protocol: The protocol (SSH2, SSH1, telnet, rlogin)
#         port: The port on which remote server is listening
#     username: The username for the account on the remote server
#    emulation: The emulation (vt100, xterm, etc.)
#  description: The comment/description. Multiple lines are separated with '\r'
# logon_script: The full path to the Logon Script filename for the session.
# =============================================================================
#
#
# As mentioned above, the first line of the data file instructs this script as
# to the format of the fields in your data file and their meaning.  It is not a
# requirement that all the options be used. For example, notice the first line
# of the following file only uses the "hostname", "username", and "protocol"
# fields.  Note also that the "protocol" field can be defaulted so that if a
# protocol field is empty it will use the default value.
# -----------------------------------------------------------------------------
#   hostname,username,folder,protocol=SSH2
#   192.168.0.1,root,_imported,SSH1
#   192.168.0.2,admin,_imported,SSH2
#   192.168.0.3,root,_imported/folderA,
#   192.168.0.4,root,,
#   192.168.0.5,admin,_imported/folderB,telnet
#   ... and so on
# =============================================================================

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

MsgBox = crt.Dialog.MessageBox
# The g_strDefaultProtocol variable will only be defined within the
# ValidateFieldDesignations function if the protocol field has a default value
# (e.g., protocol=SSH2), as read in from the first line of the data file.
global g_strDefaultProtocol
g_strDefaultProtocol = ""

# The g_strDefaultFolder variable will only be defined within the
# ValidateFieldDesignations function if the folder field has a default value
# (e.g., folder=Site34), as read in from the first line of the data file.
global g_strDefaultFolder
g_strDefaultFolder = ""

# The g_strDefaultUsername variable will only be defined within the
# ValidateFieldDesignations function if the protocol field has a default value
# (e.g., username=bobofet), as read in from the first line of the data file.
global g_strDefaultUsername
g_strDefaultUsername = ""

# If your data file uses spaces or a character other than comma as the
# delimiter, you would also need to edit the g_strDelimiter value a few lines
# below to indicate that fields are separated by spaces, rather than by commas.
# For example:
#       g_strDelimiter = " "
# Using a ";" might be a good alternative for a file that includes the comma
# character as part of any legitimate session name or folder name, etc.
global g_strDelimiter
g_strDelimiter = ","      # comma
#g_strDelimiter = " "    # space
#g_strDelimiter = ";"    # semi-colon
#g_strDelimiter = chr(9) # tab
#g_strDelimiter = "|||"  # a more unique example of a delimiter.


# The g_strSupportedFields indicates which of all the possible fields, are
# supported in this example script.  If a field designation is found in a data
# file that is not listed in this variable, it will not be imported into the
# session configuration.
global g_strSupportedFields
g_strSupportedFields = \
    "description,emulation,folder,hostname,port,protocol,session_name,username,logon_script"

# If you wish to overwrite existing sessions, set the
# g_bOverwriteExistingSessions to True; for this example script, we're playing
# it safe and leaving any existing sessions in place :).
global g_bOverwriteExistingSessions
g_bOverwriteExistingSessions = False

strHome = os.path.expanduser("~")
global g_strMyDocs
g_strMyDocs = strHome + "/Documents"

g_strMyDesktop = strHome + "/Desktop"

global g_strHostsFile
g_strHostsFile = g_strMyDocs + "/MyDataFile.csv"

global g_strExampleHostsFile
g_strExampleHostsFile = \
    "\thostname,protocol,username,folder,emulation\n" + \
    "\t192.168.0.1,SSH2,root,Linux Machines,XTerm\n" + \
    "\t192.168.0.2,SSH2,root,Linux Machines,XTerm\n" + \
    "\t...\n" + \
    "\t10.0.100.1,SSH1,admin,CISCO Routers,VT100\n" + \
    "\t10.0.101.1,SSH1,admin,CISCO Routers,VT100\n" + \
    "\t...\n" + \
    "\tmyhost.domain.com,SSH2,administrator,Windows Servers,VShell\n" + \
    "\t...\n"

g_strExampleHostsFile = g_strExampleHostsFile.replace(",", g_strDelimiter)

global g_strConfigFolder, strFieldDesignations, g_strFieldsArray, vSessionInfo

global strSessionName, strHostName, strPort
global strUserName, strProtocol, strEmulation
global strPathForSessions, strLine, nFieldIndex
global strSessionFileName, strFolder, nDescriptionLineCount, strDescription

global g_strLastError, g_strErrors, g_strSessionsCreated
global g_nSessionsCreated, g_nDataLines
g_strLastError = ""
g_strErrors = ""
g_strSessionsCreated = ""
g_nSessionsCreated = 0
g_nDataLines = 0

# Use current date/time info to avoid overwriting existing sessions by
# importing sessions into a new folder named with a unique timestamp.
g_strDateTimeTag = datetime.datetime.now().strftime("%Y%m%d_%H%M%S.%f")[:19]

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def GetConfigPath():
    objConfig = crt.OpenSessionConfiguration("Default")
    # Try and get at where the configuration folder is located. To achieve
    # this goal, we'll use one of SecureCRT's cross-platform path
    # directives that means "THE path this instance of SecureCRT
    # is using to load/save its configuration": ${VDS_CONFIG_PATH}.

    # First, let's use a session setting that we know will do the
    # translation between the cross-platform moniker ${VDS_CONFIG_PATH}
    # and the actual value... say, "Upload Directory V2"
    strOptionName = "Upload Directory V2"

    # Stash the original value, so we can restore it later...
    strOrigValue = objConfig.GetOption(strOptionName)

    # Now set the value to our moniker...
    objConfig.SetOption(strOptionName, "${VDS_CONFIG_PATH}")
    # Make the change, so that the above templated name will get written
    # to the config...
    objConfig.Save()

    # Now, load a fresh copy of the config, and pull the option... so
    # that SecureCRT will convert from the template path value to the
    # actual path value:
    objConfig = crt.OpenSessionConfiguration("Default")
    strConfigPath = objConfig.GetOption(strOptionName)

    # Now, let's restore the setting to its original value
    objConfig.SetOption(strOptionName, strOrigValue)
    objConfig.Save()

    # Now return the config path
    return strConfigPath

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def ValidateFieldDesignations(strFields):
    global g_strDelimiter, g_strExampleHostsFile, g_strDefaultProtocol
    global g_strFieldsArray, g_strDefaultFolder, g_strDefaultUsername
    if strFields.find(g_strDelimiter) == -1:
        if len(g_strDelimiter) > 1:
            strDelimiterDisplay = g_strDelimiter
        else:
            if ord(g_strDelimiter) < 33 or ord(g_strDelimiter) > 126:
                strDelimiterDisplay = "ASCII[{0}]".format(ord(g_strDelimiter))
            else:
                strDelimiterDisplay = g_strDelimiter
        strDelim = crt.Dialog.Prompt(
            "Delimiter character [" + strDelimiterDisplay + "] was not found " +
            "in the header line of your data file.\r\n\r\n" +
            "What is the delimiter (field separator) that your file " +
            "is using?\r\n\r\n\t Enter \"NONE\" if your data file only has a single field.")

        if strDelim == "":
            crt.Dialog.MessageBox("Script cannot continue w/o a field delimiter.")
            return

        if strDelim != "NONE":
            g_strDelimiter = strDelim

    g_strFieldsArray = strFields.split(g_strDelimiter)
    if not "hostname" in [x.lower() for x in g_strFieldsArray]:
        strErrorMsg = "Invalid header line in data file. " + \
            "'hostname' field is required."
        if len(g_strDelimiter) > 1:
            strDelimiterDisplay = g_strDelimiter
        else:
            if ord(g_strDelimiter) < 33 or ord(g_strDelimiter) > 126:
                strDelimiterDisplay = "ASCII[{0}]".format(ord(g_strDelimiter))
            else:
                strDelimiterDisplay = g_strDelimiter

        MsgBox(strErrorMsg + "\n" +
            "The first line of the data file is a header line " +
            "that must include\n" +
            "a '" + strDelimiterDisplay +
            "' separated list of field keywords.\n" +
            "\n" +
            "'hostname' is a required keyword." +
            "\n\n" +
            "The remainder of the lines in the file should follow the " +
            "\n" +
            "pattern established by the header line " +
            "(first line in the file)." + "\n" + "For example:\n" +
            g_strExampleHostsFile,
            "Import Data To SecureCRT Sessions")
        return


    if not "protocol" in [x.lower() for x in g_strFieldsArray]:
        if strFields.lower().find("protocol=") == -1:
            # Load the default configuration and use that as the default
            # protocol.
            objConfig = crt.OpenSessionConfiguration("Default")
            g_strDefaultProtocol = objConfig.GetOption("Protocol Name")

    for strField in g_strFieldsArray:
        #MsgBox("{0}\nHas 'protocol': {1}\nHas '=': {2}".format(strField, strField.find("protocol"), strField.find("=")))
        if strField.lower().find("protocol") > -1 and \
           strField.lower().find("=") > -1:
                g_strDefaultProtocol = strField.split("=")[1].upper()
                #MsgBox(("Found a default protocol spec: {0}".format(g_strDefaultProtocol)))
                # Fix the protocol field since we know the default protocol
                # value
                strFields = strFields.replace(strField, "protocol")
        if strField.lower().find("folder") > -1 and \
            strField.lower().find("=") > -1:
                g_strDefaultFolder = strField.split("=")[1]
                strFields = strFields.replace(strField, "folder")

        if strField.lower().find("username") > -1 and \
            strField.lower().find("=") > -1:
                g_strDefaultUsername = strField.split("=")[1]
                strFields = strFields.replace(strField, "username")


    g_strFieldsArray = strFields.split(g_strDelimiter)
    return True

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def SessionExists(strSessionPath):
    # Returns True if a session specified as value for strSessionPath already
    # exists within the SecureCRT configuration.
    # Returns False otherwise.
    try:
        objTosserConfig = crt.OpenSessionConfiguration(strSessionPath)
        return True
    except Exception as objInst:
        return False


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def OpenPathInDefaultApp(strFile):
    strPlatform = sys.platform
    crt.Session.SetStatusText("Platform: {0}".format(strPlatform))
    crt.Sleep(200)
    try:
        if sys.platform.startswith('darwin'):
            subprocess.call(('open', strFile))
        elif strPlatform == "win32":
            os.startfile(strFile)
        elif sys.platform.startswith('linux'):
            subprocess.call(('xdg-open', strFile))
        else:
            MsgBox("Unknown operating system:  " + os.name)
    except Exception, objErr:
        MsgBox(
            "Failed to open " + strFile + " with the default app.\n\n"  +
            str(objErr).replace('\\\\', '\\').replace('u\'', '\''))

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def Import():
    global g_strHostsFile, strFieldDesignations, g_strErrors, g_strDelimiter
    global g_strDefaultProtocol, g_nDataLines, g_strSessionsCreated, g_nSessionsCreated
    global g_strDefaultFolder, g_strDefaultUsername
    g_strHostsFile = crt.Dialog.FileOpenDialog(
        "Please select the host data file to be imported.",
        "Open",
        g_strHostsFile,
        "CSV/Text Files (*.txt;*.csv)|*.txt;*.csv|All files (*.*)|*.*")

    if g_strHostsFile == "":
        return

    nStartTime = time.time()
    bFoundHeader = False
    nLine = 0
    vSessionInfo = []
    # Open our data file for reading
    with open(g_strHostsFile, "r") as objDataFile:
        # Iterate over each of the lines in the file, processing them one by one.
        for strLine in objDataFile:
            strLine = strLine.strip("\r\n")
            nLine += 1
            # if nLine == 1 or (nLine % 10) == 0:
            crt.Session.SetStatusText("Processing line #{0} from import file: {1}".format(nLine, str(strLine)))
            bSaveSession = False
            strSessionPath = ""
            strPort = ""
            strProtocol = ""
            strHostName = ""
            strUserName = ""
            strEmulation = ""
            strFolder = ""
            strDescription = ""
            strLogonScript = ""

            if not bFoundHeader:
                strFieldDesignations = strLine
                # Validate the data file
                if not ValidateFieldDesignations(strFieldDesignations):
                    return
                else:
                    # Get a timer reading so that we can calculate how long it takes to import.
                    nStartTime = time.time()
                    bFoundHeader = True
            else:
                vSessionInfo = strLine.split(g_strDelimiter)
                if len(vSessionInfo) < len(g_strFieldsArray): if strLine.strip() == "": strLine = "[Empty Line]" g_strErrors = ("\n" + "Insufficient data on line #{0:04d}: {1:s}{2:s}".format(nLine, strLine, g_strErrors)) else: # Variable used to determine if a session file should actually be # created, or if there was an unrecoverable error (and the session # should be skipped). bSaveSession = True # Now we will match the items from the new file array to the correct # variable for the session's ini file for nFieldIndex in xrange(0, len(vSessionInfo)): if nFieldIndex >= len(g_strFieldsArray):
                            g_strErrors = ("\n" +
                                "Error: Too many data fields({0:d}) found on line #{1:04d}: {2:s}".format(len(vSessionInfo), nLine, strLine) +
                                "<-- This line should only have these {0:d} fields: {1:s}{2:s}".format(len(g_strFieldsArray), g_strDelimiter.join(g_strFieldsArray), g_strErrors))
                            bSaveSession = False
                            break

                        #MsgBox("nFieldIndex: {0}\nlen(vSessionInfo):{1}\n{2}:{3}".format(nFieldIndex, len(vSessionInfo), g_strFieldsArray[nFieldIndex], vSessionInfo[nFieldIndex]))
                        strFieldLabel = g_strFieldsArray[nFieldIndex].strip().lower()
                        if strFieldLabel == "session_name":
                            strSessionName = vSessionInfo[nFieldIndex].strip()
                            # Check folder name for any invalid characters
                            mSession = re.search(r"[\\\|\/\:\*\?\\\"\<\>]", strSessionName)
                            if mSession:
                                bSaveSession = False
                                g_strErrors = ("\nError: Invalid characters found in SessionName \"{0}\" specified on line #{1:04d}: {2:s}{3:s}".format(
                                    strSessionName, nLine, strLine, g_strErrors))

                        elif strFieldLabel == "logon_script":
                            strLogonScript = vSessionInfo[nFieldIndex].strip()

                        elif strFieldLabel == "port":
                            strPort = vSessionInfo[nFieldIndex].strip()
                            if not strPort == "":
                                if not strPort.isdigit():
                                    bSaveSession = False
                                    g_strErrors = ("\nError: Invalid port \"{0}\" specified on line #{1:04d}: {2:s}{3:s}".format(
                                        strPort, nLine, strLine, g_strErrors))

                        elif strFieldLabel == "protocol":
                            strProtocol = vSessionInfo[nFieldIndex].lower().strip()


                            if strProtocol == "ssh2":
                                strProtocol = "SSH2"
                            elif strProtocol == "ssh1":
                                strProtocol = "SSH1"
                            elif strProtocol == "telnet":
                                strProtocol = "Telnet"
                            elif strProtocol == "serial" or strProtocol == "tapi":
                                bSaveSession = False
                                g_strErrors = ("\n" +
                                    "Error: Unsupported protocol \"" + vSessionInfo[nFieldIndex].strip() +
                                    "\" specified on line #" +
                                    "{0:04d}: {1:s}".format(nLine, strLine) +
                                    g_strErrors)
                            elif strProtocol == "rlogin":
                                strProtocol = "RLogin"
                            else:
                                if g_strDefaultProtocol <> "":
                                    strProtocol = g_strDefaultProtocol
                                else:
                                    bSaveSession = False
                                    g_strErrors = ("\n" +
                                        "Error: Invalid protocol \"" + strProtocol +
                                        "\" specified on line #" +
                                        "{0:04d}: {1:s}".format(nLine, strLine) +
                                        g_strErrors)

                        elif strFieldLabel == "hostname":
                            strHostName = vSessionInfo[nFieldIndex].strip()
                            if strHostName == "":
                                bSaveSession = False
                                g_strErrors = ("\n" +
                                    "Error: Hostname field on line #{0:04d} is empty: {1:s}".format(nLine, strLine) +
                                    g_strErrors)

                        elif strFieldLabel == "username":
                            strUserName = vSessionInfo[nFieldIndex].strip()

                        elif strFieldLabel == "emulation":
                            strEmulation = vSessionInfo[nFieldIndex].lower().strip()
                            if strEmulation == "xterm":
                                strEmulation = "Xterm"
                            elif strEmulation == "vt100":
                                strEmulation = "VT100"
                            elif strEmulation == "vt102":
                                strEmulation = "VT102"
                            elif strEmulation == "vt220":
                                strEmulation = "VT220"
                            elif strEmulation == "ansi":
                                strEmulation = "ANSI"
                            elif strEmulation == "linux":
                                strEmulation = "Linux"
                            elif strEmulation == "scoansi":
                                strEmulation = "SCOANSI"
                            elif strEmulation == "vshell":
                                strEmulation = "VShell"
                            elif strEmulation == "wyse50":
                                strEmulation = "WYSE50"
                            elif strEmulation == "wyse60":
                                strEmulation = "WYSE60"
                            else:
                                bSaveSession = False
                                g_strErrors = ("\n" +
                                    "Error: Invalid emulation \"{0}\" specified on line #{1:04d}: {2:s}{3:s}".format(
                                        strEmulation, nLine, strLine, g_strErrors))

                        elif strFieldLabel == "folder":
                            strFolderOrig = vSessionInfo[nFieldIndex].strip()
                            strFolder = strFolderOrig.lower()
                            if strFolder == "":
                                strFolder = g_strDefaultFolder

                            # Check folder name for any invalid characters
                            # Note that a folder can have subfolder designations,
                            # so '/' is a valid character for the folder (path).
                            mSession = re.search('[\\|\\:\\*\\?\\\\"\\<\\>]', strFolder)
                            if mSession:
                                bSaveSession = False
                                g_strErrors = ("\n" +
                                    "Error: Invalid characters in folder \"{0:s}\" specified on line #{1:04d}: {2:s}{3:s}".format(
                                        strFolder, nLine, strLine, g_strErrors))
                            else:
                                strFolder = strFolderOrig

                        elif strFieldLabel == "description":
                            strCurDescription = vSessionInfo[nFieldIndex].strip()
                            if strDescription == "":
                                strDescription = strCurDescription
                            else:
                                strDescription = "{0}\\r{1}".format(strDescription, strCurDescription)
                                strDescription = strDescription.replace("\\r", "\r")

                        else:
                            # If there is an entry that the script is not set to use
                            # in strFieldDesignations, stop the script and display a
                            # message
                            strMsg1 = "Error: Unknown field designation: {0:s}\n\tSupported fields are as follows:\n\n\t{1:s}\n\nFor a description of the supported fields, see the comments in the sample script file.".format(g_strFieldsArray[nFieldIndex], g_strSupportedFields)

                            if g_strErrors.strip() <> "":
                                strMsg1 = (strMsg1 + "\n\n" +
                                    "Other errors found so far include: " +
                                    g_strErrors)
                            MsgBox(strMsg1, "Import Data To SecureCRT Sessions: Data File Error")
                            return
                    if bSaveSession:
                        # Use hostname if a session_name field wasn't present
                        if strSessionName == "":
                            strSessionName = strHostName

                        # Canonicalize the path to the session, as needed
                        strSessionPath = strSessionName
                        if strFolder.strip() == "":
                            strFolder = g_strDefaultFolder

                        if strFolder != "":
                            strSessionPath = strFolder + "/" + strSessionName

                        if strUserName.strip() == "":
                            strUserName = g_strDefaultUsername

                        # Strip any leading '/' characters from the session path
                        strSessionPath = strSessionPath.strip('/')

                        if SessionExists(strSessionPath):
                            if not g_bOverwriteExistingSessions:
                                # Append a unique tag to the session name, if it already exists
                                strSessionPath = "{0:s}(import_({1:s})".format(strSessionPath, datetime.datetime.now().strftime("%Y%m%d_%H%M%S.%f")[:19])

                        #MsgBox(
                        #    "Line #{0}: {1}\nbSaveSession: {2}\nSessionPath: {3}\n\nPort: {4}\nProtocol: {5}\nHostname: {6}\nUsername: {7}\nEmulation: {8}\nFolder: {9}\nDescription: {10}\n\n{11}".format(
                        #        nLine, strLine, bSaveSession, strSessionPath, strPort, strProtocol, strHostName, strUserName, strEmulation, strFolder, strDescription, g_strErrors))

                        # Now: Create the session.
                        # ===================================================================
                        # Copy the default session settings into new session name and set the
                        # protocol.  Setting protocol protocol is essential since some variables
                        # within a config are only available with certain protocols.  For example,
                        # a telnet configuration will not be allowed to set any port forwarding
                        # settings since port forwarding settings are specific to SSH.
                        objConfig = crt.OpenSessionConfiguration("Default")
                        if strProtocol == "":
                            strProtocol = g_strDefaultProtocol

                        objConfig.SetOption("Protocol Name", strProtocol)

                        # We opened a default session & changed the protocol, now we save the
                        # config to the new session path:
                        objConfig.Save(strSessionPath)

                        # Now, let's open the new session configuration we've saved, and set
                        # up the various parameters that were specified in the file.
                        objConfig = crt.OpenSessionConfiguration(strSessionPath)
                        if objConfig.GetOption("Protocol Name") != strProtocol:
                            MsgBox("Error: Protocol not set. Expected \"{0}\", but got \"{1}\"".format(strProtocol, objConfig.GetOption("Protocol Name")))
                            return

                        if strDescription != "":
                            vDescription = strDescription.split("\r")
                            objConfig.SetOption("Description", vDescription)

                        if strLogonScript != "":
                            objConfig.SetOption("Script Filename V2", strLogonScript)
                            objConfig.SetOption("Use Script File", True)

                        objConfig.SetOption("Emulation", strEmulation)

                        if strProtocol.lower() <> "serial":
                            if strHostName != "":
                                objConfig.SetOption("Hostname", strHostName)

                            if strUserName != "":
                                objConfig.SetOption("Username", strUserName)

                        if strProtocol.upper() == "SSH2":
                            if strPort == "":
                                strPort = 22
                            objConfig.SetOption("[SSH2] Port", int(strPort))
                        elif strProtocol.upper() == "SSH1":
                            if strPort == "":
                                strPort = "22"
                            objConfig.SetOption("[SSH1] Port", int(strPort))
                        elif strProtocol.upper() == "TELNET":
                            if strPort == "":
                                strPort = "23"
                            objConfig.SetOption("Port", int(strPort))


                        # If you would like ANSI Color enabled for all imported sessions (regardless
                        # of value in Default session, remove comment from following line)
                        # ---------------------------------------------------------------------------
                        objConfig.SetOption("ANSI Color", True)

                        # Add other "SetOption" calls desired here...
                        # ---------------------------------------------------------------------------
                        objConfig.SetOption("Auto Reconnect", False)
                        objConfig.SetOption("Color Scheme", "Monochrome")
                        objConfig.SetOption("Color Scheme Overrides Ansi Color", True)
                        objConfig.SetOption("Copy to clipboard as RTF and plain text", True)
                        objConfig.SetOption("Line Send Delay", 15)
                        objConfig.SetOption("Log Filename V2", "${VDS_USER_DATA_PATH}\_ScrtLog(%S)_%Y%M%D_%h%m%s.%t.txt")
                        objConfig.SetOption("Rows", 60)
                        objConfig.SetOption("Cols", 140)
                        objConfig.SetOption("Use Word Delimiter Chars", True)
                        objConfig.SetOption("Word Delimiter Chars", " <>()+=$%!#*")

                        objConfig.Save()

                        if g_strSessionsCreated <> "":
                            g_strSessionsCreated = g_strSessionsCreated + "\n"

                        g_strSessionsCreated = g_strSessionsCreated + "    " + strSessionPath
                        g_nSessionsCreated += 1

            # Reset all variables in preparation for reading in the next line of
            # the hosts info file.
            strEmulation = ""
            strPort = ""
            strHostName = ""
            strFolder = ""
            strUserName = ""
            strSessionName = ""
            strDescription = ""
            nDescriptionLineCount = 0
            g_nDataLines += 1

    nTimeElapsed = time.time() - nStartTime
    strResults = "Import operation completed in %2.3f seconds." % (nTimeElapsed)

    if g_nSessionsCreated > 0:
        strResults = (strResults + "\n" +
            "-" * 70 + "\n" +
            "Number of Sessions created: %d\n" % (g_nSessionsCreated))
        strResults = strResults + "\n" + g_strSessionsCreated
    else:
        strResults = (strResults + "\n" +
            "-" * 70 + "\n" +
            "No sessions were created from %d lines of data." % (g_nDataLines))

    crt.Session.SetStatusText("Import operation completed in {0:2.3f} seconds".format(nTimeElapsed))

    # Log activity information to a file for debugging purposes...
    strFilename = "{0}/__SecureCRT-Session-ImportLog-{1}.txt".format(g_strMyDocs, g_strDateTimeTag)
    if g_strErrors == "":
        strResults = (
            "No errors/warnings encountered from the import operation.\n\n{0:s}".format(strResults))
    else:
        strResults = "Errors/warnings from this operation include:{0}\n{1}\n{2}\n\n".format(
            g_strErrors, "-" * 70, strResults)

    cFilenames = [
        "{0}/__SecureCRT-Session-ImportLog-{1}.txt".format(g_strMyDocs,     g_strDateTimeTag).replace("\\", "/"),
        "{0}/__SecureCRT-Session-ImportLog-{1}.txt".format(g_strMyDesktop,  g_strDateTimeTag).replace("\\", "/"),
        "{0}/__SecureCRT-Session-ImportLog-{1}.txt".format(GetConfigPath(), g_strDateTimeTag).replace("\\", "/")
        ]

    bSuccess = False

    for strFilename in cFilenames:
        try:
            objFile = open(strFilename, "w")
            bSuccess = True
        except:
            crt.Session.SetStatusText("Unable to open results file.")
            strResults = (strResults + "\n" +
                "Failed to write summary results to: {0}".format(strFilename))
        if not os.path.isfile(strFilename):
            bSuccess = False
        else:
            break

    if not bSuccess:
        if ":\\" in g_strMyDocs:
            strResults = strResults.replace("\n", "\r\n")
        crt.Clipboard.Text = strResults
        crt.Dialog.MessageBox(
            "Attempted to write summary results to the file locations below, " +
            "but access was denied.\r\n\t{0}".format("\r\n\t".join(cFilenames)) +
            "\r\n\r\nResults are in the clipboard. " +
            "Paste them into your favorite app now to see what occurred.")
        return


    objFile.write(strResults)
    objFile.close()


    # Display the log file as an indication that the information has been
    # imported.
    OpenPathInDefaultApp(strFilename)
    crt.Session.SetStatusText("")


Import()

My Python Logon Script LogicalSystemsLogonScript.py:

# $language = "python"
# $interface = "1.0"

import SecureCRT

def Main():

    # Get the session path... since on Windows, we might get a back-slash
    # char instead of a /, convert any '\' received into '/':
    strSessionPath = crt.Session.Path.replace("\\", "/")
    
    # Now, split on "/" and take the first one from the right ([-1]),
    # which will be our session name:
    strSessionName = strSessionPath.split("/")[-1]
    
    # turn on synchronous mode so we don't miss any data
    crt.Screen.Synchronous = True
    
    # Wait for a string that looks like first Junos prompt: 
    crt.Screen.WaitForString("root@MX% ")
    
    # Send your command followed by a carriage return
    crt.Screen.Send("cli\r")

    # Wait for a string that looks like Junos operational prompt:
    crt.Screen.WaitForString("root@MX> ")

    # send the command and a carriage return
    crt.Screen.Send("set cli logical-system " + strSessionName + "\r")
  
    # turn off synchronous mode to restore normal input processing
    crt.Screen.Synchronous = False

Main()

Sources of info: