1# 2# (c) Jan Gehring <jan.gehring@gmail.com> 3# 4# vim: set ts=2 sw=2 tw=0: 5# vim: set expandtab: 6 7=head1 NAME 8 9Rex::Task - The Task Object 10 11=head1 DESCRIPTION 12 13The Task Object. Typically you only need this class if you want to manipulate tasks after their initial creation. 14 15=head1 SYNOPSIS 16 17 use Rex::Task; 18 19 # create a new task 20 my $task = Rex::Task->new( name => 'testtask' ); 21 $task->set_server('remoteserver'); 22 $task->set_code( sub { say 'Hello'; } ); 23 $task->modify( 'no_ssh', 1 ); 24 25 # retrieve an existing task 26 use Rex::TaskList; 27 28 my $existing_task = Rex::TaskList->create->get_task('my_task'); 29 30=head1 METHODS 31 32=cut 33 34package Rex::Task; 35 36use 5.010001; 37use strict; 38use warnings; 39use Data::Dumper; 40use Time::HiRes qw(time); 41 42our $VERSION = '1.13.4'; # VERSION 43 44use Rex::Logger; 45use Rex::TaskList; 46use Rex::Interface::Connection; 47use Rex::Interface::Executor; 48use Rex::Group::Entry::Server; 49use Rex::Profiler; 50use Rex::Hardware; 51use Rex::Interface::Cache; 52use Rex::Report; 53use Rex::Helper::Run; 54use Rex::Helper::Path; 55use Rex::Notify; 56use Carp; 57 58require Rex::Commands; 59 60require Rex::Args; 61 62=head2 new 63 64This is the constructor. 65 66 $task = Rex::Task->new( 67 func => sub { some_code_here }, 68 server => [ @server ], 69 desc => $description, 70 no_ssh => $no_ssh, 71 hidden => $hidden, 72 auth => { 73 user => $user, 74 password => $password, 75 private_key => $private_key, 76 public_key => $public_key, 77 }, 78 before => [sub {}, sub {}, ...], 79 after => [sub {}, sub {}, ...], 80 around => [sub {}, sub {}, ...], 81 before_task_start => [sub {}, sub {}, ...], 82 after_task_finished => [sub {}, sub {}, ...], 83 name => $task_name, 84 executor => Rex::Interface::Executor->create, 85 opts => {key1 => val1, key2 => val2, ...}, 86 args => [arg1, arg2, ...], 87 ); 88 89=cut 90 91sub new { 92 my $that = shift; 93 my $proto = ref($that) || $that; 94 my $self = {@_}; 95 96 bless( $self, $proto ); 97 98 if ( !exists $self->{name} ) { 99 die("You have to define a task name."); 100 } 101 102 $self->{no_ssh} ||= 0; 103 $self->{func} ||= sub { }; 104 $self->{executor} ||= Rex::Interface::Executor->create; 105 $self->{opts} ||= {}; 106 $self->{args} ||= []; 107 108 $self->{connection} = undef; 109 110 # set to true as default 111 if ( !exists $self->{exit_on_connect_fail} ) { 112 $self->{exit_on_connect_fail} = 1; 113 } 114 115 return $self; 116} 117 118=head2 connection 119 120Returns the current connection object. 121 122=cut 123 124sub connection { 125 my ($self) = @_; 126 if ( !exists $self->{connection} || !$self->{connection} ) { 127 $self->{connection} = 128 Rex::Interface::Connection->create( $self->get_connection_type ); 129 } 130 131 $self->{connection}; 132} 133 134sub set_connection { 135 my ( $self, $conn ) = @_; 136 $self->{connection} = $conn; 137} 138 139=head2 executor 140 141Returns the current executor object. 142 143=cut 144 145sub executor { 146 my ($self) = @_; 147 $self->{executor}->set_task($self); 148 return $self->{executor}; 149} 150 151=head2 hidden 152 153Returns true if the task is hidden. (Should not be displayed on ,,rex -T''.) 154 155=cut 156 157sub hidden { 158 my ($self) = @_; 159 return $self->{hidden}; 160} 161 162=head2 server 163 164Returns the servers on which the task should be executed as an ArrayRef. 165 166=cut 167 168sub server { 169 my ($self) = @_; 170 171 my @server = @{ $self->{server} }; 172 my @ret = (); 173 174 if ( ref( $server[-1] ) eq "HASH" ) { 175 Rex::deprecated( 176 undef, "0.40", 177 "Defining extra credentials within the task creation is deprecated.", 178 "Please use set auth => task => 'taskname' instead." 179 ); 180 181 # use extra defined credentials 182 my $data = pop(@server); 183 $self->set_auth( "user", $data->{'user'} ); 184 $self->set_auth( "password", $data->{'password'} ); 185 186 if ( exists $data->{"private_key"} ) { 187 $self->set_auth( "private_key", $data->{"private_key"} ); 188 $self->set_auth( "public_key", $data->{"public_key"} ); 189 } 190 } 191 192 if ( ref( $self->{server} ) eq "ARRAY" 193 && scalar( @{ $self->{server} } ) > 0 ) 194 { 195 for my $srv ( @{ $self->{server} } ) { 196 if ( ref($srv) eq "CODE" ) { 197 push( @ret, &$srv() ); 198 } 199 else { 200 if ( ref $srv && $srv->isa("Rex::Group::Entry::Server") ) { 201 push( @ret, $srv->get_servers ); 202 } 203 else { 204 push( @ret, $srv ); 205 } 206 } 207 } 208 } 209 elsif ( ref( $self->{server} ) eq "CODE" ) { 210 push( @ret, &{ $self->{server} }() ); 211 } 212 else { 213 push( @ret, Rex::Group::Entry::Server->new( name => "<local>" ) ); 214 } 215 216 return [@ret]; 217} 218 219=head2 set_server(@server) 220 221With this method you can set new servers on which the task should be executed on. 222 223=cut 224 225sub set_server { 226 my ( $self, @server ) = @_; 227 $self->{server} = \@server; 228} 229 230=head2 delete_server 231 232Delete every server registered to the task. 233 234=cut 235 236sub delete_server { 237 my ($self) = @_; 238 delete $self->{current_server}; 239 delete $self->{server}; 240 $self->rethink_connection; 241} 242 243=head2 current_server 244 245Returns the current server on which the tasks gets executed right now. 246 247=cut 248 249sub current_server { 250 my ($self) = @_; 251 return $self->{current_server} 252 || Rex::Group::Entry::Server->new( name => "<local>" ); 253} 254 255=head2 desc 256 257Returns the description of a task. 258 259=cut 260 261sub desc { 262 my ($self) = @_; 263 return $self->{desc}; 264} 265 266=head2 set_desc($description) 267 268Set the description of a task. 269 270=cut 271 272sub set_desc { 273 my ( $self, $desc ) = @_; 274 $self->{desc} = $desc; 275} 276 277=head2 is_remote 278 279Returns true (1) if the task will be executed remotely. 280 281=cut 282 283sub is_remote { 284 my ($self) = @_; 285 if ( exists $self->{current_server} ) { 286 if ( $self->{current_server} ne '<local>' ) { 287 return 1; 288 } 289 } 290 else { 291 if ( exists $self->{server} && scalar( @{ $self->{server} } ) > 0 ) { 292 return 1; 293 } 294 } 295 296 return 0; 297} 298 299=head2 is_local 300 301Returns true (1) if the task gets executed on the local host. 302 303=cut 304 305sub is_local { 306 my ($self) = @_; 307 return $self->is_remote() == 0 ? 1 : 0; 308} 309 310=head2 is_http 311 312Returns true (1) if the task gets executed over http protocol. 313 314=cut 315 316sub is_http { 317 my ($self) = @_; 318 return ( $self->{"connection_type"} 319 && lc( $self->{"connection_type"} ) eq "http" ); 320} 321 322=head2 is_https 323 324Returns true (1) if the task gets executed over https protocol. 325 326=cut 327 328sub is_https { 329 my ($self) = @_; 330 return ( $self->{"connection_type"} 331 && lc( $self->{"connection_type"} ) eq "https" ); 332} 333 334=head2 is_openssh 335 336Returns true (1) if the task gets executed with openssh. 337 338=cut 339 340sub is_openssh { 341 my ($self) = @_; 342 return ( $self->{"connection_type"} 343 && lc( $self->{"connection_type"} ) eq "openssh" ); 344} 345 346=head2 want_connect 347 348Returns true (1) if the task will establish a connection to a remote system. 349 350=cut 351 352sub want_connect { 353 my ($self) = @_; 354 return $self->{no_ssh} == 0 ? 1 : 0; 355} 356 357=head2 get_connection_type 358 359This method tries to guess the right connection type for the task and returns it. 360 361Current return values are below: 362 363=over 4 364 365=item * SSH: connect to the remote server using Net::SSH2 366 367=item * OpenSSH: connect to the remote server using Net::OpenSSH 368 369=item * Local: runs locally (without any connections) 370 371=item * HTTP: uses experimental HTTP connection 372 373=item * HTTPS: uses experimental HTTPS connection 374 375=item * Fake: populate the connection properties, but do not connect 376 377So you can use this type to iterate over a list of remote hosts, but don't let rex build a connection. For example if you want to use Sys::Virt or other modules. 378 379=back 380 381=cut 382 383sub get_connection_type { 384 my ($self) = @_; 385 386 if ( $self->is_http ) { 387 return "HTTP"; 388 } 389 elsif ( $self->is_https ) { 390 return "HTTPS"; 391 } 392 elsif ( $self->is_remote && $self->is_openssh && $self->want_connect ) { 393 return "OpenSSH"; 394 } 395 elsif ( $self->is_remote && $self->want_connect ) { 396 return Rex::Config->get_connection_type(); 397 } 398 elsif ( $self->is_remote ) { 399 return "Fake"; 400 } 401 else { 402 return "Local"; 403 } 404} 405 406=head2 modify($key, $value) 407 408With this method you can modify values of the task. 409 410=cut 411 412sub modify { 413 my ( $self, $key, $value ) = @_; 414 415 if ( ref( $self->{$key} ) eq "ARRAY" ) { 416 push( @{ $self->{$key} }, $value ); 417 } 418 else { 419 $self->{$key} = $value; 420 } 421 422 $self->rethink_connection; 423} 424 425=head2 rethink_connection 426 427Deletes current connection object. 428 429=cut 430 431sub rethink_connection { 432 my ($self) = @_; 433 delete $self->{connection}; 434} 435 436=head2 user 437 438Returns the username the task will use. 439 440=cut 441 442sub user { 443 my ($self) = @_; 444 if ( exists $self->{auth} && $self->{auth}->{user} ) { 445 return $self->{auth}->{user}; 446 } 447} 448 449=head2 set_user($user) 450 451Set the username of a task. 452 453=cut 454 455sub set_user { 456 my ( $self, $user ) = @_; 457 $self->{auth}->{user} = $user; 458} 459 460=head2 password 461 462Returns the password that will be used. 463 464=cut 465 466sub password { 467 my ($self) = @_; 468 if ( exists $self->{auth} && $self->{auth}->{password} ) { 469 return $self->{auth}->{password}; 470 } 471} 472 473=head2 set_password($password) 474 475Set the password of the task. 476 477=cut 478 479sub set_password { 480 my ( $self, $password ) = @_; 481 $self->{auth}->{password} = $password; 482} 483 484=head2 name 485 486Returns the name of the task. 487 488=cut 489 490sub name { 491 my ($self) = @_; 492 return $self->{name}; 493} 494 495=head2 code 496 497Returns the code of the task. 498 499=cut 500 501sub code { 502 my ($self) = @_; 503 return $self->{func}; 504} 505 506=head2 set_code(\&code_ref) 507 508Set the code of the task. 509 510=cut 511 512sub set_code { 513 my ( $self, $code ) = @_; 514 $self->{func} = $code; 515} 516 517=head2 run_hook($server, $hook) 518 519This method is used internally to execute the specified hooks. 520 521=cut 522 523sub run_hook { 524 my ( $self, $server, $hook, @more_args ) = @_; 525 my $old_server; 526 527 for my $code ( @{ $self->{$hook} } ) { 528 if ( $hook eq "after" ) { # special case for after hooks 529 $code->( 530 $$server, 531 ( $self->{"__was_authenticated"} || 0 ), 532 { $self->get_opts }, @more_args 533 ); 534 } 535 else { 536 $old_server = $$server if $server; 537 $code->( $$server, $server, { $self->get_opts }, @more_args ); 538 if ( $old_server && $old_server ne $$server ) { 539 $self->{current_server} = $$server; 540 } 541 } 542 } 543} 544 545=head2 set_auth($key, $value) 546 547Set the authentication of the task. 548 549 $task->set_auth("user", "foo"); 550 $task->set_auth("password", "bar"); 551 552=cut 553 554sub set_auth { 555 my ( $self, $key, $value ) = @_; 556 557 if ( scalar(@_) > 3 ) { 558 my $_d = shift; 559 $self->{auth} = {@_}; 560 } 561 else { 562 $self->{auth}->{$key} = $value; 563 } 564} 565 566=head2 merge_auth($server) 567 568Merges the authentication information from $server into the task. 569Tasks authentication information have precedence. 570 571=cut 572 573sub merge_auth { 574 my ( $self, $server ) = @_; 575 576 # merge auth hashs 577 # task auth as precedence 578 my %auth = $server->merge_auth( $self->{auth} ); 579 580 return \%auth; 581} 582 583=head2 get_sudo_password 584 585Returns the sudo password. 586 587=cut 588 589sub get_sudo_password { 590 my ($self) = @_; 591 592 my $server = $self->connection->server; 593 my %auth = $server->merge_auth( $self->{auth} ); 594 595 return $auth{sudo_password}; 596} 597 598=head2 parallelism 599 600Get the parallelism count of a task. 601 602=cut 603 604sub parallelism { 605 my ($self) = @_; 606 return $self->{parallelism}; 607} 608 609=head2 set_parallelism($count) 610 611Set the parallelism of the task. 612 613=cut 614 615sub set_parallelism { 616 my ( $self, $para ) = @_; 617 $self->{parallelism} = $para; 618} 619 620=head2 connect($server) 621 622Initiate the connection to $server. 623 624=cut 625 626sub connect { 627 my ( $self, $server, %override ) = @_; 628 629 if ( !ref $server ) { 630 $server = Rex::Group::Entry::Server->new( name => $server ); 631 } 632 $self->{current_server} = $server; 633 634 $self->run_hook( \$server, "before" ); 635 636 # need to be called, in case of a run_task task call. 637 # see #788 638 $self->rethink_connection; 639 640 my $user = $self->user; 641 642 #print Dumper($self); 643 my $auth = $self->merge_auth($server); 644 645 if ( exists $override{auth} ) { 646 $auth = $override{auth}; 647 $user = $auth->{user}; 648 } 649 650 my $rex_int_conf = Rex::Commands::get("rex_internals"); 651 Rex::Logger::debug( Dumper($rex_int_conf) ); 652 Rex::Logger::debug("Auth-Information inside Task:"); 653 for my $key ( keys %{$auth} ) { 654 my $data = $auth->{$key}; 655 $data = Rex::Logger::masq( "%s", $data ) if $key eq 'password'; 656 $data = Rex::Logger::masq( "%s", $data ) if $key eq 'sudo_password'; 657 $data ||= ""; 658 659 Rex::Logger::debug("$key => [[$data]]"); 660 } 661 662 $auth->{public_key} = resolv_path( $auth->{public_key}, 1 ) 663 if ( $auth->{public_key} ); 664 $auth->{private_key} = resolv_path( $auth->{private_key}, 1 ) 665 if ( $auth->{private_key} ); 666 667 my $profiler = Rex::Profiler->new; 668 669 # task specific auth rules over all 670 my %connect_hash = %{$auth}; 671 $connect_hash{server} = $server; 672 673 # need to get rid of this 674 Rex::push_connection( 675 { 676 conn => $self->connection, 677 ssh => $self->connection->get_connection_object, 678 server => $server, 679 cache => Rex::Interface::Cache->create(), 680 task => [], 681 profiler => $profiler, 682 reporter => Rex::Report->create( Rex::Config->get_report_type ), 683 notify => Rex::Notify->new(), 684 } 685 ); 686 687 push @{ Rex::get_current_connection()->{task} }, $self; 688 689 $profiler->start("connect"); 690 eval { 691 $self->connection->connect(%connect_hash); 692 1; 693 } or do { 694 if ( !defined Rex::Config->get_fallback_auth ) { 695 croak $@; 696 } 697 }; 698 $profiler->end("connect"); 699 700 if ( !$self->connection->is_connected ) { 701 Rex::pop_connection(); 702 croak("Couldn't connect to $server."); 703 } 704 elsif ( !$self->connection->is_authenticated ) { 705 Rex::pop_connection(); 706 my $message = 707 "Couldn't authenticate against $server. It may be caused by one or more of:\n"; 708 $message .= " - wrong username, password, key or passphrase\n"; 709 $message .= " - changed remote host key\n"; 710 $message .= " - root is not permitted to login over SSH\n" 711 if ( $connect_hash{user} eq 'root' ); 712 713 if ( !exists $override{auth} ) { 714 my $fallback_auth = Rex::Config->get_fallback_auth; 715 if ( ref $fallback_auth eq "ARRAY" ) { 716 my $ret_eval; 717 for my $fallback_a ( @{$fallback_auth} ) { 718 $ret_eval = eval { $self->connect( $server, auth => $fallback_a ); }; 719 } 720 721 return $ret_eval if $ret_eval; 722 } 723 } 724 725 croak($message); 726 } 727 else { 728 Rex::Logger::debug("Successfully authenticated on $server.") 729 if ( $self->connection->get_connection_type ne "Local" ); 730 $self->{"__was_authenticated"} = 1; 731 } 732 733 $self->run_hook( \$server, "around" ); 734 735 return 1; 736} 737 738=head2 disconnect 739 740Disconnect from the current connection. 741 742=cut 743 744sub disconnect { 745 my ( $self, $server ) = @_; 746 747 $self->run_hook( \$server, "around", 1 ); 748 $self->connection->disconnect; 749 750 my %args = Rex::Args->getopts; 751 752 if ( defined $args{'d'} && $args{'d'} > 2 ) { 753 Rex::Commands::profiler()->report; 754 } 755 756 delete $self->{connection}; 757 758 pop @{ Rex::get_current_connection()->{task} }; 759 760 # need to get rid of this 761 Rex::pop_connection(); 762 763 $self->run_hook( \$server, "after" ); 764} 765 766=head2 get_data 767 768Dump task data. 769 770=cut 771 772sub get_data { 773 my ($self) = @_; 774 775 return { 776 func => $self->{func}, 777 server => $self->{server}, 778 desc => $self->{desc}, 779 no_ssh => $self->{no_ssh}, 780 hidden => $self->{hidden}, 781 auth => $self->{auth}, 782 before => $self->{before}, 783 after => $self->{after}, 784 around => $self->{around}, 785 name => $self->{name}, 786 executor => $self->{executor}, 787 connection_type => $self->{connection_type}, 788 opts => $self->{opts}, 789 args => $self->{args}, 790 }; 791} 792 793=head2 run($server, %options) 794 795Run the task on C<$server>, with C<%options>. 796 797=cut 798 799sub run { 800 return pre_40_run(@_) unless ref $_[0]; 801 802 my ( $self, $server, %options ) = @_; 803 804 $options{opts} ||= { $self->get_opts }; 805 $options{args} ||= [ $self->get_args ]; 806 $options{params} ||= $options{opts}; 807 808 if ( !ref $server ) { 809 $server = Rex::Group::Entry::Server->new( name => $server ); 810 } 811 812 if ( !$_[1] ) { 813 814 # run is called without any server. 815 # so just connect to any servers. 816 return Rex::TaskList->create()->run( $self, %options ); 817 } 818 819 # this is a method call 820 # so run the task 821 822 # TODO: refactor complete task calling 823 # direct call with function and normal task call 824 825 my ( $in_transaction, $start_time ); 826 827 $start_time = time; 828 829 if ( $server ne "<func>" ) { 830 831 # this is _not_ a task call via function syntax. 832 833 $in_transaction = $options{in_transaction}; 834 835 eval { $self->connect($server) }; 836 if ($@) { 837 my $error = $@; 838 $self->{"__was_authenticated"} = 0; 839 $self->run_hook( \$server, "after" ); 840 die $error; 841 } 842 843 if ( Rex::Args->is_opt("c") ) { 844 845 # get and cache all os info 846 if ( !Rex::get_cache()->load() ) { 847 Rex::Logger::debug("No cache found, need to collect new data."); 848 $server->gather_information; 849 } 850 } 851 852 if ( !$server->test_perl ) { 853 Rex::Logger::info( 854 "There is no perl interpreter found on this system. " 855 . "Some commands may not work. Sudo won't work.", 856 "warn" 857 ); 858 sleep 3; 859 } 860 861 } 862 else { 863# we need to push the connection information of the last task onto this task object 864# if we don't do this, the task doesn't have any information of the current connection when called like a function. 865# See: #1091 866 $self->set_connection( 867 Rex::get_current_connection()->{task}->[-1]->connection ) 868 if Rex::get_current_connection()->{task}->[-1]; 869 push @{ Rex::get_current_connection()->{task} }, $self; 870 } 871 872 # execute code 873 my @ret; 874 my $wantarray = wantarray; 875 876 eval { 877 $self->set_opts( %{ $options{params} } ) 878 if ref $options{params} eq "HASH"; 879 if ($wantarray) { 880 @ret = $self->executor->exec( $options{params}, $options{args} ); 881 } 882 else { 883 $ret[0] = $self->executor->exec( $options{params}, $options{args} ); 884 } 885 my $notify = Rex::get_current_connection()->{notify}; 886 $notify->run_postponed(); 887 } or do { 888 if ($@) { 889 my $error = $@; 890 891 Rex::get_current_connection()->{reporter} 892 ->report_resource_failed( message => $error ); 893 894 Rex::get_current_connection()->{reporter}->report_task_execution( 895 failed => 1, 896 start_time => $start_time, 897 end_time => time, 898 message => $error, 899 ); 900 901 Rex::get_current_connection()->{reporter}->write_report(); 902 903 pop @{ Rex::get_current_connection()->{task} }; 904 die($error); 905 } 906 }; 907 908 if ( $server ne "<func>" ) { 909 if ( Rex::Args->is_opt("c") ) { 910 911 # get and cache all os info 912 Rex::get_cache()->save(); 913 } 914 915 Rex::get_current_connection()->{reporter}->report_task_execution( 916 failed => 0, 917 start_time => $start_time, 918 end_time => time, 919 ); 920 921 Rex::get_current_connection()->{reporter}->write_report(); 922 923 if ($in_transaction) { 924 $self->run_hook( \$server, "around", 1 ); 925 $self->run_hook( \$server, "after" ); 926 } 927 else { 928 $self->disconnect($server); 929 } 930 } 931 else { 932 pop @{ Rex::get_current_connection()->{task} }; 933 } 934 935 if ($wantarray) { 936 return @ret; 937 } 938 else { 939 return $ret[0]; 940 } 941} 942 943sub pre_40_run { 944 my ( $class, $task_name, $server_overwrite, $params ) = @_; 945 946 # static calls to this method are deprecated 947 Rex::deprecated( "Rex::Task->run()", "0.40" ); 948 949 my $tasklist = Rex::TaskList->create; 950 my $task = $tasklist->get_task($task_name); 951 952 $task->set_server($server_overwrite) if $server_overwrite; 953 $tasklist->run( $task, params => $params ); 954} 955 956=head2 modify_task($task, $key => $value) 957 958Modify C<$task>, by setting C<$key> to C<$value>. 959 960=cut 961 962sub modify_task { 963 my $class = shift; 964 my $task = shift; 965 my $key = shift; 966 my $value = shift; 967 968 Rex::TaskList->create()->get_task($task)->modify( $key => $value ); 969} 970 971=head2 is_task 972 973Returns true(1) if the passed object is a task. 974 975=cut 976 977sub is_task { 978 my ( $class, $task ) = @_; 979 return Rex::TaskList->create()->is_task($task); 980} 981 982=head2 get_tasks 983 984Returns list of tasks. 985 986=cut 987 988sub get_tasks { 989 my ( $class, @tmp ) = @_; 990 return Rex::TaskList->create()->get_tasks(@tmp); 991} 992 993=head2 get_desc 994 995Returns description of task. 996 997=cut 998 999sub get_desc { 1000 my ( $class, @tmp ) = @_; 1001 return Rex::TaskList->create()->get_desc(@tmp); 1002} 1003 1004=head2 exit_on_connect_fail 1005 1006Returns true if rex should exit on connect failure. 1007 1008=cut 1009 1010sub exit_on_connect_fail { 1011 my ($self) = @_; 1012 return $self->{exit_on_connect_fail}; 1013} 1014 1015=head2 set_exit_on_connect_fail 1016 1017Sets if rex should exit on connect failure. 1018 1019=cut 1020 1021sub set_exit_on_connect_fail { 1022 my ( $self, $exit ) = @_; 1023 $self->{exit_on_connect_fail} = $exit; 1024} 1025 1026=head2 get_args 1027 1028Returns arguments of task. 1029 1030=cut 1031 1032sub get_args { 1033 my ($self) = @_; 1034 @{ $self->{args} || [] }; 1035} 1036 1037=head2 get_opts 1038 1039Returns options of task. 1040 1041=cut 1042 1043sub get_opts { 1044 my ($self) = @_; 1045 %{ $self->{opts} || {} }; 1046} 1047 1048=head2 set_args 1049 1050Sets arguments for task. 1051 1052=cut 1053 1054sub set_args { 1055 my ( $self, @args ) = @_; 1056 $self->{args} = \@args; 1057} 1058 1059=head2 set_opt 1060 1061Sets an option for task. 1062 1063=cut 1064 1065sub set_opt { 1066 my ( $self, $key, $value ) = @_; 1067 $self->{opts}->{$key} = $value; 1068} 1069 1070=head2 set_opts 1071 1072Sets options for task. 1073 1074=cut 1075 1076sub set_opts { 1077 my ( $self, %opts ) = @_; 1078 $self->{opts} = \%opts; 1079} 1080 1081=head2 clone 1082 1083Clones a task. 1084 1085=cut 1086 1087sub clone { 1088 my $self = shift; 1089 return Rex::Task->new( %{ $self->get_data } ); 1090} 1091 10921; 1093