File Coverage

blib/lib/SSH/RPC/Client.pm
Criterion Covered Total %
statement 18 43 41.8
branch 0 12 0.0
condition 0 3 0.0
subroutine 6 8 75.0
pod 2 2 100.0
total 26 68 38.2


line stmt bran cond sub pod time code
1             package SSH::RPC::Client;
2             $SSH::RPC::Client::VERSION = '1.204';
3 1     1   1661 use strict;
  1         2  
  1         31  
4 1     1   577 use Class::InsideOut qw(readonly private id register);
  1         6264  
  1         8  
5 1     1   106 use Scalar::Util qw(blessed);
  1         4  
  1         52  
6 1     1   464 use JSON;
  1         6188  
  1         6  
7 1     1   865 use Net::OpenSSH;
  1         29171  
  1         38  
8 1     1   402 use SSH::RPC::Result;
  1         2  
  1         322  
9              
10             =head1 NAME
11              
12             SSH::RPC::Client - The requestor, or client side, of an RPC call over SSH.
13              
14             =head1 VERSION
15              
16             version 1.204
17              
18             =head1 SYNOPSIS
19              
20             use SSH::RPC::Client;
21              
22             my $rpc = SSH::RPC::Client->new($host, $user);
23             my $result = $rpc->run($command, \%args); # returns a SSH::RPC::Result object
24              
25             if ($result->isSuccess) {
26             say $result->getResponse;
27             }
28             else {
29             die $result->getError;
30             }
31              
32             =head1 DESCRIPTION
33              
34             SSH::RPC::Client allows you to make a remote procedure call over SSH to an L on the other end. In this way you can execute methods remotely on other servers while also passing and receiving complex data structures. The arguments and return values are serialized into JSON allowing shells to be written in languages other than Perl.
35              
36             =head1 METHODS
37              
38             The following methods are available from this class.
39              
40             =cut
41              
42             #-------------------------------------------------------------------
43              
44             =head2 ssh
45              
46             Constructs and returns a reference to the L object.
47              
48             =cut
49              
50             readonly ssh => my %ssh;
51              
52             #-------------------------------------------------------------------
53              
54             =head2 new ( host, user, [ pass ])
55              
56             Constructor.
57              
58             =head3 host
59              
60             The hostname or ip address you want to connect to.
61              
62             =head3 user
63              
64             The username you want to connect as.
65              
66             =head3 pass
67              
68             The password to connect to this account. Can be omitted if you've set up an ssh key to automatically authenticate. See man ssh-keygen for details.
69              
70             =head2 new ( \%opts )
71              
72             Extended constructor with Net::OpenSSH as parameter.
73              
74             =head3 \%opts
75              
76             The hash needs a key with name "host", that is the hostname or ip address you want to connect to.
77             The remaining options in the hash will be used as optinal parameters for a new Net::OpenSSH object.
78              
79             =head2 new ( \$ssh )
80              
81             =head3 \$ssh
82              
83             Blessed reference holding an object that isa Net::OpenSSH, that will be reused for connection.
84              
85             =cut
86              
87             sub new {
88 0     0 1   my $class = shift;
89 0           my $self = register($class);
90 0 0 0       if (blessed($_[0]) and $_[0]->isa('Net::OpenSSH')) {
    0          
91 0           $ssh{id $self} = shift;
92             } elsif (ref($_[0]) eq 'HASH') {
93 0           my $opts = shift;
94 0 0         my $host = $opts->{host} or die "No host option specified";
95 0           delete $opts->{host};
96 0           $ssh{id $self} = Net::OpenSSH->new($host, %$opts);
97             } else {
98 0           my ($host, $user, $pass) = @_;
99 0           $ssh{id $self} = Net::OpenSSH->new($host,user=>$user, password=>$pass, timeout=>30, master_opts => [ '-T']);
100             }
101 0           return $self;
102             }
103              
104              
105             #-------------------------------------------------------------------
106              
107             =head2 run ( command, [ args ] )
108              
109             Execute a command on the remote shell. Returns a reference to an L object.
110              
111             =head3 command
112              
113             The method you wish to invoke.
114              
115             =head3 args
116              
117             If the method has any arguments pass them in here as a scalar, hash reference, or array reference.
118              
119             =cut
120              
121             sub run {
122 0     0 1   my ($self, $command, $args) = @_;
123 0           my $json = JSON->new->utf8->pretty->encode({
124             command => $command,
125             args => $args,
126             }) . "\n"; # all requests must end with a \n
127 0           my $ssh = $self->ssh;
128 0           my $response;
129 0 0         if ($ssh) {
130 0           my $out;
131 0 0         if ($out = $ssh->capture({stdin_data => $json, ssh_opts => ['-T']})) {
132 0           $response = eval{JSON->new->utf8->decode($out)};
  0            
133 0 0         if ($@) {
134 0           $response = {error=>"Response translation error. $@".$ssh->error, status=>510};
135             }
136             }
137             else {
138 0           $response = {error=>"Transmission error. ".$ssh->error, status=>406};
139             }
140             }
141             else {
142 0           $response = {error=>"Connection error. ".$ssh->error, status=>408};
143             }
144 0           return SSH::RPC::Result->new($response);
145             }
146              
147              
148             =head1 SEE ALSO
149              
150             L and L are also good ways of solving this same problem. I chose not to use either for these reasons:
151              
152             =over
153              
154             =item Arbitrary Execution
155              
156             They both allow arbitrary execution of Perl on the remote machine. While that's not all bad, in my circumstance that was a security risk that was unacceptable. Instead, SSH::RPC requires both a client and a shell be written, so you know exactly what's allowed to be executed.
157              
158             =item Language Neutral
159              
160             Because SSH::RPC uses JSON as a serialization layer between the connection, clients and shells can be written in languages other than Perl and still interoperate.
161              
162             =item Net::OpenSSH
163              
164             The Net::OpenSSH module that SSH::RPC is based upon is fast, flexible, and most importantly way easier to install than the modules required by GRID::Machine and IPC::PerlSSH.
165              
166             =back
167              
168             =head1 PREREQS
169              
170             This package requires the following modules:
171              
172             L
173             L
174             L
175              
176             =head1 CAVEATS
177              
178             You cannot use this module inside of mod_perl currently. Not sure why, but it hoses the SSH connection.
179              
180             =head1 AUTHOR
181              
182             JT Smith
183              
184             =head1 LEGAL
185              
186             -------------------------------------------------------------------
187             SSH::RPC::Client is Copyright 2008-2009 Plain Black Corporation
188             and is licensed under the same terms as Perl itself.
189             -------------------------------------------------------------------
190             http://www.plainblack.com info@plainblack.com
191             -------------------------------------------------------------------
192              
193             =cut
194              
195              
196             1;