Cisco Support Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Announcements

Welcome to Cisco Support Community. We would love to have your feedback.

For an introduction to the new site, click here. If you'd prefer to explore, try our test area to get started. And see here for current known issues.

New Member

Asynchronous TCL Scripting

I am trying to write some Asynchronous TCL code to make things more efficent.   This is just a simple example, but in reality it will SSH into a device, run a command, do some math, and write the output to the switch.

The whole "after" thing is throwing me off.  When i run it locally trough tclsh86 , i need to run the "update" command to follow up with the results.   How and what do i need to do to get this to work?

#***************************************CODE**********************************************************

::cisco::eem::event_register_timer watchdog time 15 maxrun 15

namespace import ::cisco::eem::*

namespace import ::cisco::lib::*

proc checkDevice { interface tempName aNumber waitTime} {

puts "  "

puts "$tempName - Start"

after $waitTime

puts "$tempName - After"

if [catch {cli_write $interface "event manager environment tempName $aNumber"} _cli_result] {

error $_cli_result $errorInfo

}

puts "$tempName - EXIT"

}

if [catch {cli_open} result] {

error $result $errorInfo

} else {

array set cli1 $result

}

if [catch {cli_exec $cli1(fd) "enable"} _cli_result] {

error $_cli_result $errorInfo

}

after 0 checkDevice $cli1(fd) A 11 1000

after 0 checkDevice $cli1(fd) B 22 200

after 0 checkDevice $cli1(fd) C 33 4000

after 0 checkDevice $cli1(fd) D 44 1000

if [catch {cli_exec $cli1(fd) "exit"} _cli_result] {

error $_cli_result $errorInfo

}

catch {cli_close $cli1(fd) $cli1(tty_id)} result

  • EEM Scripting
Everyone's tags (3)
32 REPLIES
Cisco Employee

Asynchronous TCL Scripting

It would likely help to understand your final goal.  This example has  many things that aren't correct, so understanding your desired end goal  would be useful to make sure EEM will actually do what you want.

New Member

Asynchronous TCL Scripting

I have multiple devices (1 - 12)  i need to SSH into to pull data from.  When i get that data, I need to compare it with my current configuration on the switch and make adjustments accordingly.     

Basically building off of this https://supportforums.cisco.com/thread/2249735

Before I had a script (DeviceReader1.tcl) that opened a vty to the switch, read in the 30 second average from the switch, then SSHed into the device , took a sample of the data 1 time a second for 5 seconds,  then it would exit out of the device putting it back into the switch, I then injected my new data into the 30 second average, and rewrote the data back to the event manager eviroment variable.   I then had a seperate script (Adjuster.tcl) that constantly read the global variables and calculated the data and made any changes to the switch if it needed it.

I realized that in my (DeviceReader) script that i could open 2 connections, 1 for the ssh, and other just for the switch.  That way, instead of having to re-SSH into the device, I could keep it open, and still be able to write to the environment variable. And the Adjuster script works like before.

The issue that I will run into is the limited number of VTY channels available.   This will work when we have only 6 or so devices configured, but anything more this wont work.

I then realized its possible to do Asyncronous Calls with TCL.   So, instead of having the X DeviceReader scripts and Adjuster script, i could have 1 file that could take care of everything almost doubling the amount of devices i could use, and probably consume a lot less resources as well as more efficent use of variables.

So, i guess a couple of other questions i have is, when you have 2 seperate scripts running, whats the best way to share variables?   Also, how do i setup a script to start on startup and run indefinetly?

Note:  I just found EEM_HTTP_SERVER_CODE example and understanding how some of that works. 

Cisco Employee

Asynchronous TCL Scripting

Ah, okay.  What you want to do should be possible.  For async programming, the trick is to use vwait to block while your asynchronous things run.  For example:

set exiting 0

proc check_q { } {

    global poll_timer poll_q exiting

    if { $poll_q != "" } {

        puts "Q : $poll_q"

        if { $poll_q == "DIE" } {

            set exiting 1

        }

        set poll_q {}

    }

    after $poll_timer {::check_q}

}

after $poll_timer {::check_q}

vwait exiting

The vwait call blocks until the variable on which it's waiting is true.  So in this case, it will run until the queue somehow contains the word, "DIE."  Does that help?

New Member

Re: Asynchronous TCL Scripting

When i run this, it only loops A.  Can i not have multiple process going at once?  How can I send a commands to different devices in parallell?   Do each of the devices need its own proc?

Note:  Some devices can be off or slower, so i prefer to not trigger each one once in a loop, then wait for all the replies, then loop again.

