116 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			116 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
package App::Netdisco::Transport::SSH;
 | 
						|
 | 
						|
use Dancer qw/:syntax :script/;
 | 
						|
 | 
						|
use App::Netdisco::Util::Device 'get_device';
 | 
						|
use Module::Load ();
 | 
						|
use Net::OpenSSH;
 | 
						|
use Try::Tiny;
 | 
						|
 | 
						|
use base 'Dancer::Object::Singleton';
 | 
						|
 | 
						|
=head1 NAME
 | 
						|
 | 
						|
App::Netdisco::Transport::SSH
 | 
						|
 | 
						|
=head1 DESCRIPTION
 | 
						|
 | 
						|
Returns an object which has an active SSH connection which can be used
 | 
						|
for some actions such as arpnip.
 | 
						|
 | 
						|
 my $cli = App::Netdisco::Transport::SSH->session_for( ... );
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
__PACKAGE__->attributes(qw/ sessions /);
 | 
						|
 | 
						|
sub init {
 | 
						|
  my ( $class, $self ) = @_;
 | 
						|
  $self->sessions( {} );
 | 
						|
  return $self;
 | 
						|
}
 | 
						|
 | 
						|
=head1 session_for( $ip )
 | 
						|
 | 
						|
Given an IP address, returns an object instance configured for and connected
 | 
						|
to that device.
 | 
						|
 | 
						|
Returns C<undef> if the connection fails.
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
{
 | 
						|
  package MySession;
 | 
						|
  use Moo;
 | 
						|
 | 
						|
  has 'ssh'  => ( is => 'rw' );
 | 
						|
  has 'auth' => ( is => 'rw' );
 | 
						|
  has 'host' => ( is => 'rw' );
 | 
						|
  has 'platform' => ( is => 'rw' );
 | 
						|
 | 
						|
  sub arpnip {
 | 
						|
    my $self = shift;
 | 
						|
    $self->platform->arpnip(@_, $self->host, $self->ssh, $self->auth);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
sub session_for {
 | 
						|
  my ($class, $ip) = @_;
 | 
						|
 | 
						|
  my $device = get_device($ip) or return undef;
 | 
						|
  my $sessions = $class->instance->sessions or return undef;
 | 
						|
 | 
						|
  return $sessions->{$device->ip} if exists $sessions->{$device->ip};
 | 
						|
  debug sprintf 'cli session cache warm: [%s]', $device->ip;
 | 
						|
 | 
						|
  my $auth = (setting('device_auth') || []);
 | 
						|
  if (1 != scalar @$auth) {
 | 
						|
    error sprintf " [%s] require only one matching auth stanza", $device->ip;
 | 
						|
    return undef;
 | 
						|
  }
 | 
						|
  $auth = $auth->[0];
 | 
						|
 | 
						|
  my @master_opts = qw(-o BatchMode=no);
 | 
						|
  push(@master_opts, @{$auth->{ssh_master_opts}})
 | 
						|
    if $auth->{ssh_master_opts};
 | 
						|
 | 
						|
  $Net::OpenSSH::debug = $ENV{SSH_TRACE};
 | 
						|
  my $ssh = Net::OpenSSH->new(
 | 
						|
    $device->ip,
 | 
						|
    user => $auth->{username},
 | 
						|
    password => $auth->{password},
 | 
						|
    timeout => 30,
 | 
						|
    async => 0,
 | 
						|
    default_stderr_file => '/dev/null',
 | 
						|
    master_opts => \@master_opts
 | 
						|
  );
 | 
						|
 | 
						|
  if ($ssh->error) {
 | 
						|
    error sprintf " [%s] ssh connection error [%s]", $device->ip, $ssh->error;
 | 
						|
    return undef;
 | 
						|
  }
 | 
						|
  elsif (! $ssh) {
 | 
						|
    error sprintf " [%s] Net::OpenSSH instantiation error", $device->ip;
 | 
						|
    return undef;
 | 
						|
  }
 | 
						|
 | 
						|
  my $platform = "App::Netdisco::SSHCollector::Platform::" . $auth->{platform};
 | 
						|
  my $happy = false;
 | 
						|
  try {
 | 
						|
    Module::Load::load $platform;
 | 
						|
    $happy = true;
 | 
						|
  } catch { error $_ };
 | 
						|
  return unless $happy;
 | 
						|
 | 
						|
  my $sess = MySession->new(
 | 
						|
    ssh  => $ssh,
 | 
						|
    auth => $auth,
 | 
						|
    host => $device->ip,
 | 
						|
    platform => $platform->new(),
 | 
						|
  );
 | 
						|
 | 
						|
  return ($sessions->{$device->ip} = $sess);
 | 
						|
}
 | 
						|
 | 
						|
true;
 |