cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
6057
Views
0
Helpful
32
Replies

Asynchronous TCL Scripting

mmoles001
Level 1
Level 1

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

32 Replies 32

Joe Clarke
Cisco Employee
Cisco Employee

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.

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. 

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?

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

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}        

}

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

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.

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.

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.

I left it at 2 minutes because it shouldnt take a minute or so to warm up, and the rate that it iterates should be within 5 seconds max.   I was also wondering why i am starting up Proc checkDevice which runs procDevice, which then reruns itself.   wouldn't it just make more sence to initally target procDevice to begin with?

::cisco::eem::event_register_timer watchdog time 120 maxrun 120

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

After swapping the checkDevice calls "after 0 checkDevice $cli1(fd) A 11 1000"  with "after 0 procDevice $cli1(fd) A 11 1000"   , it only fires D once and it hangs after.   I reaaaaaaaly dont understand why.

Apr  9 03:00:37.085: %HA_EM-6-LOG: SSHTest2.tcl: Start

Apr  9 03:00:37.651: %HA_EM-6-LOG: SSHTest2.tcl: Before

Apr  9 03:00:37.651: %HA_EM-6-LOG: SSHTest2.tcl: After

Apr  9 03:00:37.651: %HA_EM-6-LOG: SSHTest2.tcl: After Timer

Apr  9 03:00:37.651: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

Apr  9 03:00:37.651: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:00:37.651: %HA_EM-6-LOG: SSHTest2.tcl: C - Fired

Apr  9 03:00:37.657: %HA_EM-6-LOG: SSHTest2.tcl: D - Fired

Apr  9 03:00:37.751: %HA_EM-6-LOG: SSHTest2.tcl: Q : D + 44

Apr  9 03:02:36.182: %HA_EM-6-LOG: SSHTest2.tcl: After vwait

Apr  9 03:02:36.182: %HA_EM-6-LOG: SSHTest2.tcl: Process Forced Exit- MAXRUN timer expired.

I then removed the { } in "after $waitTime {procDevice $interface $tempName $aNumber $waitTime}" and wholy crap.... i think it works....

Apr  9 03:09:47.331: %HA_EM-6-LOG: SSHTest2.tcl: Start

Apr  9 03:09:47.876: %HA_EM-6-LOG: SSHTest2.tcl: Before

Apr  9 03:09:47.876: %HA_EM-6-LOG: SSHTest2.tcl: After

Apr  9 03:09:47.876: %HA_EM-6-LOG: SSHTest2.tcl: After Timer

Apr  9 03:09:47.876: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

Apr  9 03:09:47.876: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:47.876: %HA_EM-6-LOG: SSHTest2.tcl: C - Fired

Apr  9 03:09:47.881: %HA_EM-6-LOG: SSHTest2.tcl: D - Fired

Apr  9 03:09:47.975: %HA_EM-6-LOG: SSHTest2.tcl: Q : D + 44

Apr  9 03:09:48.080: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:48.091: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  9 03:09:48.290: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:48.306: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  9 03:09:48.505: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:48.515: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  9 03:09:48.715: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:48.730: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  9 03:09:48.877: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

Apr  9 03:09:48.882: %HA_EM-6-LOG: SSHTest2.tcl: D - Fired

Apr  9 03:09:48.930: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:48.945: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  9 03:09:49.145: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:49.150: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  9 03:09:49.349: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:49.370: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  9 03:09:49.569: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:49.580: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  9 03:09:49.779: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:49.789: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  9 03:09:49.879: %HA_EM-6-LOG: SSHTest2.tcl: A - Fired

Apr  9 03:09:49.889: %HA_EM-6-LOG: SSHTest2.tcl: D - Fired

Apr  9 03:09:49.894: %HA_EM-6-LOG: SSHTest2.tcl: Q : D + 44

Apr  9 03:09:49.989: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired

Apr  9 03:09:49.999: %HA_EM-6-LOG: SSHTest2.tcl: Q : B + 22

Apr  9 03:09:50.198: %HA_EM-6-LOG: SSHTest2.tcl: B - Fired


Increased the poll rate to 10ms from 100, I removed the  X- Fired puts, changed the message to be easier to see....

