04-26-2016 01:59 PM
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"
}
Solved! Go to Solution.
05-19-2016 02:28 PM
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.
04-29-2016 08:20 AM
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.
05-18-2016 02:31 PM
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
05-19-2016 02:28 PM
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.
05-23-2016 03:01 PM
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!
05-23-2016 04:03 PM
What are the exact prompts you see when you run the "copy tftp..." command manually?
05-23-2016 06:11 PM
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#
05-24-2016 08:00 AM
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.
05-24-2016 08:03 AM
The catch is fine here. In fact, you should use catch around all your cli_* calls.
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide