File Coverage

blib/lib/POE/Component/OpenSSH.pm
Criterion Covered Total %
statement 15 36 41.6
branch 0 8 0.0
condition n/a
subroutine 5 14 35.7
pod 8 8 100.0
total 28 66 42.4


line stmt bran cond sub pod time code
1 1     1   22743 use strict;
  1         2  
  1         73  
2 1     1   5 use warnings;
  1         3  
  1         55  
3             package POE::Component::OpenSSH;
4             {
5             $POE::Component::OpenSSH::VERSION = '0.10';
6             }
7             # ABSTRACT: Nonblocking SSH Component for POE using Net::OpenSSH
8              
9 1     1   5 use Carp 'croak';
  1         3  
  1         71  
10 1     1   1525 use Net::OpenSSH;
  1         43227  
  1         52  
11 1     1   1196 use POE::Component::Generic;
  1         244257  
  1         506  
12              
13             sub _build_object {
14 0     0     my $self = shift;
15 0           my $object = POE::Component::Generic->spawn(
16             alias => $self->{'alias'},
17             package => 'Net::OpenSSH',
18             object_options => $self->{'args'},
19             debug => $self->{'debug'},
20             verbose => $self->{'verbose'},
21             error => $self->{'error'},
22             );
23              
24 0           $self->{'_object'} = $object;
25              
26 0           return 0;
27             }
28              
29 0     0 1   sub object { shift->{'_object'} }
30 0     0 1   sub capture { shift->{'_object'}->capture(@_) }
31 0     0 1   sub capture2 { shift->{'_object'}->capture2(@_) }
32 0     0 1   sub system { shift->{'_object'}->system(@_) }
33 0     0 1   sub scp_get { shift->{'_object'}->scp_get(@_) }
34 0     0 1   sub scp_put { shift->{'_object'}->scp_put(@_) }
35 0     0 1   sub sftp { shift->{'_object'}->sftp(@_) }
36              
37             sub new {
38 0     0 1   my $class = shift;
39              
40 0 0         if ( @_ % 2 != 0 ) {
41 0           croak 'Arguments must be in the form of key/value';
42             }
43              
44 0           my %opts = (
45             args => [],
46             options => {},
47             error => {},
48             alias => '',
49             debug => 0,
50             verbose => 0,
51             @_,
52             );
53              
54 0 0         ref $opts{'args'} eq 'ARRAY'or croak '"args" must be an arryref';
55 0 0         ref $opts{'options'} eq 'HASH' or croak '"options" must be a hashref';
56 0 0         ref $opts{'error'} eq 'HASH' or croak '"error" must be a hashref';
57              
58 0           my $self = bless { %opts }, $class;
59              
60 0           $self->_build_object;
61              
62 0           return $self;
63             }
64              
65             1;
66              
67              
68              
69             =pod
70              
71             =head1 NAME
72              
73             POE::Component::OpenSSH - Nonblocking SSH Component for POE using Net::OpenSSH
74              
75             =head1 VERSION
76              
77             version 0.10
78              
79             =head1 SYNOPSIS
80              
81             Need nonblocking SSH? You like Net::OpenSSH? Try out this stuff right here.
82              
83             use POE::Component::OpenSSH;
84              
85             my $ssh = POE::Component::OpenSSH->new( args => [ $host, user => $user ] );
86             $ssh->system( { event => 'read_system_output' }, 'w' );
87              
88             Perhaps you want it with debugging and verbose of POE::Component::Generic
89              
90             my $ssh = POE::Component::OpenSSH->new(
91             args => [ 'root@host', passwd => $pass ],
92             verbose => 1, # turns on POE::Component::Generic verbose
93             debug => 1, # turns on POE::Component::Generic debug
94             );
95              
96             What about setting timeout for Net::OpenSSH?
97              
98             my $ssh = POE::Component::OpenSSH->new(
99             args => [ 'root@host', passwd => $pass, timeout => 10 ],
100             );
101              
102             =head1 DESCRIPTION
103              
104             This module allows you to use SSH (via L<Net::OpenSSH>) in a nonblocking manner.
105              
106             The only differences is that in the I<new()> method, you need to indicate
107             OpenSSH args in I<args>, and the first arg to a method should be a hashref that
108             includes an I<event> to reach with the result.
109              
110             I kept having to write this small thing each time I needed nonblocking SSH in a
111             project. I got tired of it so I wrote this instead.
112              
113             You might ask 'why put the args in an "args" attribute instead of straight away
114             attributes?' Because Net::OpenSSH has a lot of options and they may collide
115             with POE::Component::Generic's options and I don't feel like maintaining the
116             mess. It's on Github so you can patch it up if you want (I accept patches...
117             and foodstamps).
118              
119             Here is a more elaborate example using L<MooseX::POE>:
120              
121             (If you know L<POE::Session>, you can use that too)
122              
123             package Runner;
124             use MooseX::POE;
125              
126             has 'host' => ( is => 'ro', isa => 'Str', default => 'localhost' );
127             has 'user' => ( is => 'ro', isa => 'Str', default => 'root' );
128             has 'pass' => ( is => 'ro', isa => 'Str', default => 'pass' );
129             has 'cmd' => ( is => 'ro', isa => 'Str', default => 'w' );
130              
131             sub START {
132             my $self = $_[OBJECT];
133             my $ssh = POE::Component::OpenSSH->new(
134             args => [
135             $self->host,
136             user => $self->user,
137             passwd => $self->passwd,
138             ],
139             );
140              
141             $ssh->capture( { event => 'parse_cmd' }, $cmd );
142             }
143              
144             event 'parse_cmd' => sub {
145             my ( $self, $output ) @_[ OBJECT, ARG1 ];
146             my $host = $self->host;
147             print "[$host]: $output";
148             };
149              
150             package main;
151              
152             use POE::Kernel;
153              
154             my @machines = ( qw( server1 server2 server3 ) );
155              
156             foreach my $machine (@machines) {
157             Runner->new(
158             host => $machine,
159             pass => 'my_super_pass',
160             cmd => 'uname -a',
161             );
162             }
163              
164             POE::Kernel->run();
165              
166             =head1 METHODS
167              
168             =head2 new
169              
170             Creates a new POE::Component::OpenSSH object. If you want to access the
171             Net::OpenSSH check I<object> below.
172              
173             This module (still?) doesn't have a I<spawn> method, so you're still required
174             to put it in a L<POE::Session>. The examples use L<MooseX::POE> which does the
175             same thing.
176              
177             =over 4
178              
179             =item args
180              
181             The arguments that will go to L<Net::OpenSSH>.
182              
183             =item options
184              
185             The options that will go to L<POE::Component::Generic>'s I<options> argument,
186             stuff like C< { trace => 1 } >.
187              
188             =item error
189              
190             Event when L<POE::Component::Generic> has an error. Either a hashref with
191             I<session> and I<event> or a string with the event in the current session.
192              
193             =item alias
194              
195             A session alias to register with the kernel. Default is none.
196              
197             =item debug
198              
199             Shows component debugging information.
200              
201             =item verbose
202              
203             Some stuff about what is happening to L<Net::OpenSSH>. Very useful for
204             debugging the L<Net::OpenSSH> object.
205              
206             =back
207              
208             =head2 object
209              
210             This method access the actual Net::OpenSSH object. It is wrapped with
211             L<POE::Component::Generic>, so the first argument is actually a hashref that
212             POE::Component::Generic requires. Specifically, noting which event will handle
213             the return of the Net::OpenSSH method.
214              
215             You can reach B<every> method is L<Net::OpenSSH> this way. However, some
216             methods are already delegated to make your life easier. If what you need isn't
217             delegated, you can reach it directing using the object.
218              
219             For example, these two methods are equivalent:
220              
221             $ssh->object->capture( { event => 'handle_capture' }, 'echo yo yo' );
222              
223             $ssh->capture( { event => 'handle_capture' }, 'echo yo yo' );
224              
225             # shell_quote isn't delegated
226             $ssh->object->shell_quote(@args);
227              
228             =head2 args
229              
230             These are the arguments that will go to L<Net::OpenSSH> creation. This is an
231             arrayref.
232              
233             For example:
234              
235             # using user@host
236             my $ssh = POE::Component::OpenSSH->new( args => [ 'root@remote_host' ] );
237              
238             # using separate arguments
239             my $ssh = POE::Component::OpenSSH->new( args => [ 'remote_host, user => 'root' ] );
240              
241             # same thing, just with pass, and writing it nicer
242             my $ssh = POE::Component::OpenSSH->new(
243             args => [
244             'remote_host',
245             user => 'root',
246             passwd => $pass,
247             ],
248             );
249              
250             =head2 capture
251              
252             This is a delegated method to L<Net::OpenSSH>'s capture.
253              
254             =head2 capture2
255              
256             This is a delegated method to L<Net::OpenSSH>'s I<capture2>.
257              
258             =head2 system
259              
260             This is a delegated method to L<Net::OpenSSH>'s I<system>.
261              
262             =head2 scp_get
263              
264             This is a delegated method to L<Net::OpenSSH>'s I<scp_get>.
265              
266             =head2 scp_put
267              
268             This is a delegated method to L<Net::OpenSSH>'s I<scp_put>.
269              
270             =head2 sftp
271              
272             This is a delegated method to L<Net::OpenSSH>'s I<sftp>.
273              
274             =head1 AUTHOR
275              
276             Sawyer X, C<< <xsawyerx at cpan.org> >>
277              
278             =head1 BUGS
279              
280             There is one known issue I've personally stumbled across which I've yet to
281             figure out and resolve. Using L<MooseX::POE>, running C<capture>s from the
282             C<START> event works, but running from another event doesn't. The connection
283             fails and hangs. In order to fix it, I use a clearance on the attribute before
284             running the second C<capture>, so now it works, but I've yet to understand why
285             that happens.
286              
287             The Github's issue tracker is available at
288             L<http://github.com/xsawyerx/poe-component-openssh/issues>.
289              
290             =head1 SUPPORT
291              
292             You can find documentation for this module with the perldoc command.
293              
294             perldoc POE::Component::OpenSSH
295              
296             You can also look for information at:
297              
298             =over 4
299              
300             =item * Github issue tracker
301              
302             L<http://github.com/xsawyerx/poe-component-openssh/issues>
303              
304             =item * Github page
305              
306             L<http://github.com/xsawyerx/poe-component-openssh/tree/master>
307              
308             =back
309              
310             =head1 SEE ALSO
311              
312             If you have no idea what I'm doing (but you generally know what POE is), check
313             these stuff:
314              
315             L<POE::Component::Generic>
316              
317             L<Net::OpenSSH>
318              
319             If you don't know POE at all, check L<POE>.
320              
321             =head1 DEPENDENCIES
322              
323             L<Net::OpenSSH>
324              
325             L<POE>
326              
327             L<POE::Component::Generic>
328              
329             =head1 ACKNOWLEDGEMENTS
330              
331             All the people involved in the aforementioned projects and the Perl community.
332              
333             =head1 AUTHOR
334              
335             Sawyer X <xsawyerx@cpan.org>
336              
337             =head1 COPYRIGHT AND LICENSE
338              
339             This software is copyright (c) 2011 by Sawyer X.
340              
341             This is free software; you can redistribute it and/or modify it under
342             the same terms as the Perl 5 programming language system itself.
343              
344             =cut
345              
346              
347             __END__
348