*****

Apr  8 09:37:30.305: %HA_EM-6-LOG: SSHTest2.tcl: Start

Apr  8 09:37:30.646: %HA_EM-6-LOG: SSHTest2.tcl: Before

Apr  8 09:37:30.646: %HA_EM-6-LOG: SSHTest2.tcl: After

Apr  8 09:37:30.646: %HA_EM-6-LOG: SSHTest2.tcl: After Timer

Apr  8 09:37:30.646: %HA_EM-6-LOG: SSHTest2.tcl: A - Start

Apr  8 09:37:31.647: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

Apr  8 09:37:32.649: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

Apr  8 09:37:33.650: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

Apr  8 09:37:34.651: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

Apr  8 09:37:35.668: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

Apr  8 09:37:36.686: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

...

*******

::cisco::eem::event_register_timer watchdog time 30 maxrun 30

namespace import ::cisco::eem::*

namespace import ::cisco::lib::*

set exiting 0

set poll_timer 100

set poll_q {}

proc checkDevice { interface tempName aNumber waitTime} {

      puts "$tempName - Start"

      while {1} {

           global poll_q

           after $waitTime

           puts "$tempName - Fired"

           if [catch {cli_write $interface "event manager environment tempName $aNumber"} _cli_result] {

                error $_cli_result $errorInfo

           }

           set poll_q "$tempName + $aNumber"

      }

}

proc check_q { } {

      global poll_timer poll_q exiting

      if { $poll_q != "" } {

           puts "Q : $poll_q"

           if { $poll_q == "DIE" } {

                set exiting 1

           }

      set poll_q {}

      }

after $poll_timer {::check_q}

}

puts "Start"

if [catch {cli_open} result] {

      error $result $errorInfo

} else {

      array set cli1 $result

}

if [catch {cli_exec $cli1(fd) "enable"} _cli_result] {

      error $_cli_result $errorInfo

}

puts "Before"

after 0 checkDevice $cli1(fd) A 11 1000

after 0 checkDevice $cli1(fd) B 22 200

after 0 checkDevice $cli1(fd) C 33 4000

after 0 checkDevice $cli1(fd) D 44 1000

puts "After"

after $poll_timer {::check_q}

puts "After Timer"

vwait exiting

puts "After vwait"

if [catch {cli_exec $cli1(fd) "exit"} _cli_result] {

      error $_cli_result $errorInfo

}

catch {cli_close $cli1(fd) $cli1(tty_id)} result

Cisco Employee

Asynchronous TCL Scripting

You're entering an infinite loop in your callback.  You don't want to do this.  If you need to continually call into it, have it set an after:  For example:

proc checkDevice { interface tempName aNumber waitTime} {

      puts "$tempName - Start"

     after $waitTime {procDevice $interface $tempName $aNumber $waitTime}

}

proc procDevice { interface tempName aNumber waitTime } {

           puts "$tempName - Fired"

           if [catch {cli_write $interface "event manager environment $tempName $aNumber"} _cli_result] {

                error $_cli_result $errorInfo

           }

           set poll_q "$tempName + $aNumber" 

           after $waitTime {procDevice $interface $tempName $aNumber $waitTime}        

}

New Member

Re: Asynchronous TCL Scripting

So i added the global poll_q to the procDevice because it may need to fire an event.   I also realized i forgot to put a $ in front of tempName in the cli_write command.  I also had forgotten to have set "conf t" before writing the event manager enviroment variables.

With or without those changes, while it starts those processes, it never steps into ProcDevice.  Another thing that was odd was the the CLI was running very sluggish.  Entering   "show run | sec event"  took a second or so to respond.  Also, there was no variables set either  not even a tempName.

Apr  8 10:09:59.372: %HA_EM-6-LOG: SSHTest2.tcl: Start

Apr  8 10:10:00.069: %HA_EM-6-LOG: SSHTest2.tcl: Before

Apr  8 10:10:00.069: %HA_EM-6-LOG: SSHTest2.tcl: After

Apr  8 10:10:00.069: %HA_EM-6-LOG: SSHTest2.tcl: After Timer

Apr  8 10:10:00.069: %HA_EM-6-LOG: SSHTest2.tcl: A - Start

Apr  8 10:10:00.069: %HA_EM-6-LOG: SSHTest2.tcl: B - Start

Apr  8 10:10:00.069: %HA_EM-6-LOG: SSHTest2.tcl: C - Start

Apr  8 10:10:00.069: %HA_EM-6-LOG: SSHTest2.tcl: D - Start

