PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Webface2.5/perl-lib/Webface/uniwrap.pm

https://bitbucket.org/genomicepidemiology/apache-webserver
Perl | 267 lines | 218 code | 33 blank | 16 comment | 29 complexity | 6a4f3099174ebeb26ee4473242bbaf75 MD5 | raw file
  1. package Webface::uniwrap;
  2. use strict;
  3. use IPC::PerlSSH;
  4. use POSIX;
  5. use Error qw(:try);
  6. use Sys::Hostname;
  7. use Webface::error;
  8. use vars qw/$config/;
  9. BEGIN {
  10. use JSON::Parse 'json_file_to_perl';
  11. our $config = json_file_to_perl('/configs/webface_config.json');
  12. }
  13. my @sshopt = ('-o', 'PasswordAuthentication no', '-o', 'KbdInteractiveDevices no');
  14. # resources ================================================================
  15. my $resources = $config->{'server_resources'};
  16. sub new
  17. {
  18. my $class = shift;
  19. my $self = {};
  20. bless($self, $class);
  21. my %opt = @_;
  22. $self->{retries} = $opt{retries} || 2;
  23. $self->{sleeptime} = $opt{sleeptime} || 2;
  24. $self->{user} = $opt{user} || $ENV{USER};
  25. $self->{server} = $opt{server} if exists $opt{server};
  26. $self->{arch} = $opt{arch} if exists $opt{arch};
  27. $self->{tmpdir} = $opt{tmpdir} || $ENV{TMPDIR} || '/tmp';
  28. $self->{host} = hostname();
  29. throw WebfaceSystemError(-text => "Specify server or arch") unless (exists $opt{server} or exists $opt{arch});
  30. throw WebfaceSystemError(-text => "Specify server OR arch")
  31. if (defined $self->{server} and defined $self->{arch});
  32. if (defined $self->{server}) {
  33. throw WebfaceSystemError(-text => "Resource $self->{server} unavailable")
  34. unless ($self->testserver($self->{server}) == 1);
  35. } else {
  36. $self->{server} = $self->findserver() unless defined $self->{server};
  37. }
  38. return $self;
  39. }
  40. sub execute
  41. {
  42. my $self = shift;
  43. my @args = @_;
  44. throw WebfaceSystemError(-text => "No arguments to uniwrap") unless (@args);
  45. # connect it ===============================================================
  46. my $st = -1;
  47. if ($self->{server} eq $self->{host}) {
  48. $st = system(@args);
  49. } else {
  50. try {
  51. $st = $self->client(@args);
  52. #warn("$self->{server}: process(@args) returned '$st'\n");
  53. }
  54. otherwise {
  55. my $e = shift;
  56. warn("Exception in execution on $self->{server}: $e\n");
  57. $st = -1;
  58. };
  59. }
  60. exit($st);
  61. }
  62. # run it =======================================================================
  63. sub client
  64. {
  65. my $self = shift;
  66. my @args = @_;
  67. my $st;
  68. # warn("run '" . join(' ', @args) . "' as $self->{user}\@$self->{server}\n");
  69. $self->open(Host => $self->{server}, User => $self->{user});
  70. $self->{ips}->use_library("IO", qw( open pclose getline ));
  71. # IPC::PerlSSH 'store' breaks for old perl, so we circumvent it below.
  72. # but lets ensure it works. To be expanded with compatible versions.
  73. throw WebfaceSystemError(-text => "IPC::PerlSSH incompatible version") unless ($IPC::PerlSSH::VERSION eq '0.16');
  74. # IPC::PerlSSH open pipe breaks for old perl, this is more portable.
  75. # (and actually more elegant!)
  76. $self->{ips}->eval('
  77. $stored_procedures{"popen"}= sub {
  78. use IO::Pipe;
  79. my $fh=new IO::Pipe();
  80. $fh->reader(@_);
  81. $fh->autoflush;
  82. $IPC::PerlSSH::Library::IO::handles{$fh->fileno}=$fh;
  83. return $fh->fileno;
  84. }
  85. ');
  86. $self->{ips}->{stored}{popen} = 1;
  87. $st = $self->{ips}->eval("chdir('$self->{tmpdir}')")
  88. or throw WebfaceSystemError(-text => "Could not chdir('$self->{tmpdir}') on $self->{server}");
  89. my $fd = $self->{ips}->call('popen', @args);
  90. throw WebfaceSystemError(-text => "Could not popen(@args) on $self->{server}")
  91. if (not defined $fd);
  92. throw WebfaceSystemError(-text => "Could not popen(@args) on $self->{server}")
  93. if ($fd < 0);
  94. while (1) {
  95. my $line;
  96. throw WebfaceSystemError(-text => "Lost connection") unless (defined $self->{ips});
  97. try {
  98. $line = $self->{ips}->call("getline", $fd);
  99. }
  100. catch WebfaceSystemError with {
  101. my $e = shift;
  102. throw WebfaceSystemError(-text => "Interrupted");
  103. }
  104. otherwise {
  105. # Unfortunately readline does not return empty as
  106. # stated in the manual, but throws an exception at EOF
  107. };
  108. last unless (defined $line);
  109. last if ($line eq "");
  110. print STDOUT "$line";
  111. }
  112. throw WebfaceSystemError(-text => "Lost connection to $self->{server}") unless exists $self->{ips};
  113. # warn("$self->{server}: closing\n");
  114. $st = $self->{ips}->call('pclose', $fd);
  115. $st = WEXITSTATUS($st);
  116. $self->close();
  117. return $st;
  118. }
  119. # utility =========================================================================
  120. sub testserver
  121. {
  122. my $self = shift;
  123. my $serv = shift;
  124. my $st;
  125. if ($serv eq $self->{host}) {
  126. warn("test $serv (local)");
  127. $st = chdir($self->{tmpdir});
  128. } else {
  129. try {
  130. warn("test $self->{user}\@$serv (remote) : chdir('$self->{tmpdir}')\n");
  131. my $ips = IPC::PerlSSH->new(Host => $serv, User => $self->{user});
  132. throw WebfaceSystemError(-text => "Unable to connect") unless $ips;
  133. $st = $ips->eval("chdir('$self->{tmpdir}')");
  134. throw WebfaceSystemError(-text => "Could not chdir('$self->{tmpdir}') on $serv") unless ($st);
  135. }
  136. otherwise {
  137. # silently mark error
  138. my $e = shift;
  139. warn("Exception in test of $serv: $e");
  140. $st = -1;
  141. };
  142. }
  143. warn("testserver($serv) returned '$st'\n");
  144. return $st;
  145. }
  146. sub findserver
  147. {
  148. my $self = shift;
  149. throw WebfaceSystemError(-text => "Unknown source host: $self->{host}") unless exists $resources->{ $self->{host} };
  150. throw WebfaceSystemError(-text => "Source host $self->{host} has no way to execute architecture $self->{arch}")
  151. unless exists $resources->{ $self->{host} }->{ $self->{arch} };
  152. my @serv = @{ $resources->{ $self->{host} }->{ $self->{arch} } };
  153. my $retries = $self->{retries};
  154. while ($retries) {
  155. # choose server
  156. foreach my $serv (@serv) {
  157. if ($self->testserver($serv) == 1) {
  158. return $serv;
  159. }
  160. }
  161. warn("zzz $self->{sleeptime} / $retries");
  162. sleep($self->{sleeptime});
  163. $retries--;
  164. }
  165. throw WebfaceSystemError(-text => "Unable to locate online resource (" . join(',', @serv) . ")");
  166. return undef;
  167. }
  168. sub open
  169. {
  170. my $self = shift;
  171. $self->{ips} = IPC::PerlSSH->new(@_, SshOptions => \@sshopt)
  172. or throw WebfaceSystemError(-text => "Cannot connect to $self->{server}");
  173. $self->{pgrp} = $self->{ips}->eval('getpgrp()');
  174. $self->{version} = $self->{ips}->eval('"$] $^X"');
  175. warn("remote perl version: $self->{version}");
  176. }
  177. sub close
  178. {
  179. my $self = shift;
  180. delete $self->{ips};
  181. delete $self->{pgrp};
  182. }
  183. sub interrupt
  184. {
  185. my $self = shift;
  186. $self->kill();
  187. $self->close();
  188. }
  189. sub kill
  190. {
  191. my $self = shift;
  192. try {
  193. if (defined $self->{ips} && defined $self->{pgrp}) {
  194. warn("Closing remote process $self->{pgrp}\@$self->{server}\n");
  195. my $ips = IPC::PerlSSH->new(Host => $self->{server}, User => $self->{user}, SshOptions => \@sshopt);
  196. throw WebfaceSystemError(-text => "Unable to connect") unless $ips;
  197. $ips->eval("kill(-2,$self->{pgrp})");
  198. } else {
  199. warn("No remote proc to kill at $self->{server}\n");
  200. }
  201. }
  202. otherwise {
  203. my $e = shift;
  204. warn("Exception killing remote proc $self->{pgrp}\@$self->{server}: $e");
  205. };
  206. }
  207. # static method
  208. sub args
  209. {
  210. my %opts;
  211. my @args;
  212. my $prog;
  213. while (@_) {
  214. my $opt = shift @_;
  215. if ($opt eq '-wraparch') {
  216. $opts{arch} = shift @_;
  217. } elsif ($opt eq '-wrapprog') {
  218. $prog = shift @_;
  219. } elsif ($opt eq '-wrapserv') {
  220. $opts{server} = shift @_;
  221. } elsif ($opt eq '-wrapuser') {
  222. $opts{user} = shift @_;
  223. } else {
  224. push @args, $opt;
  225. }
  226. }
  227. throw WebfaceConfigError(-text => "-wrapprog needed") unless (defined $prog);
  228. unshift @args, $prog;
  229. throw WebfaceConfigError(-text => "-wraparch or -wrapserv needed")
  230. unless (exists $opts{arch} or exists $opts{server});
  231. throw WebfaceConfigError(-text => "-wraparch OR -wrapserv") if (exists $opts{arch} and exists $opts{server});
  232. $opts{args} = \@args;
  233. return %opts;
  234. }