Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.

Structure (timer) calling a method regularly in case of inactivity

by Iceman1884 (Initiate)
on Jul 27, 2007 at 22:40 UTC ( [id://629215] : perlquestion . print w/replies, xml ) Need Help??

Iceman1884 has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks, I am developing my first program in Perl (object oriented), and I need the help of an experienced Perl monk. My program is aimed at configuring several Cisco switches, to which the system is connected through Telnet sessions. During the execution of the program, I have to call regularly (every 30 seconds for instance) a method (saving the running configuration file) for each switch if no command is called, otherwise the Telnet connections to the switches are closed due to inactivity. I thought of a timer structure for each switch, which would be refreshed every time a method (or command) is called for the switch. Despite my web searches on threads and forks, I have really no idea on how to create this timer structure. Could anyone give me a hand? Thanks in advance! Iceman

Replies are listed 'Best First'.
Re: Structure (timer) calling a method regularly in case of inactivity (threads are perfect)
by BrowserUk (Patriarch) on Jul 28, 2007 at 02:48 UTC

    This is untested code, and given the lack of detail in your question, assumes a lot of things.

    The basic idea is to start a thread that monitors a shared time value and if we ever reach that time, issues the keep alive command and resets the timer.

    The class wraps the Telnet object (by composition) and overrides the cmd() method to ensure the timer gets reset before the cmd is issued. It also adds a DESTROY() method to clean up the thread when the session ends.

    Adding accessors and error handling is left as an exercise.

    package My::Telnet; use threads::shared; use Net::Telnet; ## Thread to keep session alive sub _keepAlive { my( $tn, $done, $keepAlive ) = @_; ## Die when signalled ## Wake up once per second while( not $done and sleep 1 ) { ## If it's time, send the keep alive cmd. if( time() >= $keepAlive ) { my @output = $tn->cmd( ## Keep alive command goes here ); ## and reset the timeout $keepAlive = time() + 30; } } } sub new { my( $class, $host, $port ) = @_; ## Create the session my $tn = Net::Telnet->new( host => $host, port => port, errmode => sub{ warn "Error: $_[ 0 ]"; }, ) or die $!; ## Some shared vars for communication my $done :shared = 0; my $keepAlive :shared = time() + 30; ## Start the thread. my $thread = threads->create( \&_keepAlive, $tn, $done, $keepAlive } or die $!; ## build the object and return it bless { done => \$done, telnet => $tn, host => $host, port => $port, thread => $thread, keepAlive => \$keepAlive; }, $class; } ## resets the timer to the time supplied ## or now + 30 seconds sub resetKeepAlive { my( $self, $timeout ) = @_; $$self->{ keepAlive } } = $timeout || ( time() + 30 ); } ## Issues a command ## Resetting the timer to prevent the next keep alive. sub cmd { my( $self, %cmdNargs ) = @_; ## reset the timer *before* issuing the command $self->resetKeepAlive; ## Do the command and return the output return $self->{ telnet }->cmd( %cmdNargs ) } ## Clean up by telling the thread to die ## waiting for it to do so ## and closing the connection. sub DESTROY { my( $self ) = @_; $$self->{ done } = 1; $self->{ thread }->join; close $self->{ telnet }; return; }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Structure (timer) calling a method regularly in case of inactivity
by Joost (Canon) on Jul 27, 2007 at 23:03 UTC
    Just how many servers (switches) are we talking about?

    If it's a fairly small number and/or your program is reasonably memory efficient, you could fork a separate process for each server. That would probably simplify the timeout issues, since you only need to deal with one connection and timer per process.

    If you have many servers and you can't afford to fork a process for each, my approach would be to use IO::Select with non-blocking IO. It's low-level but very efficient if done right. On the other hand, it's not a very easy technique if you're not familiar with select() and/or perl.

    Perl threads are IMHO completely useless for this kind of thing, since you can probably not share enough data to make it significantly more efficient than just fork()ing more processes.

Re: Structure (timer) calling a method regularly in case of inactivity
by GrandFather (Saint) on Jul 27, 2007 at 23:42 UTC

    There are a number of ways of achieving that sort of timed polling, but the best choice depends a lot on what else the application is doing and what else it interacts with.

    You could use Tk (Tk's with timer driven events) or POE (about which I know very little). You could hand roll a polled loop using a queue to manage events and sleep to wait until the next even is due.

    If you show us a sketch of your code we may be able to help a little more.

    DWIM is Perl's answer to Gödel
Re: Structure (timer) calling a method regularly in case of inactivity
by zentara (Archbishop) on Jul 28, 2007 at 11:30 UTC
    Glib ( the non-gui object class on which Gtk2 is built) will easily let you make a simple timer. See Roll your own Event-loop You can also subclass Glib but you may need to go to the maillist for help with that. See Subclass Glib
    #!/usr/bin/perl use warnings; use strict; use Glib; my $main_loop = Glib::MainLoop->new; my $count = 1; my $timer = Glib::Timeout->add (1000, \&timer_callback, undef, 1 ); sub timer_callback{ $count++; print "$count\n"; return 1; # return 0 to stop timer } my $count1 = 1; my $timer1 = Glib::Timeout->add (100, \&timer1_callback, undef, 1 ); sub timer1_callback{ $count1++; print "\t$count1\n"; return 1; } ### filehandle watch open (FH, "+> test.log") or warn "$!\n"; Glib::IO->add_watch (fileno 'FH', ['in'], \&watch_callback, 'FH', 1 ); $main_loop->run; #################################################################### sub watch_callback { my ($fd, $condition, $fh) = @_; my @lines = <FH>; print @lines; #always return TRUE to continue the callback return 1; }

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      Dear monks, Thank you very much for your help. As a new member, I am really impressed by the monk community's solidarity. I am trying to implement BrowserUk's thread method. Thanks again, Iceman
        Any chance you can simply increase the timeout at the Cisco device end or as part of your telnet connection? Many years ago I used Net::Telnet to visit a series of Cisco routers. I found Net::Telnet to be very capable and configurable. Plus, once connected to the Cisco router I could issue local commands to configure my session. I don't recall all the Cisco syntax but maybe there is some at the Cisco device end to keep your connection alive or a setting in Net::Telnet (or Net::Telnet::Cisco which I've not yet used). Just some ideas...