cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
3090
Views
5
Helpful
8
Replies

TCL Script works until it gets registered

rtjensen4
Level 4
Level 4

Hello!

I'm fumbling my way through TCLtrying to fulfill a need.

I had an EEM script that would daily pull in a file via TFTP and apply the contents of that file. Idea is to update various ACLs daily in an automated fashion. This worked fine except that each day it created a config version, meaing my out of band managers and my LMS would pull in a new copy of the config daily even when things didnt actually change, but the the ACL file was update. Not the most effective use of resources.

I created a TCL script that does the following. Takes an argument passed in, uses it to create a file path and downloads a file to flash.

It MD5s the file and compares it to the MD5 of the previous day's downloaded file. If the MD5s are equal, does nothing and quits. If they're different, the commands in the file are applied and it's deleted from flash.

The script works fine when run from tclsh:

VID-RTR# tcltest.tcl VID-ACL.txt
VID-ACL.txt

VID-RTR#
23507465: Apr 26 16:55:12.669: Downloaded ACL file signature is the same as the previous. No changes made. Removing source file

I try to register the script and most of the commands aren't working. Most specifically, the exec commands. I've managed to get the scheduling part of it worked out for testing, I used this:

::cisco::eem::event_register_none

From what i've read, it has to do with the script being untrusted and running in Safe-TCL mode. I am asking for some help. How can I convert this script into a script that will run via cron daily and acheive the same results?


set ACLFile ""
set oldmd5 ""

if { $::argc > 0 } {d
set ACLFile $argv
} else {
puts "No ACL Filename Passed, exiting"
break
}

puts $argv

exec "enable"
exec "copy tftp://192.168.101.179/$ACLFile flash:"

if {[file exists "flash:$ACLFile"]} {

} else {
puts "File not downloaded, abort!"
break
}


if {![file exists "flash:md5old.txt"]} {
set sysout [open "syslog:" "w"]
puts $sysout {File not found, settting variable = 0}
close $sysout
set oldmd5 "0"
} else {
set fIN [open "flash:md5old.txt" "r"]
set oldmd52 [read $fIN]
set oldmd5 [string trim $oldmd52]
close $fIN
}


set fo [open "flash:md5old.txt" "w"]
set input [exec "verify /md5 flash:$ACLFile"]
regexp -all -nocase { = ([a-f0-9]{32})} $input matched md5
#puts "NewMD5: ^$md5^"
puts $fo $md5
close $fo

if {[string equal $oldmd5 $md5]} {
set sysout [open "syslog:" "w"]
puts $sysout {Downloaded ACL file signature is the same as the previous. No changes made. Removing source file}
close $sysout
exec "delete flash:$ACLFile"
} else {
set sysout [open "syslog:" "w"]
puts $sysout {Updates to ACL file found. Applying config and removing source file...}
exec "copy flash:$ACLFile running-config"
exec "copy run start"
close $sysout
exec "delete flash:$ACLFile"
}

1 Accepted Solution

Accepted Solutions

This command is interactive.  So you either need to configure "file prompt quiet" or interact with the prompts using cli_read_pattern and cli_write.

cli_write $cli(fd) "copy tftp://192.168.101.179/$ACLFile flash:"

cli_read_pattern $cli(fd) "192.168.101.179"

cli_write $cli(fd) "\r"

...

Essentially, repeat using cli_write and cli_read_pattern to watch for patterns in the prompts you get when doing the command manually until you would get back to the IOS EXEC prompt.

View solution in original post

8 Replies 8

Joe Clarke
Cisco Employee
Cisco Employee

Turning a tclsh script into EEM Tcl is not as simple as adding an event registration line.  In this case, you'll need to modify your argc behavior as well as the CLI handling.  You also don't need to use the syslog file system as EEM allows you to send syslogs using "action_syslog".

You can use my helper script at https://supportforums.cisco.com/document/12628061/migration-code-move-tclsh-eem-tcl to help with the CLI migration.  And look at the output from "show event manager detector none detail" to see how to use the variables to get the arg count and args from the none event detector.

