Rex

 view release on metacpan or  search on metacpan

lib/Rex/Task.pm  view on Meta::CPAN


=head1 SYNOPSIS

 use Rex::Task;

 # create a new task
 my $task = Rex::Task->new( name => 'testtask' );
 $task->set_server('remoteserver');
 $task->set_code( sub { say 'Hello'; } );
 $task->modify( 'no_ssh', 1 );

 # retrieve an existing task
 use Rex::TaskList;

 my $existing_task = Rex::TaskList->create->get_task('my_task');

=head1 METHODS

=cut

package Rex::Task;

use v5.14.4;
use warnings;
use Data::Dumper;
use Time::HiRes qw(time);

our $VERSION = '1.16.1'; # VERSION

use Rex::Logger;
use Rex::TaskList;
use Rex::Interface::Connection;
use Rex::Interface::Executor;
use Rex::Group::Entry::Server;
use Rex::Profiler;
use Rex::Hardware;
use Rex::Interface::Cache;
use Rex::Report;
use Rex::Helper::Run;
use Rex::Helper::Path;
use Rex::Notify;
use Carp;

require Rex::Commands;

require Rex::Args;

=head2 new

This is the constructor.

 $task = Rex::Task->new(
   func => sub { some_code_here },
   server => [ @server ],
   desc => $description,
   no_ssh => $no_ssh,
   hidden => $hidden,
   auth => {
     user      => $user,
     password   => $password,
     private_key => $private_key,
     public_key  => $public_key,
   },
   before => [sub {}, sub {}, ...],
   after  => [sub {}, sub {}, ...],
   around => [sub {}, sub {}, ...],
   before_task_start => [sub {}, sub {}, ...],
   after_task_finished => [sub {}, sub {}, ...],
   name => $task_name,
   executor => Rex::Interface::Executor->create,
   opts => {key1 => val1, key2 => val2, ...},
   args => [arg1, arg2, ...],
 );

=cut

sub new {
  my $that  = shift;
  my $proto = ref($that) || $that;
  my $self  = {@_};

  bless( $self, $proto );

  if ( !exists $self->{name} ) {
    die("You have to define a task name.");
  }

  $self->{no_ssh}   ||= 0;
  $self->{func}     ||= sub { };
  $self->{executor} ||= Rex::Interface::Executor->create;
  $self->{opts}     ||= {};
  $self->{args}     ||= [];

  $self->{connection} = undef;

  # set to true as default
  if ( !exists $self->{exit_on_connect_fail} ) {
    $self->{exit_on_connect_fail} = 1;
  }

  return $self;
}

=head2 connection

Returns the current connection object.

=cut

sub connection {
  my ($self) = @_;
  if ( !exists $self->{connection} || !$self->{connection} ) {
    $self->{connection} =
      Rex::Interface::Connection->create( $self->get_connection_type );
  }

  $self->{connection};
}

sub set_connection {
  my ( $self, $conn ) = @_;
  $self->{connection} = $conn;
}

=head2 executor

Returns the current executor object.

=cut

sub executor {
  my ($self) = @_;
  $self->{executor}->set_task($self);
  return $self->{executor};
}

=head2 hidden

Returns true if the task is hidden. (Should not be displayed on ,,rex -T''.)

=cut

sub hidden {
  my ($self) = @_;
  return $self->{hidden};
}

=head2 server

Returns the servers on which the task should be executed as an ArrayRef.

=cut

sub server {
  my ($self) = @_;

  my @server = @{ $self->{server} };
  my @ret    = ();

  if ( ref( $server[-1] ) eq "HASH" ) {
    Rex::deprecated(
      undef, "0.40",
      "Defining extra credentials within the task creation is deprecated.",
      "Please use set auth => task => 'taskname' instead."
    );

    # use extra defined credentials
    my $data = pop(@server);
    $self->set_auth( "user",     $data->{'user'} );
    $self->set_auth( "password", $data->{'password'} );

    if ( exists $data->{"private_key"} ) {
      $self->set_auth( "private_key", $data->{"private_key"} );
      $self->set_auth( "public_key",  $data->{"public_key"} );
    }
  }

  if ( ref( $self->{server} ) eq "ARRAY"
    && scalar( @{ $self->{server} } ) > 0 )
  {
    for my $srv ( @{ $self->{server} } ) {
      if ( ref($srv) eq "CODE" ) {
        push( @ret, &$srv() );
      }
      else {
        if ( ref $srv && $srv->isa("Rex::Group::Entry::Server") ) {
          push( @ret, $srv->get_servers );
        }
        else {
          push( @ret, $srv );
        }
      }
    }
  }
  elsif ( ref( $self->{server} ) eq "CODE" ) {
    push( @ret, &{ $self->{server} }() );
  }
  else {
    push( @ret, Rex::Group::Entry::Server->new( name => "<local>" ) );
  }

  return [@ret];
}

