cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
1785
Views
0
Helpful
7
Replies

cli_run_interactive: unexpected results

pieter.bryon
Level 1
Level 1

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

7 Replies 7

Joe Clarke
Cisco Employee
Cisco Employee

What version of IOS is this?  Post the contents of tmpsys:/lib/tcl/cli_lib.tcl from this router.

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

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".

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?

Give this a shot.

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)

This should do it for you.