File Coverage

blib/lib/SSH/RPC/Client.pm
Criterion Covered Total %
statement 15 40 37.5
branch 0 12 0.0
condition 0 3 0.0
subroutine 5 7 71.4
pod 2 2 100.0
total 22 64 34.3


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