01-08-2015 04:01 AM
Hey,
I'm trying to write a script that will handle LTE > UMTS downgrades automatically. As part of this script, I'm doing a reset of the Cellular interface, wait for it to come up and take further measures if it doesn't come up or the Dialer interface does not negotiate an IP via DHCP. The relevant code for my problem is:
proc ::lte::session_open {} { if {![info exists v::session(fd)]} { print "CLI session not defined - creating one" if [catch {cli_open} result] { print "Unable to open a CLI session" error $result $::errorCode $::errorInfo exit 1 } else { array set v::session $result print "CLI session created - entering enabled mode" after 10 if [catch {cli_exec $v::session(fd) "ena"} result] { print "Unable to enter enabled mode:\n" error $result $::errorCode $::errorInfo } else { #print "Enabled OK" } } } else { print "CLI session already defined - ignoring open attempt" } } proc ::lte::session_send {cmd {flag {}} } { if {![info exists v::session(fd)]} { ::lte::session_open after 50 } after 50 if {$flag != {}} { set exp_confirm {[confirm]} set exp_yesno {yes|no} set exp_sure {are you sure} set exp_running {running-config} set reply_yes {y} set reply_running {running-config} set exp_reply_list [list [list expect $exp_confirm reply $reply_yes] [list expect $exp_yesno reply $reply_yes] [list expect $exp_sure reply $reply_yes] [list expect $exp_running reply $reply_running]] set clist [list command $cmd responses $exp_reply_list] cli_run_interactive { clist } return "" } else { # prepare expect and reply for log messages to filter them out if [catch {cli_exec $v::session(fd) $cmd} result] { print "Couldn't send command '$cmd':\n" error $result $::errorCode $::errorInfo exit 1 } else { #print "Command '$cmd' OK" # remove NC hostname: regsub -all {\n*ti2\d{8}j\d{3}\#} $result {} result # remove IC/CC/OC hostnames (future proofing): regsub -all {\n*FB\d{6}\#} $result {} result # remove log messages: set lines [split $result "\n"] foreach line $lines { # if the line begins with a date: regexp {^Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec} $line matched if [info exists matched] { # substitute out the log line in the result regsub {$line} $result {} result } } return $result } } } proc ::lte::session_close {} { if {![info exists v::session(fd)]} { print "CLI session not open, ignoring close attempt" } else { if [catch {cli_close $v::session(fd) $v::session(tty_id)} result] { print "Unable to close CLI session:\n" error $result $::errorCode $::errorInfo exit 1 } else { #print "CLI session closed OK" array set session {} } } } proc print {arg} { action_syslog msg "$arg\n" after 10 }
The output of the script is as expected, up to this point:
Jan 8 11:11:20.451: %HA_EM-6-LOG: lte.tcl: Resetting Cellular interface Jan 8 11:11:20.863: %SYS-5-CONFIG_I: Configured from console by on vty0 (EEM:lte.tcl) Jan 8 11:11:21.027: %LINEPROTO-5-UPDOWN: Line protocol on Interface Tunnel51, changed state to down Jan 8 11:11:21.027: %LINEPROTO-5-UPDOWN: Line protocol on Interface Tunnel50, changed state to down Jan 8 11:11:21.031: %LINEPROTO-5-UPDOWN: Line protocol on Interface Tunnel0, changed state to down Jan 8 11:11:23.027: %LINK-5-CHANGED: Interface Cellular0, changed state to reset Jan 8 11:11:23.027: %DIALER-6-UNBIND: Interface Ce0 unbound from profile Di1 Jan 8 11:11:24.027: %LINEPROTO-5-UPDOWN: Line protocol on Interface Cellular0, changed state to down Jan 8 11:11:24.131: %CISCO800-2-MODEM_DOWN: Cellular0 modem is now DOWN. Jan 8 11:11:28.027: %LINK-3-UPDOWN: Interface Cellular0, changed state to down Jan 8 11:11:34.651: %HA_EM-6-LOG: lte.tcl: Waiting until Cellular interface recovers %Remove Dialer Profile Configuration first Jan 8 11:11:37.467: %CISCO800-2-MODEM_UP: Cellular0 modem is now UP. Router#invalid command name "cli_open" while executing "cli_run_interactive { clist }" (procedure "::lte::session_send" line 16) invoked from within "::lte::session_send "copy start run" "yes"" (procedure "::lte::wait_until_up" line 28) invoked from within "::lte::wait_until_up" (procedure "::lte::reset_cell" line 13) invoked from within "::lte::reset_cell" (procedure "::lte::check_cell_band" line 8) invoked from within "::lte::check_cell_band" (procedure "::lte::run" line 5) invoked from within "::lte::run" 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: invalid command name "cli_open"
The only place in this script I actually call cli_open is in the proc ::lte::session_open, which is not run in this instance or it would have output messages like "CLI session not defined - creating one", "Unable to open a CLI session" or "CLI session created - entering enabled mode" due to the print statements in ::lte::session_open.
I am absolutely baffled as to why this is happening. Can anyone help me out?
Kind regards,
Pieter
01-10-2015 05:45 AM
What version of IOS is this? Post the contents of tmpsys:/lib/tcl/cli_lib.tcl from this router.
01-12-2015 12:07 AM
Hi,
Sorry for the late reply - I had no access to the network at the time.
The IOS version is 15.3(3)M1 (c800-universalk9-mz.SPA.153-3.M1.bin) on a Cisco 819G-4G-G-K9.
The contents of the cli_lib.tcl, specifically on cli_run_interactive, are:
# Open a CLI channel, enter "enable" mode and execute each command in the # list 'clist'. The format of each entry in the list is an array with three # members: # command - command to be executed # expect - a regular expression pattern match for the expected reply prompt # responses - a list of possible responses to the reply prompt constructed # as an array with two members: # expect - a regular expression pattern match for a possible reply prompt # reply - the replay for that expected prompt # As each command is executed its output is appended to a result variable. # Upon exhaustion of the input list, the CLI channel is closed and the # aggregate result is returned. # # Example: To clear counters for intrface fa0/0 use the following: # set cmdarr(command) "clear counters fa0/0" # set cmdarr(responses) [list] # set resps(expect) {[confirm]} # set resps(reply) "y" # lappend cmdarr(responses) [array get resps] # set rc [catch {cli_run_interactive [list [array get cmdarr]]} result] # # Possible errors raised: # 1. cannot get pty for exec # 2. cannot spawn exec # 3. error reading the first prompt # 4. error reading the channel # 5. cannot close channel proc cli_run_interactive { clist } { set rbuf "" if {[llength $clist] < 1} { return -code ok $rbuf } if {[catch {cli_open} result]} { return -code error $result } else { array set cliarr $result } if {[catch {cli_exec $cliarr(fd) "enable"} result]} { return -code error $result } foreach cmd $clist { array set sendexp $cmd if {[catch {cli_write $cliarr(fd) $sendexp(command)} result]} { return -code error $result } foreach response $sendexp(responses) { array set resp $response if {[catch {cli_read_pattern $cliarr(fd) $resp(expect)} result]} { return -code error $result } if {[catch {cli_write $cliarr(fd) $resp(reply)} result]} { return -code error $result } } if {[catch {cli_read $cliarr(fd)} result]} { return -code error $result } append rbuf $result } if {[catch {cli_close $cliarr(fd) $cliarr(tty_id)} result]} { puts "WARNING: $result" } return -code ok $rbuf }
If you're looking for something else, see the attached file.
KR,
Pieter
01-12-2015 12:44 PM
Can you try using cli_run_interactive outside of your own namespace (i.e., in global mode)? Also, you appear to be misusing it. You should have "cli_run_interactive $clist".
01-13-2015 03:58 AM
I had previously tried "cli_run_interactive $clist" as well as "cli_run_interactive {clist}" and "cli_run_interactive clist". None of them changed the outcome.
I did as you asked and created a script (in attachment) containing only session_open, session_send and session_close.
Now I'm getting:
Router#even man run session.tcl list must have an even number of elements while executing "array set sendexp $cmd" (procedure "cli_run_interactive" line 19) invoked from within "cli_run_interactive $clist" (procedure "session_send" line 16) invoked from within "session_send "copy start run" "yes"" 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: list must have an even number of elements Router#
Could this be due to the fact that I have several expect/reply elements to the same command?
01-17-2015 11:10 AM
01-19-2015 12:48 AM
Thanks for the suggestion. I'm still getting an error though:
Router(config)#do even man run session0.tcl TTY write error while executing "cli_run_interactive $clist" (procedure "session_send" line 16) invoked from within
I suspected this had something to do with defining the session before running cli_run_interactive, which defines the session itself.
I removed session_open and session_close from the script entirely and then got:
Router(config)#do even man run session0.tcl list must have an even number of elements while executing "array set sendexp $cmd" (procedure "cli_run_interactive" line 19) invoked from within "cli_run_interactive $clist" (procedure "session_send" line 16) invoked from within "session_send "copy start run" "yes"" 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)
01-19-2015 02:06 PM
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