App-Foca

 view release on metacpan or  search on metacpan

lib/App/Foca/Client.pm  view on Meta::CPAN


=item B<ok> Boolean. True if command went well or false otherwise.

=item B<output> output of the command

=item B<hostname> Hostname.

=back

In addition the method offers a third parameter, C<%options> that can be used to
tie the collection of data of each host and send it to a subroutine. Options are:

=over 4

=item B<on_good> A CODE reference. Called on every host that succeeded.

=item B<on_bad> A CODE reference. Called on every host that failed

=item B<on_host> A CODE reference. Called on every host, succeeded or not.

=back

Each one of the CODE references will get one argument: the hash described before.

Command should be the full command, just as if you were executing it from your
shell (for example: I<uptime> or I<uptime -V>). This method will take care of
getting the basename of the command you are calling and look for any extra
parameters/arguments, if any parameters/arguments are found then they get
sent as a HTTP header, header would be I<Foca-Cmd-Params>.

=cut
sub run {
    my ($self, $hosts, $command, $options) = @_;

    # Some basic verification
    log_die("No hosts were given") unless $hosts;
    log_die("Hosts are not an array ref") unless (ref $hosts eq 'ARRAY');
    log_die("No command was given") unless $command;
    
    # Ok, get the command args and params
    my ($foca_cmd, $foca_args) = ($command, '');
    if ($command =~ /(.+?)\s+(.+?)$/) {
        ($foca_cmd, $foca_args) = ($1, $2);
    }

    $options = {} unless $options;
    $options->{'on_good'} = '' unless $options->{'on_good'};
    $options->{'on_bad'}  = '' unless $options->{'on_bad'};
    $options->{'on_host'} = '' unless $options->{'on_host'};

    my @results = ();
    my $pm = new Parallel::ForkManager($self->{'maxflight'});
    $pm->run_on_finish(
            sub {
                my ($pid, $exit_code, $id, $exit, $core, $data) = @_;
                
                my $item;
                if ($data->{'got_response'}) {
                    my $response = $data->{'response'};
                    if ($response->is_success) {
                        my $data = $response->decoded_content;
                        chomp($data);
                        $item = {
                            'hostname'  => $id,
                            'ok'        => 1,
                            'output'    => $data};
                        $options->{'on_good'}->($item) if
                            ref $options->{'on_good'} eq 'CODE';
                    } else {
                        my $msg = $response->decoded_content || $response->status_line;
                        chomp($msg);
                        if ($msg eq "500 Can't connect to $id:12346 (connect: timeout)") {
                            $msg = "Connect timeout";
                        }
                        $item = {
                            'hostname'  => $id,
                            'ok'        => 0,
                            'output'    => $msg};
                        $options->{'on_bad'}->($item) if
                            ref $options->{'on_bad'} eq 'CODE';
                    }
                } else {
                    $item = {
                        'hostname'      => $id,
                        'ok'            => 0,
                        'output'        => $data->{'reason'}};
                }
                push(@results, $item);
                $options->{'on_host'}->($item) if
                    ref $options->{'on_host'} eq 'CODE';
            });

    foreach my $host (@{$hosts}) {
        $pm->start($host) and next;

        my $url = 'http://' . $host . ':' . $self->{'port'} . '/foca/' . $foca_cmd;

        my ($response_body, $response_headers) = ('', '');

        open(my $response_body_fh, ">", \$response_body);
        open(my $response_headers_fh, ">", \$response_headers);

        my @headers = ();
        push(@headers, 'Foca-Cmd-Params:' . $foca_args) if $foca_args;
        
        my $curl = new WWW::Curl::Easy;
        $curl->setopt(CURLOPT_VERBOSE, $self->{'debug'});
        $curl->setopt(CURLOPT_HEADER, 0);
        $curl->setopt(CURLOPT_URL, $url);
        $curl->setopt(CURLOPT_WRITEDATA, $response_body_fh);
        $curl->setopt(CURLOPT_WRITEHEADER, $response_headers_fh);
        $curl->setopt(CURLOPT_TIMEOUT, $self->{'timeout'});
        $curl->setopt(CURLOPT_CONNECTTIMEOUT, $self->{'connect_timeout'});
        $curl->setopt(CURLOPT_HTTPHEADER, \@headers);

        log_debug("$host - Requesting $url");

        my $retcode = $curl->perform;
        my $data    = {};
        if ($retcode == 0) {
             my $full_response = $response_headers;
             $full_response .= $response_body if $response_body;
             my $response = HTTP::Response->parse($full_response);
             $pm->finish(1, {
                     'got_response' => 1,
                     'response'     => $response});
        } else {
            $pm->finish(1, {
                    'got_response'  => 0,
                    'reason'        => $curl->strerror($retcode)});



( run in 0.454 second using v1.01-cache-2.11-cpan-d06a3f9ecfd )