Apr  8 10:10:17.769: %HA_EM-6-LOG: SSHTest2.tcl: After vwait

Apr  8 10:10:17.769: %HA_EM-6-LOG: SSHTest2.tcl: Process Forced Exit- MAXRUN timer expired.

::cisco::eem::event_register_timer watchdog time 20 maxrun 20

namespace import ::cisco::eem::*

namespace import ::cisco::lib::*

set exiting 0

set poll_timer 100

set poll_q {}

proc checkDevice { interface tempName aNumber waitTime} {

      puts "$tempName - Start"

      after $waitTime {procDevice $interface $tempName $aNumber $waitTime}

}

proc procDevice { interface tempName aNumber waitTime } {

      global poll_q

      puts "$tempName - Fired"

      if [catch {cli_write $interface "event manager environment $tempName $aNumber"} _cli_result] {

           error $_cli_result $errorInfo

      }

      set poll_q "$tempName + $aNumber"

      after $waitTime {procDevice $interface $tempName $aNumber $waitTime}

}

proc check_q { } {

      global poll_timer poll_q exiting

      if { $poll_q != "" } {

           puts "Q : $poll_q"

           if { $poll_q == "DIE" } {

                set exiting 1

           }

      set poll_q {}

      }

      after $poll_timer {::check_q}

}

puts "Start"

if [catch {cli_open} result] {

      error $result $errorInfo

} else {

      array set cli1 $result

}

if [catch {cli_exec $cli1(fd) "enable"} _cli_result] {

      error $_cli_result $errorInfo

}

if [catch {cli_exec $cli1(fd) "config t"} _cli_result] {

      error $_cli_result $errorInfo

}

    puts "Before"

after 0 checkDevice $cli1(fd) A 11 1000

after 0 checkDevice $cli1(fd) B 22 200

after 0 checkDevice $cli1(fd) C 33 4000

after 0 checkDevice $cli1(fd) D 44 1000

puts "After"

after $poll_timer {::check_q}

puts "After Timer"

vwait exiting

puts "After vwait"

if [catch {cli_exec $cli1(fd) "exit"} _cli_result] {

      error $_cli_result $errorInfo

}

catch {cli_close $cli1(fd) $cli1(tty_id)} result

Cisco Employee

Re: Asynchronous TCL Scripting

Remove the {} around the calls to procDevice:

after $waitTime procDevice $interface $tempName $aNumber $waitTime

And you'll want to increase your maxrun to something like 120 to let it run for a while.

New Member

Re: Asynchronous TCL Scripting

Its firing once... but its not looping properly.   It also causes the switch to run very slow.   

Apr  8 11:20:34.098: %HA_EM-6-LOG: SSHTest2.tcl: Start

Apr  8 11:20:34.796: %HA_EM-6-LOG: SSHTest2.tcl: Before

Apr  8 11:20:34.796: %HA_EM-6-LOG: SSHTest2.tcl: After

Apr  8 11:20:34.796: %HA_EM-6-LOG: SSHTest2.tcl: After Timer

Apr  8 11:20:34.796: %HA_EM-6-LOG: SSHTest2.tcl: A - Start

Apr  8 11:20:34.796: %HA_EM-6-LOG: SSHTest2.tcl: B - Start

Apr  8 11:20:34.796: %HA_EM-6-LOG: SSHTest2.tcl: C - Start

Apr  8 11:20:34.796: %HA_EM-6-LOG: SSHTest2.tcl: D - Start

Apr  8 11:20:35.005: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  8 11:20:35.095: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  8 11:20:35.797: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

Apr  8 11:20:35.808: %HA_EM-6-LOG: SSHTest2.tcl: D - Fired

Apr  8 11:20:35.808: %HA_EM-6-LOG: SSHTest2.tcl: Q : D + 44

Apr  8 11:20:38.796: %HA_EM-6-LOG: SSHTest2.tcl: C - Fired

Apr  8 11:20:38.828: %HA_EM-6-LOG: SSHTest2.tcl: Q : C + 33

Apr  8 11:22:32.907: %HA_EM-6-LOG: SSHTest2.tcl: After vwait

Apr  8 11:22:32.913: %HA_EM-6-LOG: SSHTest2.tcl: Process Forced Exit- MAXRUN timer expired.

Cisco Employee

Re: Asynchronous TCL Scripting

What does your current script look like?  Also, you may want to increase your poll timers to be at least 1000.  The tighter the loop, the hotter the device will run.

865
Views
0
Helpful
32
Replies
This widget could not be displayed.