File Coverage

blib/lib/SSH/RPC/Client.pm
Criterion Covered Total %
statement 15 33 45.4
branch 0 6 0.0
condition n/a
subroutine 5 7 71.4
pod 2 2 100.0
total 22 48 45.8


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