Advertisement
Guest User

Untitled

a guest
May 3rd, 2019
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 45.87 KB | None | 0 0
  1. # $language = "Python"
  2. # $interface = "1.0"
  3.  
  4. # RunCommandsOnMultipleHostsAndLogResults.py
  5. #
  6. # Last Modified: 21 May, 2018
  7. # - If a commands file cannot be opened/read, complain "nicely"
  8. # rather than having the script bomb out with an exception.
  9. # - If parent folder specified in the log file template doesn't
  10. # exist, try to create it before attempting to write results
  11. # or errors.
  12. # - If unable to write to a results file or an error log file,
  13. # the script will continue and attempt to perform the work it
  14. # has been instructed to do -- regardless of ability to log
  15. # errors/results. Errors will still be displayed when the
  16. # script completes.
  17. # - Allow host to be specified as either existing sessions in the
  18. # session manager (in which case the existing session is used to
  19. # establish the connection), or a hostname/IP which which an ad
  20. # hoc connection is made. To force SecureCRT to make an ad hoc
  21. # connection even if an existing session is found to match the
  22. # host entry specified, set g_bUseExistingSessions = False below.
  23. # If a host matches an existing session, and the session has
  24. # a saved username and password (or Automate Logon option is
  25. # enabled, the script will not send any credentials in the Connect()
  26. # function defined below. Instead, it is expected that the session
  27. # will authenticate itself using credentials stored within the session.
  28. #
  29. # - Standardize logging to one function to avoid code duplication and
  30. # facilitate consistency with error reporting message format.
  31. #
  32. # - Ensure that errors are logged to the same file with "All(Errors)"
  33. # in the name, even if the template log file name doesn't have the
  34. # "IPADDRESS" substitution.
  35. #
  36. # - Only respond to Password: prompts if they appear as the left-most
  37. # item on the screen.
  38. #
  39. # Last Modified: 17 May, 2018
  40. # - Make it so that the global commands file is not needed if the hosts
  41. # file has a host-specific command file specified for all hosts.
  42. #
  43. # Last Modified: 11 May, 2018
  44. # - Add support for specifying a unique command file specific to each host.
  45. # To take advantage of this feature, your hosts.txt file should have this
  46. # format:
  47. # ---------------------------------------------------------------------
  48. # hostname1;commandfileA.txt
  49. # hostname2;commandfileA.txt
  50. # hostname3;commandfileA.txt
  51. # ipaddress1;commandFileB.txt
  52. # ipaddress2;commandFileB.txt
  53. # hostname4;commandFileB.txt
  54. # ---------------------------------------------------------------------
  55. # If a host-specific file is not specified for a host, the commands from
  56. # the file specified in the global g_strCommandsFile variable are used.
  57. # Host-specific command files can be specified as a fully-qualified (AKA
  58. # "Absolute") file path OR as a file name, in which case it is required
  59. # that the file exist in the same location as the hosts file.
  60. #
  61. # Last Modified: 08 May, 2018
  62. # - Converted from VBScript to Python as an example.
  63. #
  64. # - Errors like connection/authentication are now logged to a single
  65. # "AllErrors" log file (same base name as the other log file(s)).
  66. #
  67. # - Default log file name template uses both IPADDRESS and COMMAND.
  68. # However, the g_bLogToIndividualFilesPerCommand is set to False, and
  69. # the g_bLogToIndividualFilesPerHost is set to True so that one log
  70. # file per host will be generated by default. If a single log file for
  71. # all hosts and all commands is desired, simply set both:
  72. # g_bLogToIndividualFilesPerCommand = False
  73. # g_bLogToIndividualFilesPerHost = False
  74. #
  75. # - Log file names will all share the same time stamp which correlates to
  76. # the time at which this script was intially launched, instead of the
  77. # time at which each command is run. This will help in collating log
  78. # files in terms of instantiations of the script.
  79. #
  80. # DESCRIPTION:
  81. # Demonstrates how to connect to hosts and send commands, logging results of
  82. # each command to separate, uniquely-named files based on the host IP and/or
  83. # the command that has been sent. Logging behavior can be controlled either
  84. # by changing the variable named "g_strLogFileTemplate" (examples shown
  85. # below) to add/remove components or by setting one or both of the following
  86. # variables to True/False to get the desired logging behavior regardless of
  87. # the template log file name:
  88. # g_bLogToIndividualFilesPerHost
  89. # g_bLogToIndividualFilesPerCommand
  90. #
  91. # By default, this script will log commands and their results to individual
  92. # files - one file per each host. Here are some examples of modifying the
  93. # log file template.
  94. # Default: Log all commands to separate files based on host:
  95. # g_strLogFileTemplate = "{0}/##IPADDRESS--%Y-%m-%d--%H'%M'%S.txt".format(
  96. # g_strMyDocs)
  97. # --> Note: This is effectively equivalent to setting the following
  98. # variables to the values indicated below, WITHOUT modifying the
  99. # default g_strLogFileTemplate value:
  100. # g_bLogToIndividualFilesPerHost = True
  101. # g_bLogToIndividualFilesPerCommand = False
  102. #
  103. # Log Option #1: Log everything (all hosts and commands) to A SINGLE FILE.
  104. # g_strLogFileTemplate = "(0}/##%Y-%m-%d--%H'%M'%S.txt".format(
  105. # g_strMyDocs)
  106. # --> Note: This is effectively equivalent to setting the following
  107. # variables to the values indicated below, WITHOUT modifying the
  108. # default g_strLogFileTemplate value:
  109. # g_bLogToIndividualFilesPerHost = False
  110. # g_bLogToIndividualFilesPerCommand = False
  111. #
  112. # Log Option #2: Log hosts + commands in SEPARATE FILES; ONE FILE FOR
  113. # EACH COMMAND.
  114. # g_strLogFileTemplate = "{0}/##IPADDRESS--COMMAND--%Y-%m-%d--%H'%M'%S.txt".format(
  115. # g_strMyDocs)
  116. # --> Note: This is effectively equivalent to setting the following
  117. # variables to the values indicated below, WITHOUT modifying the
  118. # default g_strLogFileTemplate value:
  119. # g_bLogToIndividualFilesPerHost = True
  120. # g_bLogToIndividualFilesPerCommand = True
  121. #
  122. #
  123. # Host information is read from a file named "##hosts.txt" (located in
  124. # "Documents" folder)
  125. #
  126. # Commands to be run on each remote host are read in from a file named
  127. # "##commands.txt" (also located in "Documents" folder)
  128. #
  129. # This script does not interfere with a session's logging settings; instead,
  130. # the crt.Screen.ReadString() method is used to capture the output of a
  131. # command, and then native python code is used to write the captured data
  132. # to a file.
  133. #
  134.  
  135. import os, platform, time, re, errno
  136.  
  137. # -----------------------------------------------------------------------------
  138. def GetDocumentsFolder():
  139. objConfig = crt.OpenSessionConfiguration("Default")
  140. strOptionName = "Upload Directory V2"
  141. strOrigValue = objConfig.GetOption(strOptionName)
  142. objConfig.SetOption(strOptionName, "${VDS_USER_DATA_PATH}")
  143. objConfig.Save()
  144. objConfig = crt.OpenSessionConfiguration("Default")
  145. strMyDocs = objConfig.GetOption(strOptionName)
  146. objConfig.SetOption(strOptionName, strOrigValue)
  147. objConfig.Save()
  148. return strMyDocs.replace("\\", "/")
  149.  
  150. g_strMyDocs = GetDocumentsFolder()
  151.  
  152. # ##hosts.txt and ##commands.txt files are located in the current user's
  153. # ##MyDocuments folder. Hard-code to different paths as your needs dictate.
  154. g_strHostsFile = g_strMyDocs + "/##hosts.txt"
  155. g_strCommandsFile = g_strMyDocs + "/##commands.txt"
  156.  
  157. # Template used for formulating the name of the results file in which the
  158. # command output will be saved. You can choose to arrange the various
  159. # components (i.e. "IPADDRESS", "COMMAND", "%Y", "%m", "%d", "%H", etc.) of
  160. # the log file name into whatever order you want it to be.
  161. g_strLogFileTemplate = (g_strMyDocs +
  162. "/Log/##IPADDRESS-COMMAND-%Y-%m-%d--%H'%M'%S.txt")
  163.  
  164. g_bLogToIndividualFilesPerHost = True
  165. g_bLogToIndividualFilesPerCommand = False
  166.  
  167. # Add Time stats to the log file name based on the Template
  168. # defined by the script author. We do this here at the top
  169. # of the script so that even when logging to multiple hosts
  170. # in separate files, the time stamps will all show up the
  171. # same for easy sorting/grouping in your file explorer/finder.
  172. g_strLogFileTemplate = time.strftime(g_strLogFileTemplate, time.localtime())
  173.  
  174. # Comment character allows for comments to exist in either the ##host.txt or
  175. # ##commands.txt files. Lines beginning with this character will be ignored.
  176. g_strComment = "#"
  177.  
  178. g_vHosts = []
  179. g_vCommands = []
  180. g_nHostCount = 0
  181.  
  182. # If connecting through a proxy is required, comment out the second statement
  183. # below, and modify the first statement below to match the name of the firewall
  184. # through which you're connecting (as defined in global options within
  185. # SecureCRT)
  186. g_strFirewall = " /FIREWALL=myFireWallName "
  187. g_strFirewall = ""
  188.  
  189. # Username for authenticating to the remote system
  190. g_strUsername = "user"
  191. # Password for authenticating to the remote system
  192. g_strPassword = "p4$$w0rd"
  193.  
  194. # Global variable for housing details of any errors that might be encountered
  195. global g_strError
  196.  
  197. global g_objNewTab
  198. global g_strHost
  199.  
  200. global g_vDefaultCommands, g_nDefaultCommandCount, g_bDefaultCommandsFileExists
  201. g_vDefaultCommands = []
  202. g_nDefaultCommandCount = 0
  203. g_bDefaultCommandsFileExists = False
  204.  
  205. global g_bUseExistingSessions
  206. g_bUseExistingSessions = True
  207.  
  208. # -----------------------------------------------------------------------------
  209. def MainSub():
  210.  
  211. # Create arrays in which lines from the hosts and commands files will be
  212. # stored.
  213. global g_vHosts, g_vCommands, g_nDefaultCommandCount, g_vDefaultCommands
  214. # Create variables for storing information about the lines read from the
  215. # file
  216. g_nHostCount = 0
  217. nCommandCount = 0
  218.  
  219. global g_strHostsFile, g_strComment, g_strHost, g_objNewTab, g_strError
  220.  
  221. # Call the ReadDataFromFile() function defined in this script. It will
  222. # read in the hosts file and populate an array with non-comment lines that
  223. # will represent the hosts to which connections will be made later on.
  224. vReturnValsHostsFile = ReadDataFromFile(g_strHostsFile, g_strComment)
  225. if not vReturnValsHostsFile[0]:
  226. DisplayMessage("No hosts were found in file: \r\n " + g_strHostsFile)
  227. return
  228. g_vHosts = vReturnValsHostsFile[1]
  229. g_nHostCount = vReturnValsHostsFile[2]
  230. # crt.Dialog.MessageBox("Read in {0} hosts".format(g_nHostCount))
  231.  
  232. strErrors = ""
  233. strSuccesses = ""
  234.  
  235. # If the g_strCommandsFile path exists, load those commands into a
  236. # global array and populate global variables for use when a host needs
  237. # to send commands from this file. If a line in the hosts file contains
  238. # a host-specific commands file, this script will load those as needed.
  239. # Call the ReadDataFromFile() function to populate the array of commands
  240. # that will be sent for this host.
  241. if os.path.isfile(g_strCommandsFile):
  242. g_bDefaultCommandsFileExists = True
  243. vReturnValsCmdsFile = ReadDataFromFile(g_strCommandsFile, g_strComment)
  244. if not vReturnValsCmdsFile[0]:
  245. strError = (
  246. "WARNING: Default commands file does not contain any " +
  247. "commands: " + g_strCommandsFile + ".\r\n" +
  248. "Any lines in the hosts file which don't include a " +
  249. "commands file specification will fail.")
  250. strErrors = CaptureError(strErrors, strError)
  251. else:
  252. g_vDefaultCommands = vReturnValsCmdsFile[1]
  253. g_nDefaultCommandCount = vReturnValsCmdsFile[2]
  254. else:
  255. g_bDefaultCommandsFileExists = False
  256.  
  257. # Before attempting any connections, ensure that the "Auth Prompts In
  258. # Window" option in the Default session is already enabled. If not, prompt
  259. # the user to have the script enable it automatically before continuing.
  260. # Before continuing on with the script, ensure that the session option
  261. # for handling authentication within the terminal window is enabled
  262. objConfig = crt.OpenSessionConfiguration("Default")
  263. bAuthInTerminal = objConfig.GetOption("Auth Prompts In Window")
  264. if not bAuthInTerminal:
  265. strMessage = ("" +
  266. "The 'Default' session (used for all ad hoc " +
  267. "connections) does not have the 'Display logon prompts in " +
  268. "terminal window' option enabled, which is required for this " +
  269. "script to operate successfully.\r\n\r\n")
  270. if not PromptYesNo(
  271. strMessage +
  272. "Would you like to have this script automatically enable this " +
  273. "option in the 'Default' session so that next time you run " +
  274. "this script, the option will already be enabled?"):
  275. return
  276.  
  277. # User answered prompt with Yes, so let's set the option and save
  278. objConfig.SetOption("Auth Prompts In Window", True)
  279. objConfig.Save()
  280.  
  281.  
  282. # Iterate through each element of our g_vHosts array...
  283. for nIndex in range(0, g_nHostCount):
  284. # Arrays are indexed starting at 0 (zero)
  285.  
  286. # Store the current host in a variable so we don't have to remember
  287. # what "g_vHosts(nIndex)" means.
  288. g_strHost = g_vHosts[nIndex]
  289.  
  290. # Exit the loop if the host name is empty (this means we've
  291. # reached the end of our array
  292. if g_strHost == "":
  293. return
  294.  
  295. strCommandsFilename = ""
  296. bContinue = True
  297.  
  298. if ";" in g_strHost:
  299. vHostElems = g_strHost.split(";")
  300. g_strHost = vHostElems[0]
  301. strCommandsFilename = vHostElems[1]
  302. if strCommandsFilename == "":
  303. strCommandsFilename = g_strCommandsFile
  304. strCommandsFilenameOrig = strCommandsFilename
  305. if not os.path.isfile(strCommandsFilename):
  306. # Check if the file path is relative to where hosts.txt
  307. # lives
  308. strCommandsFilename = os.path.join(
  309. os.path.dirname(os.path.normpath(g_strHostsFile)),
  310. strCommandsFilename)
  311. if not os.path.isfile(strCommandsFilename):
  312. strError = (
  313. "Host-specific command file not found for host '" +
  314. g_strHost + "': " + strCommandsFilenameOrig + " (" + strCommandsFilename + ")")
  315. strErrors = CaptureError(strErrors, strError)
  316. bContinue = False
  317.  
  318. if bContinue:
  319. g_strError = ""
  320. # Now call the ReadDataFromFile() function for the commands file.
  321. vReturnValsCmdsFile = ReadDataFromFile(strCommandsFilename, g_strComment)
  322. g_vCommands = vReturnValsCmdsFile[1]
  323. nCommandCount = vReturnValsCmdsFile[2]
  324. if not vReturnValsCmdsFile[0]:
  325. strError = (
  326. "Error attempting to read host-specific file for host '" +
  327. g_strHost + "': " + g_strError)
  328. strErrors = CaptureError(strErrors, strError)
  329. bContinue = False
  330.  
  331. else:
  332. # If we've had any host-specific command file change, we'll need
  333. # to use the common commands file to avoid running the last-known
  334. # host-specific commands with this new host that doesn't have a
  335. # commands file specified; use the default commands file.
  336.  
  337. # Call the ReadDataFromFile() function to populate the array of commands
  338. # that will be sent for this host.
  339. if not g_bDefaultCommandsFileExists:
  340. strError = (
  341. "While working on host '" +
  342. g_strHost + "... could not send any commands; " +
  343. "default commands file was not found. Check the " +
  344. "g_strCommandsFile variable; it should point to " +
  345. "a valid commands file. Alternatively, edit " +
  346. "your hosts file and make sure it contains a " +
  347. "host-specific commands file.")
  348. strErrors = CaptureError(strErrors, strError)
  349. bContinue = False
  350.  
  351. elif g_nDefaultCommandCount == 0:
  352. strError = (
  353. "While working on host '" +
  354. g_strHost + "... could not send any commands; default " +
  355. "commands file is empty.")
  356. strErrors = CaptureError(strErrors, strError)
  357. bContinue = False
  358.  
  359. else:
  360. # We have a valid default commands file and it has commands
  361. # in it. Let's use it.
  362. g_vCommands = g_vDefaultCommands
  363. nCommandCount = g_nDefaultCommandCount
  364.  
  365.  
  366. if bContinue:
  367. # Build up a string containing connection information and options.
  368. # /ACCEPTHOSTKEYS should only be used if you suspect that there might be
  369. # hosts in the hosts.txt file to which you haven't connected before, and
  370. # therefore SecureCRT hasn't saved out the SSH2 server's host key. If
  371. # you are confused about what a host key is, please read through the
  372. # white paper:
  373. # http://www.vandyke.com/solutions/host_keys/index.html
  374. #
  375. # A best practice would be to connect manually to each device,
  376. # verifying each server's hostkey individually before running this
  377. # script.
  378. #
  379. # If you want to authenticate with publickey authentication instead of
  380. # password, in the assignment of strConnectString below, replace:
  381. # " /AUTH password,keyboard-interactive /PASSWORD " + g_strPassword + _
  382. # with:
  383. # " /AUTH publickey /I \"full_path_to_private_key_file\" " + _
  384. if g_bUseExistingSessions and SessionExists(g_strHost):
  385. strConnectString = ("/S {0}".format(g_strHost))
  386. else:
  387. strConnectString = ("" +
  388. g_strFirewall +
  389. " /SSH2 " +
  390. " /L " + g_strUsername +
  391. " /AUTH password,keyboard-interactive /PASSWORD " + g_strPassword +
  392. " " + g_strHost)
  393.  
  394. #crt.Dialog.MessageBox(strConnectString)
  395. # Call the Connect() function defined below in this script. It handles
  396. # the connection process, returning success/fail.
  397. if not Connect(strConnectString):
  398. strError = "Failed to connect to " + g_strHost + ": " + g_strError
  399. strErrors = CaptureError(strErrors, strError)
  400.  
  401. else:
  402. # If we get to this point in the script, we're connected (including
  403. # authentication) to a remote host successfully.
  404. g_objNewTab.Screen.Synchronous = True
  405. g_objNewTab.Screen.IgnoreEscape = True
  406.  
  407. # Once the screen contents have stopped changing (polling every
  408. # 350 milliseconds), we'll assume it's safe to start interacting
  409. # with the remote system.
  410. if not WaitForScreenContentsToStopChanging(250):
  411. strError = ("Error: " +
  412. "Failed to detect remote ready status for host: " +
  413. "{0}. {1}".format(g_strHost, g_strError))
  414. strErrors = CaptureError(strErrors, strError)
  415. else:
  416. # Get the shell prompt so that we can know what to look for when
  417. # determining if the command is completed. Won't work if the
  418. # prompt is dynamic (e.g. changes according to current working
  419. # folder, etc)
  420. nRow = g_objNewTab.Screen.CurrentRow
  421. strPrompt = g_objNewTab.Screen.Get(
  422. nRow,
  423. 0,
  424. nRow,
  425. g_objNewTab.Screen.CurrentColumn - 1)
  426. strPrompt = strPrompt.strip(" \r\n")
  427.  
  428. # crt.Dialog.MessageBox("Here is the prompt: {0}".format(strPrompt))
  429. # Send each command one-by-one to the remote system:
  430. for strCommand in g_vCommands:
  431. if strCommand == "":
  432. break
  433.  
  434. # Send the command text to the remote
  435. # crt.Dialog.MessageBox("About to send cmd: {0}".format(str(strCommand)))
  436. g_objNewTab.Screen.Send("{0}\r".format(strCommand))
  437.  
  438. # Wait for the command to be echo'd back to us.
  439. g_objNewTab.Screen.WaitForString(strCommand)
  440.  
  441. # Since we don't know if we're connecting to a cisco switch or a
  442. # linux box or whatever, let's look for either a Carriage Return
  443. # (CR) or a Line Feed (LF) character in any order.
  444. vWaitFors = ["\r", "\n"]
  445. bFoundEOLMarker = False
  446. while True:
  447. # Call WaitForStrings, passing in the array of possible
  448. # matches.
  449. g_objNewTab.Screen.WaitForStrings(vWaitFors, 1)
  450.  
  451. # Determine what to do based on what was found)
  452. nMatchIndex = g_objNewTab.Screen.MatchIndex
  453. if nMatchIndex == 0: # Timed out
  454. break
  455.  
  456. if nMatchIndex in [1,2]: # found either CR or LF
  457. # Check to see if we've already seen the other
  458. # EOL Marker
  459. if bFoundEOLMarker:
  460. break
  461.  
  462. # If this is the first time we've been through
  463. # here, indicate as much, and then loop back up
  464. # to the top and try to find the other EOL
  465. # marker.
  466. bFoundEOLMarker = True
  467.  
  468. # Now that we know the command has been sent to the remote
  469. # system, we'll begin the process of capturing the output of
  470. # the command.
  471.  
  472. strResult = ""
  473. # Use the ReadString() method to get the text displayed
  474. # while the command was runnning. Note that the ReadString
  475. # usage shown below is not documented properly in SecureCRT
  476. # help files included in SecureCRT versions prior to 6.0
  477. # Official. Note also that the ReadString() method captures
  478. # escape sequences sent from the remote machine as well as
  479. # displayed text. As mentioned earlier in comments above,
  480. # if you want to suppress escape sequences from being
  481. # captured, set the Screen.IgnoreEscape property = True.
  482. strResult = g_objNewTab.Screen.ReadString(strPrompt)
  483.  
  484. # Set the log file name based on the remote host's IP
  485. # address and the command we're currently running. We also
  486. # add a date/timestamp to help make each filename unique
  487. # over time.
  488. strLogFile = g_strLogFileTemplate.replace(
  489. "IPADDRESS",
  490. g_objNewTab.Session.RemoteAddress)
  491. if g_bLogToIndividualFilesPerHost:
  492. strLogFile = g_strLogFileTemplate
  493. if "IPADDRESS" in g_strLogFileTemplate:
  494. strLogFile = g_strLogFileTemplate.replace(
  495. "IPADDRESS", g_objNewTab.Session.RemoteAddress)
  496. else:
  497. strLogFile = os.path.join(
  498. os.path.dirname(g_strLogFileTemplate),
  499. g_objNewTab.Session.RemoteAddress +
  500. os.path.basename(g_strLogFileTemplate))
  501. else:
  502. if "IPADDRESS" in g_strLogFileTemplate:
  503. strLogFile = g_strLogFileTemplate.replace(
  504. "IPADDRESS",
  505. "ALLHOSTS")
  506. else:
  507. strLogFile = g_strLogFileTemplate
  508. if not "ALLHOSTS" in g_strLogFileTemplate:
  509. strLogFile = os.path.join(
  510. os.path.dirname(g_strLogFileTemplate),
  511. "ALLHOSTS_" +
  512. os.path.basename(g_strLogFileTemplate))
  513.  
  514. if "COMMAND" in strLogFile:
  515. if g_bLogToIndividualFilesPerCommand:
  516. # Replace any illegal characters that might have been
  517. # introduced by the command we're running (e.g. if the
  518. # command had a path or a pipe in it)
  519. strCleanCmd = strCommand.replace('/', "[SLASH]")
  520. strCleanCmd = strCleanCmd.replace('\\', "[BKSLASH]")
  521. strCleanCmd = strCleanCmd.replace(':', "[COLON]")
  522. strCleanCmd = strCleanCmd.replace( '*', "[STAR]")
  523. strCleanCmd = strCleanCmd.replace( '?', "[QUESTION]")
  524. strCleanCmd = strCleanCmd.replace( '"', "[QUOTE]")
  525. strCleanCmd = strCleanCmd.replace( '<', "[LT]")
  526. strCleanCmd = strCleanCmd.replace( '>', "[GT]")
  527. strCleanCmd = strCleanCmd.replace( '|', "[PIPE]")
  528. strLogFile = strLogFile.replace("COMMAND", strCleanCmd)
  529. else:
  530. strLogFile = strLogFile.replace("COMMAND", "ALLCMDS")
  531.  
  532. if not os.path.isdir(os.path.dirname(strLogFile)):
  533. try:
  534. os.makedirs(os.path.dirname(strLogFile))
  535. except Exception, objInst:
  536. if objInst.errno != errno.EEXIST:
  537. strErrors = CaptureError(strErrors,
  538. "Failed to create directory " +
  539. "structure for output files: " +
  540. str(objInst))
  541.  
  542. try:
  543. with open(strLogFile, "a") as objFile:
  544. # If you do not want the command logged along with the results,
  545. # command out the following script statement:
  546. objFile.write(
  547. "=" * 80 + "\n" +
  548. "Results of command " +
  549. "'{0}' sent to host '{1}':\n".format(strCommand,
  550. g_strHost) + "-" * 80)
  551.  
  552. # Write out the results of the command and a separator
  553. objFile.write("\n{0}\n".format(strResult))
  554. except Exception, objInst:
  555. strErrors = CaptureError(strErrors,
  556. "Failed to open file for " +
  557. "writing results of command '" + strCommand +
  558. "': " + str(objInst))
  559.  
  560. # Now disconnect from the current machine before connecting to
  561. # the next machine
  562. while g_objNewTab.Session.Connected:
  563. g_objNewTab.Session.Disconnect()
  564. crt.Sleep(100)
  565.  
  566. strSuccesses += "\n{0}".format(g_strHost)
  567. # End of WaitForScreenContentsToStopChanging()
  568. # End of Connect()
  569. # End of if bContinue
  570.  
  571. # End of 'for nIndex in range(...)'
  572.  
  573. strMsg = "Commands were sent to the following hosts:\n{0}".format(
  574. strSuccesses)
  575.  
  576. if strErrors <> "":
  577. strMsg += ("\n\nErrors were encountered connecting to " +
  578. "these hosts:\n{0}".format(strErrors))
  579.  
  580. DisplayMessage(strMsg)
  581.  
  582. if strErrors <> "":
  583. if crt.Dialog.MessageBox(
  584. "Copy these errors to the clipboard?", "Copy to clipboard?", 4) == 6:
  585. crt.Clipboard.Text = strErrors
  586.  
  587. # End of def MainSub()
  588.  
  589. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  590. def ReadDataFromFile(strFile, strComment):
  591. # strFile: IN parameter specifying full path to data file.
  592. # strComment: IN parameter specifying string that preceded
  593. # by 0 or more space characters will indicate
  594. # that the line should be ignored.
  595. # Return value:
  596. # Returns an array where the elements of the array have the following
  597. # meanings.
  598. # vParams[0]: OUT parameter indicating success/failure of this
  599. # function
  600. # vParams[1]: OUT parameter (destructive) containing array
  601. # of lines read in from file.
  602. # vParams[2]: OUT parameter integer indicating number of lines read
  603. # in from file.
  604. # vParams[3]: OUT parameter indicating number of comment/blank lines
  605. # found in the file
  606. global g_strError
  607. strFile = strFile.replace("\\", "/")
  608.  
  609. # Set up return values
  610. # Start of with an empty list/array:
  611. vLines = []
  612. nLineCount = 0
  613. nCommentLines = 0
  614.  
  615. vOutParams = [False, vLines, nLineCount, nCommentLines]
  616.  
  617. # Check to see if the file exists... if not, bail early.
  618. if not os.path.exists(strFile):
  619. DisplayMessage("File not found: {0}".format(strFile))
  620. g_strError = "File not found: {0}".format(strFile)
  621. return vOutParams
  622.  
  623. # Used for detecting comment lines, a regular expression object
  624. p = re.compile("(^[ \\t]*(?:{0})+.*$)|(^[ \\t]+$)|(^$)".format(strComment))
  625.  
  626. try:
  627. # Open a TextStream Object to the file...
  628. objFile = open(strFile, 'r')
  629. except Exception as objInst:
  630. g_strError = "Unable to open '{0}' for reading: {1}".format(
  631. strFile, str(objInst))
  632. return vOutParams
  633.  
  634. # Read in just the first byte so that we can tell if the encoding isn't right.
  635. # better to display a warning than to have things go awry and not really know
  636. # why...
  637. try:
  638. b = str(objFile.read(1))
  639. except Exception as objInst:
  640. g_strError = "Unable to read data from file '{0}': {1}".format(
  641. strFile, strObjInst)
  642.  
  643. if len(b) < 1:
  644. # if the file is empty...
  645. g_strError = "File is empty: {0}".format(strFile)
  646. return vOutParams
  647.  
  648. if ord(b) == 239:
  649. objFile.close()
  650. strMsg = "UTF-8 format is not supported. File must be saved in ANSI format:\r\n{0}".format(strFile)
  651. DisplayMessage(strMsg)
  652. g_strError = strMsg
  653. return vOutParams
  654. elif ord(b) == 255 or ord(b) == 254:
  655. objFile.close()
  656. strMsg = "Unicode format is not supported. File must be saved in ANSI format:\r\n{0}".format(strFile)
  657. DisplayMessage(strMsg)
  658. g_strError = strMsg
  659. return vOutParams
  660. else:
  661. # We know we're likely an ANSI encoded file... close the file
  662. # and re-open so that we don't lose the first byte
  663. objFile.close()
  664. objFile = open(strFile, 'r')
  665.  
  666. for strLine in objFile:
  667. strLine = strLine.strip(' \r\n')
  668.  
  669. # Look for comment lines that match the pattern
  670. # [whitespace][strComment]
  671. if p.match(strLine):
  672. # Line matches our comment pattern... ignore it
  673. nCommentLines += 1
  674. else:
  675. vLines.append(strLine)
  676. nLineCount += 1
  677.  
  678. # Check to make sure we actually have commands to run
  679. if nLineCount < 1:
  680. vOutParams = [False, vLines, nLineCount, nCommentLines]
  681. g_strError = 'No valid lines found in file: {0}'.format(strFile)
  682. return vOutParams
  683.  
  684. # crt.Dialog.MessageBox("Read in {0} lines from file: {1}".format(nLineCount, strFile))
  685. vOutParams = [True, vLines, nLineCount, nCommentLines]
  686. return vOutParams
  687.  
  688.  
  689. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  690. def Connect(strConnectInfo):
  691. # Connect in a new tab to the host specified
  692. global g_objNewTab, g_strError, g_strHost, g_strUsername
  693.  
  694. bWaitForAuthToCompleteBeforeReturning = False
  695. bLetCallerDetectAndHandleConnectionErrors = True
  696.  
  697. try:
  698. g_objNewTab = crt.Session.ConnectInTab(
  699. strConnectInfo,
  700. bWaitForAuthToCompleteBeforeReturning,
  701. bLetCallerDetectAndHandleConnectionErrors)
  702. except Exception, objInst:
  703. g_strError = "Connection attempt raised an exception.\r\n{0}".format(
  704. str(objInst))
  705. return False
  706.  
  707. if g_objNewTab.Session.Connected <> True:
  708. if crt.GetLastErrorMessage() == "":
  709. g_strError = "Unknown error"
  710. #crt.Dialog.MessageBox(g_strError)
  711.  
  712. else:
  713. g_strError = str(crt.GetLastErrorMessage())
  714. #crt.Dialog.MessageBox(g_strError)
  715.  
  716. # You're not allowed to close the script tab (the tab in which the
  717. # script was launched oringinally), so only try if the new tab really
  718. # was a new tab -- not just reusing a disconnected tab.
  719. if g_objNewTab.Index <> crt.GetScriptTab().Index:
  720. g_objNewTab.Close()
  721.  
  722. return False
  723.  
  724. # Make sure the new tab is "Synchronous" so we can properly wait/send/etc.
  725. g_objNewTab.Screen.Synchronous = True
  726.  
  727. # Handle authentication in the new tab using the new tab's object reference
  728. # instead of 'crt'
  729. nAuthTimeout = 10 # seconds
  730.  
  731. # Modify the "$", the "]#", and/or the "->" in the array below to reflect
  732. # the variety of legitimate shell prompts you would expect to see when
  733. # authentication is successful to one of your remote machines.
  734. vPossibleShellPrompts = [
  735. "ogin:",
  736. "name:",
  737. "sword:",
  738. "Login incorrect",
  739. "authentication failed.",
  740. "$",
  741. "#",
  742. ">"]
  743.  
  744. bSendPassword = True
  745. bSendUsername = True
  746. if SessionExists(g_strHost):
  747. objConfig = crt.OpenSessionConfiguration(g_strHost)
  748. if objConfig.GetOption("Use Login Script") == True:
  749. bSendUsername = False
  750. bSendPassword = False
  751.  
  752. if objConfig.GetOption("Session Password Saved") == True:
  753. bSendPassword = False
  754.  
  755. if objConfig.GetOption("Username") <> "":
  756. bSendUsername = False
  757.  
  758. while True:
  759. try:
  760. g_objNewTab.Screen.WaitForStrings(vPossibleShellPrompts, nAuthTimeout)
  761.  
  762. # This Select..Case statement represents somewhat of a "state machine"
  763. # in which the value of Screen.MatchIndex represents the index of the
  764. # array of strings we told WaitForStrings() to look for. Based on this
  765. # index, we know what action needs to be performed next.
  766. if g_objNewTab.Screen.MatchIndex == 0:
  767. # ...time out condition...
  768. g_strError = ("Authentication timed out!\r\n" +
  769. "(Or you forgot to add a case for a successful shell " +
  770. "prompt in the vPossibleShellPrompts array)")
  771. # Disconnect from the host so that we can reuse the disconnected
  772. # tab for the next connection in the loop
  773. while g_objNewTab.Session.Connected:
  774. g_objNewTab.Session.Disconnect()
  775. crt.Sleep(100)
  776.  
  777. #crt.Dialog.MessageBox(g_strError)
  778. return False
  779.  
  780. elif g_objNewTab.Screen.MatchIndex == 1:
  781. if not bSendUsername:
  782. continue
  783.  
  784. #... "ogin: "...
  785. # Send the username only if it makes sense based on
  786. # the current position of the cursor:
  787. if g_objNewTab.Screen.CurrentColumn <= len("Login: "):
  788. g_objNewTab.Screen.Send(g_strUsername + "\r")
  789.  
  790. elif g_objNewTab.Screen.MatchIndex == 2:
  791. if not bSendUsername:
  792. continue
  793.  
  794. # ..."name:"...
  795. # Send the username, but only if it makes sense based on
  796. # the current position of the cursor:
  797. if g_objNewTab.Screen.CurrentColumn <= len("Username: "):
  798. g_objNewTab.Screen.Send(g_strUsername + "\r")
  799.  
  800. elif g_objNewTab.Screen.MatchIndex == 3:
  801. if not bSendPassword:
  802. continue
  803.  
  804. # ..."sword:"...
  805. # Send the password
  806. if g_objNewTab.Screen.CurrentColumn <= len("Password: "):
  807. g_objNewTab.Screen.Send(g_strPassword + "\r")
  808.  
  809. elif (g_objNewTab.Screen.MatchIndex == 4 or
  810. g_objNewTab.Screen.MatchIndex == 5):
  811. # ..."Login incorrect", "authentication failed."...
  812. g_strError = (
  813. "Password authentication to '" + g_strHost + "' as user '" +
  814. g_strUsername + "' failed.\r\n\r\n" +
  815. "Please specify the correct password for user " +
  816. "'" + g_strUsername + "'")
  817.  
  818. # Disconnect from the host so that we can reuse the disconnected
  819. # tab for the next connection in the loop
  820. while g_objNewTab.Session.Connected:
  821. g_objNewTab.Session.Disconnect()
  822. crt.Sleep(100)
  823. #crt.Dialog.MessageBox(g_strError)
  824. return False
  825.  
  826. elif (g_objNewTab.Screen.MatchIndex == 6 or
  827. g_objNewTab.Screen.MatchIndex == 7 or
  828. g_objNewTab.Screen.MatchIndex == 8):
  829. #...6,7,8 # "$", "#", or ">" <-- Shell prompt means auth success...
  830. g_objNewTab.Session.SetStatusText("Connected to " + g_strHost +
  831. " as " + g_strUsername)
  832. break
  833.  
  834. else:
  835. g_strError = ("Ooops! Looks like you forgot to add code " +
  836. "to handle the '{0}' prompt (vPossibleShellPrompts[{1}]).".format(
  837. vPossibleShellPrompts[g_objNewTab.Screen.MatchIndex - 1],
  838. g_objNewTab.Screen.MatchIndex - 1) +
  839. "\r\n" +
  840. "\r\n" +
  841. "Modify your script code's 'Connect()' function " +
  842. "to have a statment like this for the corresponding " +
  843. "shell prompt:\n\n" +
  844. "elif g_objNewTab.Screen.MatchIndex == " +
  845. "{0}:".format(g_objNewTab.Screen.MatchIndex) +
  846. "\n # put your code to handle this case here" +
  847. "\n # ." +
  848. "\n # ." +
  849. "\n # .")
  850.  
  851. #crt.Dialog.MessageBox(g_strError)
  852. while g_objNewTab.Session.Connected:
  853. g_objNewTab.Session.Disconnect()
  854. crt.Sleep(100)
  855.  
  856. return False
  857.  
  858. except Exception, objInst:
  859. # Most likely if there was a problem here, it would have been caused by
  860. # an unexpected disconnect occurring while WaitForStrings() was running
  861. # as called above. If error, set the global description variable and
  862. # then exit the function.
  863. g_strError = "{0}\n{1}".format(str(crt.GetLastErrorMessage()), str(objInst))
  864.  
  865. # Ensure that the session is disconnected before we exit this
  866. # function. If there are subsequent hosts to loop through, we don't
  867. # want a connected tab interfering with the next host's connection
  868. # attempts
  869. while g_objNewTab.Session.Connected:
  870. g_objNewTab.Session.Disconnect()
  871. crt.Sleep(100)
  872.  
  873. return False
  874.  
  875. # If the code gets here, then we must have been successful connecting and
  876. # authenticating to the remote machine; return the value of True
  877. # for the Connect() function.
  878. return True
  879.  
  880. # -----------------------------------------------------------------------------
  881. def CaptureError(strErrors, strError):
  882. global g_strLogFileTemplate
  883. if strErrors == "":
  884. strErrors = strError
  885. else:
  886. strErrors = "{0}\r\n{1}".format(strErrors, strError)
  887. strLogFile = g_strLogFileTemplate
  888. if "IPADDRESS" in strLogFile:
  889. strLogFile = strLogFile.replace("IPADDRESS", "All(Errors)")
  890. else:
  891. strLogFile = os.path.join(os.path.dirname(strLogFile),
  892. "All(Errors)" + os.path.basename(strLogFile))
  893.  
  894. strLogFile = strLogFile.replace("COMMAND", "")
  895.  
  896. if not os.path.isdir(os.path.dirname(strLogFile)):
  897. try:
  898. os.makedirs(os.path.dirname(strLogFile))
  899. except Exception, objInst:
  900. if objInst.errno != errno.EEXIST:
  901. if not "Failed to create" in strError:
  902. strErrors += ("\r\nFailed to create directory " +
  903. "structure for log file: " +
  904. str(objInst))
  905.  
  906. try:
  907. with open(strLogFile, "a") as objFile:
  908. objFile.write("=" * 80)
  909. objFile.write("\r\n{0}\r\n".format(strError))
  910. except Exception as objInst:
  911. if not (
  912. "Failed to create" in strError or
  913. "Failed to open" in strError):
  914. strErrors += "\r\nFailed to open file for writing: {1}".format(
  915. strLogFile, str(objInst))
  916.  
  917. return strErrors
  918.  
  919. # -----------------------------------------------------------------------------
  920. def WaitForScreenContentsToStopChanging(nMsDataReceiveWindow):
  921. # This function relies on new data received being different from the
  922. # data that was already received. It won't work if, as one example, you
  923. # have a screenful of 'A's and more 'A's arrive (because one screen
  924. # "capture" will look exactly like the previous screen "capture").
  925. global g_objNewTab
  926.  
  927. # Store Synch flag for later restoration
  928. bOrig = g_objNewTab.Screen.Synchronous
  929. # Turn Synch off since speed is of the essence; we'll turn it back on (if
  930. # it was already on) at the end of this function
  931. g_objNewTab.Screen.Synchronous = False
  932.  
  933. # Be "safe" about trying to access Screen.Get(). If for any reason we
  934. # get disconnected, we don't want the script to error out on the problem
  935. # so we'll just return false and handle writing something to our log
  936. # file for this host.
  937. try:
  938. strLastScreen = g_objNewTab.Screen.Get(1,1,g_objNewTab.Screen.Rows,g_objNewTab.Screen.Columns)
  939. while True:
  940. crt.Sleep(nMsDataReceiveWindow)
  941.  
  942. # Be "safe" about trying to access Screen.Get(). If for any reason we
  943. # get disconnected, we don't want the script to error out on the problem
  944. # so we'll just return false and handle writing something to our log
  945. # file for this host.
  946. strNewScreen = g_objNewTab.Screen.Get(1,1,g_objNewTab.Screen.Rows, g_objNewTab.Screen.Columns)
  947. if strNewScreen == strLastScreen:
  948. break
  949.  
  950. strLastScreen = strNewScreen
  951.  
  952. # Restore the Synch setting
  953. g_objNewTab.Screen.Synchronous = bOrig
  954.  
  955. return True
  956.  
  957. except Exception, objInst:
  958. # Most likely if there was a problem here, it would have been caused by
  959. # an unexpected disconnect occurring while WaitForStrings() was running
  960. # as called above. If error, set the global description variable and
  961. # then exit the function.
  962. g_strError = crt.GetLastErrorMessage()
  963.  
  964. # Ensure that the session is disconnected before we exit this
  965. # function. If there are subsequent hosts to loop through, we don't
  966. # want a connected tab interfering with the next host's connection
  967. # attempts
  968. while g_objNewTab.Session.Connected:
  969. g_objNewTab.Session.Disconnect()
  970. crt.Sleep(100)
  971.  
  972. g_objNewTab.Screen.Synchronous = bOrig
  973. return False
  974.  
  975. # -----------------------------------------------------------------------------
  976. def PromptYesNo(strText):
  977. vbYesNo = 4
  978. return crt.Dialog.MessageBox(strText, "SecureCRT", vbYesNo)
  979.  
  980. # -----------------------------------------------------------------------------
  981. def DisplayMessage(strText):
  982. crt.Dialog.MessageBox(strText)
  983.  
  984.  
  985. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  986. def SessionExists(strSessionPath):
  987. # Returns True if a session specified as value for strSessionPath already
  988. # exists within the SecureCRT configuration.
  989. # Returns False otherwise.
  990. try:
  991. objTosserConfig = crt.OpenSessionConfiguration(strSessionPath)
  992. return True
  993. except Exception as objInst:
  994. return False
  995.  
  996.  
  997. # Call the main subroutine named "MainSub"
  998. MainSub()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement