File Coverage

blib/lib/Net/PDSH.pm
Criterion Covered Total %
statement 28 30 93.3
branch n/a
condition n/a
subroutine 10 10 100.0
pod n/a
total 38 40 95.0


line stmt bran cond sub pod time code
1             #
2             # PDSH.pm
3             #
4             # Interface for Parallel Distributed shell
5             #
6             package Net::PDSH;
7              
8 1     1   1025 use strict;
  1         2  
  1         42  
9 1         490 use vars qw($VERSION @ISA @EXPORT_OK $pdsh $equalspace $DEBUG @pdsh_options
10             $list_options $set_credentials $set_batch_mode $set_connect_timeout
11 1     1   7 $set_command_timeout $set_fanout $set_remote_command $list_modules);
  1         2  
12              
13 1     1   6 use Exporter;
  1         5  
  1         48  
14 1     1   992 use POSIX ":sys_wait_h";
  1         23122  
  1         6  
15 1     1   3783 use IO::File;
  1         15008  
  1         255  
16 1     1   1617 use IO::Select;
  1         2136  
  1         51  
17 1     1   13300 use IPC::Open2;
  1         6075  
  1         108  
18 1     1   15 use IPC::Open3;
  1         4  
  1         107  
19 1     1   1599 use Data::Dumper;
  1         38874  
  1         80  
20 1     1   1529 use String::Util ':all';
  0            
  0            
