From 8ef18c4a3f18b7d1e75c62f2d1a6c35300043ab5 Mon Sep 17 00:00:00 2001 From: Mercier Pierre-Olivier Date: Sun, 29 Sep 2013 04:34:11 +0200 Subject: [PATCH] Add guantanamo: a way to execute command in an unknown environnement --- ACU/Process.pm | 25 ++++- process/exec/guantanamo.pl | 175 ++++++++++++++++++++++++++++++++ process/exec/guantanamo_node.pl | 108 ++++++++++++++++++++ 3 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 process/exec/guantanamo.pl create mode 100644 process/exec/guantanamo_node.pl diff --git a/ACU/Process.pm b/ACU/Process.pm index 4298e8f..68e7638 100644 --- a/ACU/Process.pm +++ b/ACU/Process.pm @@ -320,6 +320,30 @@ sub getFirstChild ($) return $self->{children}[0]; } +sub recreateNode +{ + my $self = shift; + my $doc = shift; + my $parent = shift; + + my $node = $doc->createElement($self->{nodeName}); + for my $attkey (keys %{ $self->{attributes} }) + { + $node->addChild( $doc->createAttribute($attkey, $self->{attributes}{ $attkey }) ); + } + + for my $child (@{ $self->{children} }) + { + $child->recreateNode($doc, $node); + } + + if ($self->{nodeValue}) { + $node->appendText( $self->{nodeValue} ); + } + + $parent->appendChild($node); +} + package ProcessHandler; @@ -399,7 +423,6 @@ sub end_element { my $item = pop @{ $self->{subtreeStack} }; $item->{nodeValue} .= $self->{values}; - $item->{nodeValue} =~ s/\n+/ /g; $item->{nodeValue} =~ s/ +/ /g; if (@{ $self->{subtreeStack} } > 0) { push @{ $self->{subtreeStack}[-1]->{children} }, $item; diff --git a/process/exec/guantanamo.pl b/process/exec/guantanamo.pl new file mode 100644 index 0000000..745a120 --- /dev/null +++ b/process/exec/guantanamo.pl @@ -0,0 +1,175 @@ +#!/usr/bin/env perl + +use v5.10.1; +use strict; +use warnings; +use Gearman::Worker; +use MIME::Base64; +use XML::LibXML; + +use ACU::LDAP; +use ACU::Log; +use ACU::Process; + +my %master_actions = +( + "launch" => \&master_launch, + "register" => \&master_register, +); + +my @nodes; + +sub master_register +{ + my $args = shift; + + if ($args->{param}{nodename}) { + my $nodename = $args->{param}{nodename}; + + log INFO, "New node: $nodename"; + push @nodes, "$nodename"; + } + else { + log WARN, "nodename empty, cannot register new node"; + } +} + +sub build_task_xml +{ + my $files = shift; + my $subtree = shift; + + my $doc = XML::LibXML::Document->new('1.0'); + my $root = $doc->createElement("guantanamo"); + $doc->setDocumentElement( $root ); + + log TRACE, $subtree; + + if ($files) + { + log TRACE, $files; + + for my $key (keys %{ $files }) + { + my $file = $doc->createElement("file"); + $file->addChild( $doc->createAttribute("name", $key) ); + $file->addChild( $doc->createAttribute("encoding", "base64") ); + $file->appendText(encode_base64($files->{$key})); + $root->appendChild($file); + } + } + + if ($subtree) { + $subtree->recreateNode($doc, $root); + } + + my $ret = $doc->toString(); + log TRACE, $ret; + return $ret; +} + +sub master_launch +{ + my $args = shift; + + my @lnodes; + my @warn; + + if ($args->{unamed}) + { + for (my $i = $args->{unamed}; $i > 0; $i--) + { + if (grep { $args->{param}{$i} eq $_ } @nodes) { + push @lnodes, $args->{param}{$i}; + } else { + push @warn, $args->{param}{$i}." not a currently launched architecture."; + } + } + } + else { + @lnodes = @nodes; + } + + log DEBUG, "Launching nodes..."; + + my %ret; + + my $client = Gearman::Client->new; + $client->job_servers('gearmand:4730'); + my $taskset = $client->new_task_set; + for my $node (@lnodes) + { + log DEBUG, "Launching $node..."; + + $taskset->add_task( + "guantanamo_".$node => build_task_xml($args->{files}, $args->{subtree}), + { + on_complete => sub { + my $dom = XML::LibXML->load_xml(string => ${ $_[0] }); + $ret{ $node } = $dom; + } + }); + } + $taskset->wait; + + if ($args->{subtree}->hasAttribute("output") && $args->{subtree}->getAttribute("output") eq "text") + { + my $output = ""; + + for my $w (@warn) { + $output .= $w."\n"; + } + + for my $node (@lnodes) { + my $o = $ret{$node}->documentElement->getElementsByTagName("out"); + if ($o) { + $output .= $o[0]->firstChild->nodeValue; + } + + $e = $ret{$node}->documentElement->getElementsByTagName("err"); + if ($e) { + $output .= $e[0]->firstChild->nodeValue; + } + $output .= $e[0]->firstChild->nodeValue; + } + + return $output; + } + else + { + my $doc = XML::LibXML::Document->new('1.0'); + my $root = $doc->createElement("process"); + $doc->setDocumentElement( $root ); + + for my $w (@warn) + { + my $warning = $doc->createElement("warning"); + $warning->appendText($w); + $root->appendChild($warning); + } + + for my $k (keys %ret) + { + $root->appendChild($ret{ $k }->documentElement); + } + + return $doc->toString(); + } +} + +sub process_master +{ + my ($given_args, $args) = @_; + + my $action = $args->{param}{action} // "launch"; + + if (! exists $master_actions{$action}) { + log WARN, "Unknown action '$action' for guantanamo master process."; + } + return $master_actions{$action}($args); +} + + +log INFO, "Starting guantanamo.pl as master process"; + +Process::register("guantanamo", \&process_master); diff --git a/process/exec/guantanamo_node.pl b/process/exec/guantanamo_node.pl new file mode 100644 index 0000000..0e0cdeb --- /dev/null +++ b/process/exec/guantanamo_node.pl @@ -0,0 +1,108 @@ +#!/usr/bin/env perl + +use v5.10.1; +use strict; +use warnings; +use Carp; +use File::Path qw(make_path remove_tree); +use File::Temp qw/tempfile tempdir/; +use IPC::Open3; +use XML::LibXML; + +use ACU::LDAP; +use ACU::Log; +use ACU::Process; + +my %node_actions = +( + "launch" => \&node_launch, +); + +sub node_launch +{ + my $args = shift; + + # First, create a temporary directory + my $dir = tempdir(); + chdir( $dir ); + + # Extract all files to current directory + for my $filename (keys %{ $args->{files} }) + { + open my $fh, ">", $filename or croak("$filename: $!"); + print $fh $args->{files}{$filename}; + close $fh; + } + + my $doc = XML::LibXML::Document->new('1.0'); + my $root = $doc->createElement("target"); + $root->addChild( $doc->createAttribute("name", $ARGV[0]) ); + $doc->setDocumentElement( $root ); + + for my $c ($args->{subtree}->getElementsByTagName("command")) + { + if (! exists $c->{attributes}{target} || + index($c->{attributes}{target}, $ARGV[0]) != -1) { + + my $cmd = $doc->createElement("cmd"); + if (! exists $c->{attributes}{hide}) { + $root->appendChild($cmd); + } + + my $command = $doc->createElement("command"); + $command->appendText($c->{nodeValue}); + $cmd->appendChild($command); + + my($wtr, $rdr, $stderr); + my $pid = open3($wtr, $rdr, $stderr, $c->{nodeValue}); + waitpid( $pid, 0 ); + my $rv = $? >> 8; + + my $out = $doc->createElement("out"); + my $str = ""; + if ($rdr) { + $str .= $_ while (<$rdr>); + } + $out->appendText($str); + $cmd->appendChild($out); + + my $err = $doc->createElement("err"); + $str = ""; + if ($stderr) { + $str .= $_ while (<$stderr>); + } + $err->appendText($str); + $cmd->appendChild($err); + + my $ret = $doc->createElement("return"); + $ret->appendText($rv); + $cmd->appendChild($ret); + } + } + + chdir(); + remove_tree( $dir ); + + return $doc->toString(); +} + +sub process_node +{ + my ($given_args, $args) = @_; + + my $action = $args->{param}{action} // "launch"; + + if (! exists $node_actions{$action}) { + log WARN, "Unknown action '$action' for guantanamo node process."; + } + return $node_actions{$action}($args); +} + +if ($#ARGV == 0) +{ + log INFO, "Starting guantanamo.pl as node process"; + + Process::Client::launch("guantanamo", {"action" => "register", "nodename" => $ARGV[0]}); + + Process::register("guantanamo_".$ARGV[0], \&process_node); +}