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

I'm trying to create a module for connecting to our Cisco devices. My problem is that if I connect via the module the connection is closed when the module subroutine is done (I think.)

This is the first module I've ever tried to create. The login_auth subroutine works perfectly fine if I put it into the scrip directly, and it appears to do exactly what I want when I run it (it does log in and authenticate). However, the rest of the script doesn't do anything because it seems like the interactive session isn't being passed through. I have a lot of scripts for various things that use it over and over and I want to create a module to just call common subroutines like that.

The Script:

#!/usr/bin/perl -w use lib '/enm01/custom/scripts/modules'; use Expect; use cisco qw(&login_auth);; my $exp = new Expect; # Parces the BASH arugments into Perl variables ( $UNAME, $PWD, $ENABLE, $DNAME, $IP, $LOG ) = @ARGV; # This logs in and authenticates to the device login_auth(@ARGV); # This checks to make sure the hostname is correct namecheck(); sub namecheck { if ( $DNAME !~ /U....WX[0-9][0-9]/ ) { $exp->send("show run | include hostname\n"); @hostintval = $exp->expect( 10,"#"); $newmatch = $hostintval[3]; @hostname = split(m[ |\n],$newmatch); foreach(@hostname) { if ( ($_ !~ m/^hostname$|^show$|^run$|\||^include$|^$ +|\cM$/) ) { if ( $DNAME ne $_ ) { print LOG "$DNAME" . " is inco +rrect please change to " . "$_\n"; $DNAME = $_; } } } } }

The Module:

package cisco; use strict; use Exporter; use Expect; use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK $UNAME $PWD $ENABLE $DNA +ME $IP $LOG $command $result $exp ); $VERSION = 1.00; @ISA = qw(Exporter); @EXPORT = (); @EXPORT_OK = qw(login_auth); ( $UNAME, $PWD, $ENABLE, $DNAME, $IP, $LOG ) = @ARGV; $exp = new Expect; open( LOG, ">>$LOG" ); sub login_auth { $command = "ssh $UNAME" . "@" . "$IP"; $exp->spawn($command) or die "Cannot spawn $command: $!\n"; my $connect = $exp->expect ( 30, [ qr/\(yes\/no\)\?\s*$/ => sub { $exp->send("yes\n"); +exp_continue; } ], [ qr/assword:\s*$/ => sub { $exp->send("$PWD\n"); } ], ); $result = $exp->expect(30, "#", ">"); if ($result == 2) { $exp->send("enable\n"); $result = $exp->expect(30, "assword:", "#"); if ($result == 1) { $exp->send("$ENABLE\n"); $result = $exp->expect(30, "assword:", "#"); if ($result == 1) { print LOG "Enable Password Rejected\n" +; exit(); } } } $exp->send("term len 0\n"); $exp->expect(10,"#"); } 1;

Replies are listed 'Best First'.
Re: SSH connection with module
by NetWallah (Canon) on Mar 10, 2014 at 01:22 UTC
    You are not persisting an object and associated states.

    You end up creating two completely separate Expect objects.

    You should create a cisco::new() method that creates an object (see 'perldoc perltoot') - this method should look something like this:

    sub new{ my ($class, $logname) = @_; $logname or die "NO log file name specified"; my $self = bless {}, $class; # Creating an empty instance $self->{EXP} = Expect::->new(); # Now has an EXP attribute open $self->{LOGFH}, ">>", $logname or die "Could not append to $l +ogname : $!"; return $self; }
    Once you get an object back from new(), you need to call login_auth like this:
    my $cisco = cisco::->new(); $cisco->login_auth();
    Of course, you will need to modify login_auth() to accept a parameter, preferably named $self.

    Update: Added LOG to new() method.

    So happy journey into the world of OO programming!

    If you run into concept issues with how or why things are done is a certain way, be sure to post specific questions here, for assistance.

            What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against?
                  -Larry Wall, 1992

      I got most of that but I have no idea what you mean by "you will need to modify login_auth() to accept a parameter, preferably named $self." Don't I have to use this because I have to pass the variables:
      $cisco->login_auth(@ARGV);
      I get that sub new is returning an object into the main script but I don't get how I then pass it back to the module and use it then pass it back? Am I even understanding that right?
        Yes - you are asking the right questions - so you are close to "getting it".

        When you invoke

        $cisco->login_auth( $whatever );
        you are actually passing TWO parameters into login_auth - which you can retrieve using:
        sub login_auth{ my ($self, $whatever) = @_; # Now $self contains whatever sub new() put into it. # and $self can be updated to include more useful things. }
        Instead of passing @ARGV, you should pass it as a reference : \@ARGV.

        This will allow passing additional parameters if necessary.

        OK - next question ?...

                What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against?
                      -Larry Wall, 1992