=head2 set_server(@server)

With this method you can set new servers on which the task should be executed on.

=cut

sub set_server {
  my ( $self, @server ) = @_;
  $self->{server} = \@server;
}

=head2 delete_server

Delete every server registered to the task.

=cut

sub delete_server {
  my ($self) = @_;
  delete $self->{current_server};
  delete $self->{server};
  $self->rethink_connection;
}

=head2 current_server

Returns the current server on which the tasks gets executed right now.

=cut

lib/Rex/Task.pm  view on Meta::CPAN

sub parallelism {
  my ($self) = @_;
  return $self->{parallelism};
}

=head2 set_parallelism($count)

Set the parallelism of the task.

=cut

sub set_parallelism {
  my ( $self, $para ) = @_;
  $self->{parallelism} = $para;
}

=head2 connect($server)

Initiate the connection to $server.

=cut

sub connect {
  my ( $self, $server, %override ) = @_;

  if ( !ref $server ) {
    $server = Rex::Group::Entry::Server->new( name => $server );
  }
  $self->{current_server} = $server;

  $self->run_hook( \$server, "before" );

  # need to be called, in case of a run_task task call.
  # see #788
  $self->rethink_connection;

  my $user = $self->user;

  #print Dumper($self);
  my $auth = $self->merge_auth($server);

  if ( exists $override{auth} ) {
    $auth = $override{auth};
    $user = $auth->{user};
  }

  my $rex_int_conf = Rex::Commands::get("rex_internals");
  Rex::Logger::debug( Dumper($rex_int_conf) );
  Rex::Logger::debug("Auth-Information inside Task:");
  for my $key ( keys %{$auth} ) {
    my $data = $auth->{$key};
    $data = Rex::Logger::masq( "%s", $data ) if $key eq 'password';
    $data = Rex::Logger::masq( "%s", $data ) if $key eq 'sudo_password';
    $data ||= "";

    Rex::Logger::debug("$key => [[$data]]");
  }

  $auth->{public_key} = resolv_path( $auth->{public_key}, 1 )
    if ( $auth->{public_key} );
  $auth->{private_key} = resolv_path( $auth->{private_key}, 1 )
    if ( $auth->{private_key} );

  my $profiler = Rex::Profiler->new;

  # task specific auth rules over all
  my %connect_hash = %{$auth};
  $connect_hash{server} = $server;

  # need to get rid of this
  Rex::push_connection(
    {
      conn     => $self->connection,
      ssh      => $self->connection->get_connection_object,
      server   => $server,
      cache    => Rex::Interface::Cache->create(),
      task     => [],
      profiler => $profiler,
      reporter => Rex::Report->create( Rex::Config->get_report_type ),
      notify   => Rex::Notify->new(),
    }
  );

  push @{ Rex::get_current_connection()->{task} }, $self;

  $profiler->start("connect");
  eval {
    $self->connection->connect(%connect_hash);
    1;
  } or do {
    if ( !defined Rex::Config->get_fallback_auth ) {
      croak $@;
    }
  };
  $profiler->end("connect");

  if ( !$self->connection->is_connected ) {
    Rex::pop_connection();
    croak("Couldn't connect to $server.");
  }
  elsif ( !$self->connection->is_authenticated ) {
    Rex::pop_connection();
    my $message =
      "Couldn't authenticate against $server. It may be caused by one or more of:\n";
    $message .= " - wrong username, password, key or passphrase\n";
    $message .= " - changed remote host key\n";
    $message .= " - root is not permitted to login over SSH\n"
      if ( $connect_hash{user} eq 'root' );

    if ( !exists $override{auth} ) {
      my $fallback_auth = Rex::Config->get_fallback_auth;
      if ( ref $fallback_auth eq "ARRAY" ) {
        my $ret_eval;
        for my $fallback_a ( @{$fallback_auth} ) {
          $ret_eval = eval { $self->connect( $server, auth => $fallback_a ); };
        }

        return $ret_eval if $ret_eval;
      }
    }

    croak($message);



( run in 0.561 second using v1.01-cache-2.11-cpan-524268b4103 )