for my $cmd (@{$ctxt->{commands}}) { ... #### use Ops::Exec qw(ops_do_ssh_shell ops_new_context ops_new_cmd); use Ops::Config qw(ops_get_servers); my %servers = map { $_ => new_server($_) } ops_get_servers('foo'); my @contexts = map {ops_new_context({ name => $servers{$_}{name}, user => 'foo', host => $servers{$_}{host}, key => $ENV{HOME} . '/.ssh/id_rsa.foo.prod', commands => [ ops_new_cmd({ name => 'capacity', command => 'df -k /home'}), ops_new_cmd({ name => 'process_type1', command => 'ps -ef | grep process_type1 | grep -v grep | wc -l'}), ops_new_cmd({ name => 'process_type2', command => 'ps -ef | grep process_type2 | grep -v grep | wc -l'}) ] })} sort keys %servers; for my $ctxt (@contexts) { if ($local_state->{status} eq 'success') { my $ctxt_status = ops_do_ssh_shell($ctxt); for my $cmd_status (@{$ctxt_status->{commands}}) { if ( $ctxt_status->{success} ) { $servers{$ctxt_status->{name}}{$cmd_status->{name}} = $cmd_status->{output}; } else { $local_state->{status} = 'failure'; $local_state->{stack_trace} = $cmd_status; } } } } if ($local_state->{status} eq 'success') { for my $server (keys %servers) { $servers{$server}{capacity} = parse_capacity($servers{$server}{capacity}); # further parsing and validation } } # statuses are evaluated to generate warnings and alerts # texts and emails are sent yadda yadda #### package Ops::Exec; use strict; use warnings; use Exporter qw(import); use Data::Dumper; use Net::OpenSSH; use Hash::Util qw(lock_ref_keys); use Clone qw(clone); our @EXPORT_OK = qw(ops_do_ssh_shell ops_do_ssh_qx ops_new_context ops_new_cmd ops_new_qx_cmd); use constant SUCCESS => 1; use constant FAILURE => 0; sub ops_new_cmd { my ($init_hash) = @_; my $new_cmd = {}; lock_ref_keys($new_cmd, qw(name command success output std_err cmd_ret_code cmd_ret_msg)); $new_cmd = clone($init_hash); $new_cmd->{success} = SUCCESS; return $new_cmd } sub ops_new_context { my ($init_hash) = @_; my $new_cmd = {}; lock_ref_keys($new_cmd, qw(name user host key commands success ssh_retcode ssh_retmsg outputs)); $new_cmd = clone($init_hash); $new_cmd->{success} = SUCCESS; return $new_cmd } sub ops_do_ssh_shell { my ($ctxt) = @_; my $ssh; defined $ctxt->{key} or $ctxt->{key} = $ENV{HOME} . '/.ssh/id_rsa'; if ( $ctxt->{success} ) { $ssh = Net::OpenSSH->new($ctxt->{host}, user => $ctxt->{user}, key_path => $ctxt->{key}); if ( $ssh->error ) { $ctxt->{success} = FAILURE; ($ctxt->{ssh_retcode}, $ctxt->{ssh_retmsg}) = (0 + $ssh->error, '' . $ssh->error); } } for my $cmd (@{$ctxt->{commands}}) { if ( $ctxt->{success} ) { ($cmd->{output}, $cmd->{std_err}) = $ssh->capture2($cmd->{command}); $cmd->{cmd_ret_code} = 0 + $ssh->error; if ( $cmd->{cmd_ret_code} ) { $ctxt->{success} = FAILURE; $ctxt->{cmd_ret_msg} = '' . $ssh->error; } chomp $cmd->{output}; chomp $cmd->{std_err}; } } return $ctxt; } sub ops_new_qx_cmd { my ($init_hash) = @_; my $new_cmd = {}; lock_ref_keys($new_cmd, qw(name user host command success output std_err cmd_ret_code cmd_ret_msg ssh_ret_code ssh_ret_msg ssh_cmd_qx)); %$new_cmd = %$init_hash; $new_cmd->{success} = SUCCESS; return $new_cmd } sub ops_do_ssh_qx { my ($cmd) = @_; $cmd->{ssh_cmd_qx} = 'ssh ' . $cmd->{user} . '\@' . $cmd->{host} . ' \'' . $cmd->{command} . '\'' . ' 2>/dev/null'; $cmd->{output} = qx($cmd->{ssh_cmd_qx}); if ( defined $cmd->{output} ) { $cmd->{cmd_ret_code} = $?; chomp $cmd->{output}; } else { ($cmd->{ssh_ret_code}, $cmd->{ssh_ret_msg}) = (0 + $!, '' . $!); } return $cmd; } "Red Guitar, Three Chords ...";