Apr  9 03:15:40.171: %HA_EM-6-LOG: SSHTest2.tcl: Start

Apr  9 03:15:40.737: %HA_EM-6-LOG: SSHTest2.tcl: Before

Apr  9 03:15:40.737: %HA_EM-6-LOG: SSHTest2.tcl: After

Apr  9 03:15:40.737: %HA_EM-6-LOG: SSHTest2.tcl: After Timer

Apr  9 03:15:40.737: %HA_EM-6-LOG: SSHTest2.tcl: A A A

Apr  9 03:15:40.737: %HA_EM-6-LOG: SSHTest2.tcl: B B B

Apr  9 03:15:40.743: %HA_EM-6-LOG: SSHTest2.tcl: C C C

Apr  9 03:15:40.743: %HA_EM-6-LOG: SSHTest2.tcl: D D D

Apr  9 03:15:40.942: %HA_EM-6-LOG: SSHTest2.tcl: B B B

Apr  9 03:15:41.141: %HA_EM-6-LOG: SSHTest2.tcl: B B B

Apr  9 03:15:41.340: %HA_EM-6-LOG: SSHTest2.tcl: B B B

Apr  9 03:15:41.545: %HA_EM-6-LOG: SSHTest2.tcl: B B B

Apr  9 03:15:41.739: %HA_EM-6-LOG: SSHTest2.tcl: A A A

Apr  9 03:15:41.744: %HA_EM-6-LOG: SSHTest2.tcl: D D D

Apr  9 03:15:41.744: %HA_EM-6-LOG: SSHTest2.tcl: B B B

Apr  9 03:15:41.943: %HA_EM-6-LOG: SSHTest2.tcl: B B B

Apr  9 03:15:42.148: %HA_EM-6-LOG: SSHTest2.tcl: B B B

Apr  9 03:15:42.357: %HA_EM-6-LOG: SSHTest2.tcl: B B B

Apr  9 03:15:42.562: %HA_EM-6-LOG: SSHTest2.tcl: B B B

Its getting better.....  I realized i can remove the after poll_timer

and everything looks right... but CPU is out the roof.  It feels like recursion isnt the right approach.

3560Switch#show process cpu | inc TCL

409       77963         502     155304 85.97% 65.55% 21.55%   0 EEM TCL Proc

You could call procDevice directly.  The only reason I broke it out is because you had that extra syslog message in there.  But regardless you need to change this line in procDevice:

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

To:

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

That will fix the misfiring issue and may help CPU.

I had done that with no luck. CPU still burning.    I am wondering if its the recursion or what...

I tried to put it in an indefinate loop, but that doesnt work.  I used the ping so it would cause a delay without having to use "after"   It never steps into the while nor does it pass it.

proc procDevice { interface tempName aNumber waitTime } {

      puts "$tempName"

      while{1} {

           global poll_q

           puts "$tempName $tempName $tempName"

           if [catch {cli_exec $interface "#ping 1.1.4.1 size 15000  repeat $waitTime"} _cli_result] {

                error $_cli_result $errorInfo

           }

      }

      puts "This shouldnt appear"

}

Mar 30 01:53:02.383: %HA_EM-6-LOG: SSHTest2.tcl: Start

Mar 30 01:53:02.823: %HA_EM-6-LOG: SSHTest2.tcl: Before

Mar 30 01:53:02.823: %HA_EM-6-LOG: SSHTest2.tcl: After

Mar 30 01:53:02.823: %HA_EM-6-LOG: SSHTest2.tcl: After Timer

Mar 30 01:53:02.823: %HA_EM-6-LOG: SSHTest2.tcl: A

Mar 30 01:53:02.828: %HA_EM-6-LOG: SSHTest2.tcl: B

Mar 30 01:53:02.833: %HA_EM-6-LOG: SSHTest2.tcl: C

Mar 30 01:53:02.839: %HA_EM-6-LOG: SSHTest2.tcl: D

Mar 30 01:53:21.236: %HA_EM-6-LOG: SSHTest2.tcl: After vwait

Mar 30 01:53:21.236: %HA_EM-6-LOG: SSHTest2.tcl: Process Forced Exit- MAXRUN timer expired.

Review Cisco Networking for a $25 gift card