#!/opt/perl5/bin/perl -w


# This script will capture the running configuration of a
# Cisco IOS OS SSH device and print it to STDOUT.
#
# Error Codes:
#   0   = Success
#   255 = Usage error
#   254 = Invalid timeout value
#   252 = Connection error
#   251 = Login error
#   249 = Enable error
#   244 = Error retrieving configuration
#   253 = Unexpected output
#

use strict;
use warnings;
use Net::SSH::Expect;

$ENV{'PATH'} = "/usr/bin:". $ENV{'PATH'};

### Main ###
if( $#ARGV != 4 && $#ARGV != 5 )
{
    print "Usage: capture_running_ssh.pl <device IP> <user> <pass> <enable_pass> <login_timeout_in_seconds> <capture_timeout_in_seconds>\n";
    print STDERR "Usage:  capture_running_ssh.pl <deviceIP> <user> <pass> <enable_pass> <login_timeout_in_seconds> <capture_timeout_in_seconds>\n";
    exit 255;
}
elsif( $ARGV[4] < 1 || $ARGV[4] > 600 )
{
    print "$ARGV[4] is the login timeout and must be an int between 1 and 600 seconds\n";
    print STDERR "$ARGV[4] is the login timeout and must be an int between 1 and 600 seconds\n";
    exit 254;
}
elsif( $#ARGV == 5 && ( $ARGV[5] < 1 || $ARGV[5] > 600 ) )
{
    print "$ARGV[5] is the capture timeout and must be an int between 1 and 600 seconds\n";
    print STDERR "$ARGV[5] is the capture timeout and must be an int between 1 and 600 seconds\n";
    exit 254;
}
else
{
    my $capture_timeout = $ARGV[4];
    if( $ARGV[5] )
    {
       $capture_timeout = $ARGV[5];
    }

    my $errorCode = 1;
    my @data;
    my $errorString = "\nHost $ARGV[0]:  \n";

    ($errorCode, @data) = GetConfig( $ARGV[0], $ARGV[1], $ARGV[2], $ARGV[3],
                                     $ARGV[4], $capture_timeout );

    if( $errorCode == 0 )
    {
        # Success.  The running configuration
        # content is in the data variable

        foreach ( @data ) { print "$_\n" }; # print the configuration to STDOUT
        exit 0;
    }
    else
    {
        print STDERR $errorString;

        if( $errorCode == 253 )
        {
            print STDERR join " ", @data, "\nEnable password may be invalid\n";
        }
        else
        {
            print STDERR join " ", @data, "\n";
        }

        exit $errorCode;
    }
}

exit 0;

sub GetConfig
{
    my $deviceIP=shift;
    my $user=shift;
    my $pass=shift;
    my $epass=shift;
    my $login_timeout=shift;
    my $capture_timeout=shift;
    my @config;
    my $msg;

    my $ssh = Net::SSH::Expect->new ( host => $deviceIP, 
                                      user => $user, 
                                      password=> $pass, 
                                      raw_pty => 1,
                                      no_terminal => 1,
                                      timeout => $login_timeout,
                                    );

    my $login_output;
    eval { $login_output = $ssh->login(); };
    if( $@ )
    {
        $msg = "Login has failed. Output: $login_output";
        return( 252, $msg );
    }
    
    #first try login without username and just password
    if( $login_output !~ /[\#\>]\s*\z/ )
    {
        $msg = "Login has failed. Didn't see device prompt as expected.";
        $ssh->close();
        return( 252, $msg );
    }

    $ssh->timeout( $capture_timeout );    

    if( $login_output !~ /\#\s*\z/ )
    {
        my $enable = $ssh->exec( "enable" );
        # if we have a password prompt after enable, send the password
        if( $enable =~ /[Pp]assword:/ )
        {
            my $enablepass = $ssh->exec( $epass );
            
            # did the enable password fail?
            if( $enablepass !~ /\#\s*\z/ )
            {
                $msg = "Enable password failed.";
                $ssh->close( );
                return( 249, $msg );
            }
        }
        # we didn't require a password, but did we get the enable prompt?
        elsif( $enable !~ /\#\s*\z/ )
        {
            $msg = "Enable mode prompt not found.";
            $ssh->close();
            return( 249, $msg );
        }
    }

    # disable paging
    # different commands for different devices, if they don't
    # work then we will get messages about problems later
    # specifically the "No prompt after 'sh run'" error
    # errmsg doesn't get set when these error and if we use print
    # and getlines to read for errors it causes problems with print "sh run"
    # later.
    $ssh->exec( "term pager 0" );
    $ssh->exec( "term length 0" );

    $ssh->send( "sh run" );
    $ssh->timeout( $capture_timeout );
    $ssh->peek(0);
    
    while( my $line = $ssh->read_line() )
    {
        # get configuration content

        if( $line !~
            /sh run|Building configuration|Current configuration|^\s*$/ )
        {
            push @config, $line;
        }
    }

    if( @config <= 0 )
    {
        $msg = "No data retrieved, the capture timeout may be too low.";
        $ssh->close();
        return( 244, $msg );
    }

    if( scalar grep { $_ =~ /^%/ } @config )
    {
        # Ensure show start actually returned the config and not an error message containing '%'
        return( 253, @config );
    }

    return( 0, @config ); # everything was okay, return the captured data
}