Hi Joe,

Thanks for your response, sorry I didn't reply earlier.

I've modified the script and have all of it working except for two parts.

The part to transfer the file via TFTP to flash: and to copy run start.

Here's what I have now:

cli_exec $cli(fd) "copy tftp://192.168.101.179/$ACLFile flash:"

I run the script and it just times out due to MAXRUN, which I have set at 120 sec. Watching the TFTP server I do not see any activity from the router. Here's the logs I get:

Process Forced Exit- MAXRUN timer expired.
    while executing
"return -code error "error reading the channel: $result""
    (procedure "cli_exec" line 5)
    invoked from within
"cli_exec $cli(fd) "copy tftp://192.168.101.179/$ACLFile flash:""
    invoked from within
"$slave eval $Contents"
    (procedure "eval_script" line 7)
    invoked from within
"eval_script slave $scriptname"
    invoked from within
"if {$security_level == 1} {       #untrusted script
     interp create -safe slave
     interp share {} stdin slave
     interp share {} stdout slave
..."
    (file "tmpsys:/lib/tcl/base.tcl" line 50)
Tcl policy execute failed: Process Forced Exit- MAXRUN timer expired.

 Any ideas oh great one? :P

This command is interactive.  So you either need to configure "file prompt quiet" or interact with the prompts using cli_read_pattern and cli_write.

cli_write $cli(fd) "copy tftp://192.168.101.179/$ACLFile flash:"

cli_read_pattern $cli(fd) "192.168.101.179"

cli_write $cli(fd) "\r"

...

Essentially, repeat using cli_write and cli_read_pattern to watch for patterns in the prompts you get when doing the command manually until you would get back to the IOS EXEC prompt.

Oh excellent, now we're getting somewhere!

I tried using cli_write and cli_read_pattern as you suggested, it worked, it pulled in the file, however... right after I download the file, I do this:

set fin [cli_exec $cli(fd) "verify /md5 flash:$ACLFile"]
regexp -all -nocase { = ([a-f0-9]{32})} $fin matched md5

I run an MD5 check on the file and set the output to $fin variable, then run regexp on that output and save the computed hash to the $md5 variable.

When I use cli_write and cli_read_pattern, the output that's in the $fin variable is always the output from the TFTP transfer, not from the md5 validation.

INPUT: ^^
Accessing tftp://192.168.101.179/VID-ACL.txt...
Loading VID-ACL.txt from 192.168.101.179 (via Tunnel10): !
[OK - 10903 bytes]

10903 bytes copied in 1.352 secs (8064 bytes/sec)
 --More--        
 --More--        
VID-RTR#^^

and it should be the output from the MD5 check.

If I set file prompt quiet, everything works like a champ. I think the cli_write method is a better solution, I don't know how to get it to work properly.

I'm including the entire script up to where I am now for reference.


Thanks so much for your help so far!










What are the exact prompts you see when you run the "copy tftp..." command manually?

VID-RTR#copy tftp://192.168.101.179/VID-ACL.txt flash:
Destination filename [VID-ACL.txt]?
Accessing tftp://192.168.101.179/VID-ACL.txt...
Loading VID-ACL.txt from 192.168.101.179 (via Tunnel10): !
[OK - 10903 bytes]

10903 bytes copied in 1.540 secs (7080 bytes/sec)

VID-RTR#

I got it to work!

Thank you very much Joe for all your help. I ended up using this:

cli_write $cli(fd) "copy flash:$ACLFile running-config"
 cli_read_pattern $cli(fd) "Destination filename"
 cli_write $cli(fd) "\n"
 if [catch {cli_read $cli(fd) } result] {
  error $result $errorInfo
 } else {
 set cmd_output $result
 }

I understand what the catch part is for, but I don't really know how it works, or if I even need it. I just know I tested it and it works like a champ.

The catch is fine here.  In fact, you should use catch around all your cli_* calls.