21              
22             @ISA = qw(Exporter);
23             @EXPORT_OK = qw(pdsh pdsh_cmd pdshopen2 pdshopen3 list_options pdsh_options
24             DEBUG set_batch_mode set_credentials set_connect_timeout
25             set_command_timeout set_fanout set_remote_commmand
26             list_moodules);
27             $VERSION = '0.01';
28              
29             $DEBUG = 1;
30              
31             $pdsh = "pdsh";
32             $list_options = "list_options";
33             $list_modules= "list_modules";
34             $set_remote_command = "set_remote_command";
35             $set_credentials = "set_credentials";
36             $set_batch_mode = "set_batch_mode";
37             $set_connect_timeout = "set_connect_timeout";
38             $set_command_timeout = "set_command_timeout";
39             $set_fanout = "set_fanout";
40             $set_remote_command = "set_remote_command";
41             $list_modules = "list_modules";
42              
43             sub new {
44             my($class,%args) = @_;
45             my $self = {};
46             bless($self,$class);
47             $self->{_DEBUG} = 0;
48             $self->{_pdsh_options} = 0;
49             $self->{_batch_mode} = 0;
50             $self->{_user} = "";
51             $self->{_connect_timeout} = 0;
52             $self->{_command_timeout} = 0;
53             $self->{_fanout} = 0;
54             $self->{_rcmd} = "";
55             return $self;
56             }
57              
58              
59             =head1 NAME
60              
61             Net::PDSH - Perl extension for parallel distributed shell
62              
63             =head1 SYNOPSIS
64              
65             use Net::PDSH;
66              
67             my $pdsh = Net::PDSH->new;
68              
69             $pdsh->pdsh("remotehost", "/bin/ls");
70              
71             cmd( [
72             {
73             user => 'user',
74             host => 'host.name,host.name,...',
75             command => 'command',
76             args => [ '-arg1', '-arg2' ],
77             stdin_string => "string\n",
78             },
79             {
80             user => 'user',
81             host => 'host.name,host.name,...',
82             command => 'command',
83             args => [ '-arg1', '-arg2' ],
84             stdin_string => "string\n",
85             }
86             ]
87             );
88              
89             =head1 DESCRIPTION
90              
91             Simple wrappers around pdsh commands.
92              
93             =over
94              
95             =cut
96              
97             sub pdsh {
98             my($self, $host, @command) = @_;
99             our(@pdsh_options);
100             if ($self->{_rcmd} ne "") {
101             push @pdsh_options, ("-R", $self->{_rcmd});
102             } else {
103             push @pdsh_options, ("-R", "exec") if $DEBUG == 1;
104             }
105             if ($self->{_user} ne "") {
106             push @pdsh_options, ("-l", $self->{_user});
107             }
108             if ($self->{_connect_timeout} != 0) {
109             push @pdsh_options, ("-t", $self->{_connect_timeout});
110             }
111             if ($self->{_command_timeout} != 0 ) {
112             push @pdsh_options, ("-u", $self->{_command_timeout});
113             }
114             my @cmd = ($pdsh, @pdsh_options, "-w $host", @command);
115             warn "[Net::PDSH::pdsh] executing ". join(' ', @cmd). "\n"
116             if $DEBUG;
117             @cmd = join(' ', @cmd);
118             system(@cmd);
119             }
120              
121             =item cmd
122              
123             Calls pdsh in batch mode. Throws a fatal error if data occurs on the command's
124             STDERR. Returns any data from the command's STDOUT.
125              
126             If using the hashref-style of passing arguments, possible keys are:
127              
128             host (requried)
129             command (required)
130             args (optional, arrayref)
131             stdin_string (optional) - written to the command's STDIN
132              
133             =cut
134             sub cmd {
135             my $self = shift;
136             my @command_list = @_;
137             my %pids;
138             our @pdsh_options;
139             my @pdsh_version = &_pdsh_options unless @pdsh_options;
140              
141             my $reader = IO::File->new();
142             my $writer = IO::File->new();
143             my $error = IO::File->new();
144             foreach (@command_list) {
145             my %cmd = %$_;
146             # print "Executing:" . $cmd{hostname}. " = =============". Dumper($cmd{command})."=========\n";
147             if ($self->{_rcmd} ne "") {
148             push @pdsh_options, ("-R", $self->{_rcmd});
149             } else {
150             push @pdsh_options, ("-R", "exec") if $DEBUG == 1;
151             }
152             if ($self->{_user} ne "") {
153             push @pdsh_options, ("-l", $self->{_user});
154             }
155             if ($self->{_connect_timeout} != 0) {
156             push @pdsh_options, ("-t", $self->{_connect_timeout});
157             }
158             if ($self->{_command_timeout} != 0) {
159             push @pdsh_options, ("-u", $self->{_command_timeout});
160             }
161             if ($DEBUG == 1) {
162             push @pdsh_options, ("-d") ;
163             }
164             my @cmd2 = $cmd{command};
165             my @arguments = $cmd{arguments};
166             my $pid = pdshopen3($cmd{hostname}, $writer, $reader, $error, @cmd2);
167             waitpid($pid, WNOHANG);
168             my $buffer;
169             while(<$reader>) {
170             my $buffer1 = $_;
171             my @buffer2 = split(" ", $buffer1);
172             $buffer = $buffer . "\n" .$buffer2[1];
173             }
174             $pids{$pid} = $buffer;
175            
176             }
177             return %pids;
178             }
179              
180             sub pdshopen2 {
181             my($host, $reader, $writer, @command) = @_;
182             @pdsh_options = &_pdsh_options unless @pdsh_options;
183             open2($reader, $writer, $pdsh, @pdsh_options, $host, @command);
184             }
185              
186             sub pdshopen3 {
187             my($host, $writer, $reader, $error, $command) = @_;
188             @pdsh_options = &_pdsh_options unless @pdsh_options;
189             my @cmd1 = @pdsh_options;
190             my $end = $#$command;
191             my $i = 0;
192             while($i <= $end) {
193             push @cmd1, @$command[$i];
194             $i = $i + 1;
195             }
196            
197             open3($writer, $reader, $error, $pdsh, @pdsh_options, "-w $host", @cmd1);
198             }
199              
200             sub _yesno {
201             print "Proceed [y/N]:";
202             my $x = scalar();
203             $x =~ /^y/i;
204             }
205              
206             sub _pdsh_options {
207             my $reader = IO::File->new();
208             my $writer = IO::File->new();
209             my $error = IO::File->new();
210             open3($writer, $reader, $error, $pdsh, '-V');
211             my $pdsh_version = <$reader>;
212             chomp($pdsh_version);
213             $pdsh_version = split(" ", $pdsh_version);
214             $equalspace = " ";
215             my @options = "";
216             # ( '-V', $pdsh_version );
217             @pdsh_options;
218             }
219              
220              
221             =item set_batch_mode
222              
223             Executes pdsh in batchmode
224              
225             Input: 0/1
226              
227             =cut
228              
229             sub set_batch_mode {
230             my ($self, $batch_mode) = @_;
231             $self->{_batch_mode} = $batch_mode;
232             return $self->{_batch_mode};
233             }
234              
235             =item set_credentials
236              
237             Executes pdsh under a given user. All the further commands would be executed as that user.
238              
239             Input: username
240              
241             =cut
242              
243             sub set_credentials {
244             my ($self, $user) = @_;
245             $self->{_user} = $user;
246             return $self->{_user};
247             }
248              
249             sub set_connect_timeout {
250             my ($self, $val) = @_;
251             $self->{_connect_timeout} = $val;
252             return $self->{_connect_timeout};
253             }
254             sub set_command_timeout {
255             my ($self, $val) = @_;
256             $self->{_command_timeout} = $val;
257             return $self->{_command_timeout};
258             }
259             sub set_fanout {
260             my ($self, $val) = @_;
261             $self->{_fanout} = $val;
262             return $self->{_fanout};
263             }
264             sub set_remote_command {
265             my ($self, $val) = @_;
266             $self->{_rcmd} = $val;
267             return $self->{_rcmd};
268             }
269              
270             sub list_modules {
271             my($self, $host) = @_;
272             my @pdsh_version = &_pdsh_options unless @pdsh_options;
273             use vars qw(@pdsh_options);
274             my @cmd = ($pdsh, @pdsh_options, "-w $host -L");
275             @cmd = join(' ', @cmd);
276             my $options = `@cmd`;
277             my @options = split '\n',$options;
278             my %options;
279             nextoption: foreach (@options) {
280             my @val2 = split(':', $_);
281             my $key = @val2[0];
282             my $val = @val2[1];
283             next nextoption if((trim($key) eq '') || (trim($key) =~ /^--/)) ;
284             $options{trim($key)} = trim($val);
285             }
286             return %options;
287             }
288              
289             sub list_options {
290             my($self, $host) = @_;
291             my @pdsh_version = &_pdsh_options unless @pdsh_options;
292             use vars qw(@pdsh_options);
293             my @cmd = ($pdsh, @pdsh_options, "-w $host -q");
294             @cmd = join(' ', @cmd);
295             my $options = `@cmd`;
296             my @options = split '\n',$options;
297             my %options;
298             nextoption: foreach (@options) {
299             my $val2 = join(' ', split('\t', $_));
300             my $key = substr $val2, 0, 24;
301             my $val = substr $val2, 24, 100;
302             next nextoption if((trim($key) eq '') || (trim($key) =~ /^--/)) ;
303             $options{trim($key)} = trim($val);
304             }
305             return %options;
306             }
307              
308              
309             =back
310              
311             =head1 EXAMPLE
312              
313             my @cmd_arr = (
314             { "hostname" => "remotehost1,remotehost2",
315             "command" => ["/bin/cat", "/tmp/fsck.log", "/etc/hosts", ],
316             },
317             { "hostname" => "remotehost3",
318             "command" => ["/bin/cat", "/etc/sysconfig/network",],
319             },
320             );
321             my %pids = $pdsh->cmd(@cmd_arr);
322              
323             This would execute "cat /tmp/fsck.log /etc/hosts" on remotehost1 and remotehost2
324             and it would execute "cat /etc/sysconfig/network" on remotehost3
325              
326             It would return the output in %pids hash table where keys are pids and values are
327             output contents
328              
329             =head1 FREQUENTLY ASKED QUESTIONS
330              
331             Q: How do you supply a password to connect with pdsh within a perl script
332             using the Net::PDSH module?
333              
334             A: You don't (at least not with this module). Use RSA or DSA keys. See the
335             quick help in the next section and the ssh-keygen(1) manpage.
336              
337             A #2: See L instead.
338              
339             =head1 GENERATING AND USING SSH KEYS
340              
341             =over 4
342              
343             =item 1 Generate keys
344              
345             Type:
346              
347             ssh-keygen -t rsa
348              
349             And do not enter a passphrase unless you wanted to be prompted for
350             one during file copying.
351              
352             Here is what you will see:
353              
354             $ ssh-keygen -t rsa
355             Generating public/private rsa key pair.
356             Enter file in which to save the key (/home/User/.pdsh/id_rsa):
357             Enter passphrase (empty for no passphrase):
358             Enter same passphrase again:
359              
360             Your identification will be saved in /home/User/.ssh/id_rsa
361             Your public key will be saved in /home/User/.ssh/id_rsa.pub
362              
363             =item 2 Copy public to machines you want to upload to
364              
365             C is your public key. Copy it to C<~/.ssh> on target machine.
366              
367             Put a copy of the public key file on each machine you want to log into.
368             Name the copy C (some implementations name this file
369             C)
370              
371             Then type:
372              
373             chmod 600 authorized_keys
374              
375             Then make sure your home dir on the remote machine is not group or
376             world writeable.
377              
378             =back
379              
380             =head1 AUTHORS
381              
382             Aditya Pandit
383              
384             =head1 COPYRIGHT
385              
386             This program is free software; you can redistribute it and/or modify it under
387             the same terms as Perl itself.
388              
389              
390             =head1 SEE ALSO
391              
392             L, L, L, L, pdsh(1)
393              
394             =cut
395              
396             1;
397