PageRenderTime 56ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Rex/Task.pm

https://github.com/pplu/Rex
Perl | 324 lines | 253 code | 53 blank | 18 comment | 36 complexity | a14a5b812d15ecb9071fa2908dedb0ad MD5 | raw file
  1. #
  2. # (c) Jan Gehring <jan.gehring@gmail.com>
  3. #
  4. # vim: set ts=3 sw=3 tw=0:
  5. # vim: set expandtab:
  6. package Rex::Task;
  7. use strict;
  8. use warnings;
  9. use Net::SSH2;
  10. use Rex::Group;
  11. use Rex::Fork::Manager;
  12. use Rex::Cache;
  13. use Sys::Hostname;
  14. use vars qw(%tasks);
  15. # will be set from Rex::Transaction::transaction()
  16. our $IN_TRANSACTION = 0;
  17. sub create_task {
  18. my $class = shift;
  19. my $task_name = shift;
  20. my $options = pop;
  21. my $desc = pop;
  22. Rex::Logger::debug("Creating task: $task_name");
  23. my $func;
  24. if(ref($desc) eq "CODE") {
  25. $func = $desc;
  26. $desc = "";
  27. } else {
  28. $func = pop;
  29. }
  30. my $group = 'ALL';
  31. my @server = ();
  32. if($::FORCE_SERVER) {
  33. my @servers = split(/\s+/, $::FORCE_SERVER);
  34. push @server, Rex::Commands::evaluate_hostname($_) for @servers;
  35. Rex::Logger::debug("\tserver: $_") for @server;
  36. }
  37. else {
  38. if(scalar(@_) >= 1) {
  39. if($_[0] eq "group") {
  40. $group = $_[1];
  41. if(Rex::Group->is_group($group)) {
  42. Rex::Logger::debug("\tusing group: $group -> " . join(", ", Rex::Group->get_group($group)));
  43. for my $server_name (Rex::Group->get_group($group)) {
  44. if(ref($server_name) eq "CODE") {
  45. push(@server, $server_name);
  46. }
  47. else {
  48. push(@server, Rex::Commands::evaluate_hostname($server_name));
  49. }
  50. }
  51. Rex::Logger::debug("\tserver: $_") for @server;
  52. } else {
  53. Rex::Logger::info("Group $group not found!");
  54. exit 1;
  55. }
  56. } else {
  57. push @server, Rex::Commands::evaluate_hostname($_) for @_;
  58. Rex::Logger::debug("\tserver: $_") for @server;
  59. }
  60. }
  61. }
  62. $tasks{$task_name} = {
  63. func => $func,
  64. server => [ @server ],
  65. desc => $desc,
  66. no_ssh => ($options->{"no_ssh"}?1:0)
  67. };
  68. }
  69. sub get_tasks {
  70. my $class = shift;
  71. return sort { $a cmp $b } keys %tasks;
  72. }
  73. sub get_tasks_for {
  74. my $class = shift;
  75. my $host = shift;
  76. my @tasks;
  77. for my $task_name (keys %tasks) {
  78. my @servers = @{$tasks{$task_name}->{"server"}};
  79. if(grep { /^$host$/ } @servers) {
  80. push @tasks, $task_name;
  81. }
  82. }
  83. return sort { $a cmp $b } @tasks;
  84. }
  85. sub clear_tasks {
  86. my $class = shift;
  87. %tasks = ();
  88. }
  89. sub get_desc {
  90. my $class = shift;
  91. my $task = shift;
  92. return $tasks{$task}->{"desc"};
  93. }
  94. sub is_task {
  95. my $class = shift;
  96. my $task = shift;
  97. if(exists $tasks{$task}) { return 1; }
  98. return 0;
  99. }
  100. sub run {
  101. my $class = shift;
  102. my $task = shift;
  103. my $server_overwrite = shift;
  104. my $params = shift;
  105. my $ret;
  106. Rex::Logger::info("Running task: $task");
  107. # get servers belonging to the task
  108. my @server = @{$tasks{$task}->{'server'}};
  109. Rex::Logger::debug("\tserver: $_") for @server;
  110. my @new_server;
  111. for my $server_name (@server) {
  112. if(ref($server_name) eq "CODE") {
  113. push(@new_server, &$server_name());
  114. }
  115. else {
  116. push(@new_server, $server_name);
  117. }
  118. }
  119. @server = @new_server;
  120. # overwrite servers if requested
  121. # this is mostly for the rex agent
  122. if($server_overwrite) {
  123. @server = ($server_overwrite);
  124. }
  125. my($user, $pass, $pass_auth);
  126. if(ref($server[-1]) eq "HASH") {
  127. # use extra defined credentials
  128. my $data = pop(@server);
  129. $user = $data->{'user'};
  130. $pass = $data->{'password'};
  131. $pass_auth = 1;
  132. } else {
  133. $user = Rex::Config->get_user;
  134. $pass = Rex::Config->get_password;
  135. $pass_auth = Rex::Config->get_password_auth;
  136. }
  137. Rex::Logger::debug("Using user: $user");
  138. Rex::Logger::debug("Using password: " . ($pass?$pass:"<no password>"));
  139. my $timeout = Rex::Config->get_timeout;
  140. # parse cli parameter in the form
  141. # --key=value or --key
  142. my @params = @ARGV[1..$#ARGV];
  143. my %opts = ();
  144. for my $p (@params) {
  145. my($key, $val) = split(/=/, $p, 2);
  146. $key = substr($key, 2);
  147. if($val) { $opts{$key} = $val; next; }
  148. $opts{$key} = 1;
  149. }
  150. if($params) {
  151. %opts = %{$params};
  152. }
  153. # get hostname
  154. my $hostname = hostname();
  155. my ($shortname) = ($hostname =~ m/^([^\.]+)\.?/);
  156. Rex::Logger::debug("My Hostname: " . $hostname);
  157. Rex::Logger::debug("My Shortname: " . $shortname);
  158. if(scalar(@server) > 0) {
  159. # task should not run lokal
  160. my @children;
  161. # create form manager object
  162. my $fm = Rex::Fork::Manager->new(max => Rex::Config->get_parallelism);
  163. # iterate over the server and push the worker function to the fork manager's queue
  164. for my $server (@server) {
  165. Rex::Logger::debug("Next Server: $server");
  166. # push it
  167. my $forked_sub = sub {
  168. my $ssh;
  169. # this must be a ssh connection
  170. if(! $tasks{$task}->{"no_ssh"} && $server ne "localhost" && $server ne $shortname) {
  171. $ssh = Net::SSH2->new;
  172. my $fail_connect = 0;
  173. Rex::Logger::info("Connecting to $server (" . $user . ")");
  174. CON_SSH:
  175. unless($ssh->connect($server, Rex::Config->get_port, Timeout => Rex::Config->get_timeout)) {
  176. ++$fail_connect;
  177. sleep 1;
  178. goto CON_SSH if($fail_connect < Rex::Config->get_max_connect_fails); # try connecting 3 times
  179. Rex::Logger::info("Can't connect to $server");
  180. CORE::exit; # kind beenden
  181. }
  182. Rex::Logger::debug("Current Error-Code: " . $ssh->error());
  183. Rex::Logger::info("Connected to $server, trying to authenticate.");
  184. my $auth_ret;
  185. $auth_ret = $ssh->auth('username' => $user,
  186. 'password' => $pass,
  187. 'publickey' => Rex::Config->get_public_key,
  188. 'privatekey' => Rex::Config->get_private_key);
  189. # push a remote connection
  190. Rex::push_connection({ssh => $ssh, server => $server, sftp => $ssh->sftp?$ssh->sftp:undef, cache => Rex::Cache->new});
  191. Rex::Logger::debug("Current Error-Code: " . $ssh->error());
  192. # auth unsuccessfull
  193. unless($auth_ret) {
  194. Rex::Logger::info("Wrong username or password. Or wrong key.");
  195. CORE::exit 1;
  196. }
  197. Rex::Logger::debug("Successfull auth");
  198. }
  199. else {
  200. # this is a remote session without a ssh connection
  201. # for example for libvirt.
  202. Rex::Logger::debug("This is a remote session with NO_SSH");
  203. Rex::push_connection({ssh => 0, server => $server, sftp => 0, cache => Rex::Cache->new});
  204. }
  205. # run the task
  206. $ret = _exec($task, \%opts);
  207. # disconnect if ssh connection
  208. if(! $tasks{$task}->{"no_ssh"} && $server ne "localhost" && $server ne $shortname) {
  209. Rex::Logger::debug("Disconnecting from $server");
  210. $ssh->disconnect() unless($IN_TRANSACTION);
  211. }
  212. # remove remote connection from the stack
  213. Rex::pop_connection();
  214. CORE::exit unless($IN_TRANSACTION); # exit child
  215. }; # [END] $forked_sub
  216. # add the worker (forked_sub) to the fork queue
  217. unless($IN_TRANSACTION) {
  218. # not inside a transaction, so lets fork happyly...
  219. $fm->add($forked_sub, 1);
  220. }
  221. else {
  222. # inside a transaction, no little small funny kids, ... and no chance to get zombies :(
  223. &$forked_sub();
  224. }
  225. } # [END] for my $server
  226. Rex::Logger::debug("Waiting for children to finish");
  227. # wait for all jobs to be finished
  228. $fm->wait_for_all;
  229. } else {
  230. Rex::Logger::debug("This is not a remote session");
  231. # push a local connection
  232. Rex::push_connection({ssh => 0, server => "<local>", sftp => 0, cache => Rex::Cache->new});
  233. $ret = _exec($task, \%opts);
  234. # remove local connection from stack
  235. Rex::pop_connection();
  236. }
  237. return $ret;
  238. }
  239. sub _exec {
  240. my $task = shift;
  241. my $opts = shift;
  242. Rex::Logger::debug("Executing $task");
  243. my $code = $tasks{$task}->{'func'};
  244. return &$code($opts);
  245. }
  246. 1;