/usr/share/arc/submit-condor-job is in nordugrid-arc-arex 1.1.1-1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | #!/usr/bin/perl -w
use File::Spec;
use File::Temp 'tempfile';
use File::Basename;
use Sys::Hostname;
use Cwd;
BEGIN {
my $oldcwd = getcwd();
my $basedir = dirname($0);
chdir $basedir or die "$basedir: $!";
$basedir = getcwd();
my $pkgdatadir = $basedir;
chdir $oldcwd or die "$oldcwd: $!";
unshift @INC, $pkgdatadir;
}
use ConfigCentral;
use condor_env;
use strict;
use warnings;
# True if the program is run in debugging mode.
my $debug;
# The name of the Condor job description file. This file is generated from the
# GRAMI file.
my $cmd_filename;
# Pathname of the Condor log.
my $condor_log;
# Pathname of the real executable.
my $real_exe;
# Pathname of the wrapper script.
my $exewrapper;
my %grami;
$0 =~ s#.*/##;
warn "----- starting $0 -----\n";
if (@ARGV > 1 && $ARGV[0] eq '--config') {
$ENV{ARC_CONFIG} = $ARGV[1];
shift;
shift;
}
if (@ARGV > 0 && $ARGV[0] eq '-d') {
$debug = 1;
shift;
}
die "Usage: $0 [--config CONFIG_FILE] [-d] GRAMI_FILE\n" unless @ARGV;
parse_grami(my $gramifile = $ARGV[0]);
$grami{joboption_controldir} ||= dirname($gramifile) || getwd();
$grami{joboption_gridid} ||= $1 if basename($gramifile) =~ /job\.(.*)\.grami$/;
my $configfile = $ENV{ARC_CONFIG} ? $ENV{ARC_CONFIG} : '/etc/arc.conf';
my $fullconfig = ConfigCentral::parseConfig($configfile);
my $share = $grami{joboption_queue};
die "$0: ERROR: Job requested invalid share: $share\n"
unless exists $fullconfig->{shares}{$share};
my %config = ( %$fullconfig, %{$fullconfig->{shares}{$share}} );
# this finds location of condor executables, and sets up environment vars.
configure_condor_env(%config) or die "Condor executables not found\n";
my $condor_bin_path = $ENV{CONDOR_BIN_PATH};
$ENV{RUNTIME_CONFIG_DIR} = $config{runtimedir} || '.'; # avoid undefined warning
if ($debug) {
# Use a bogus name for the logfile if debugging -- it doesn't matter.
$condor_log = 'job.log';
} else {
$condor_log = File::Temp::tempnam($grami{joboption_directory}, 'log.');
}
run_rte0();
create_shell_wrapper();
create_condor_job_description();
submit_condor_job();
warn "$0: job submitted successfully\n",
"----- exiting $0 -----\n";
exit;
##############################################################################
## Function Definitons
##############################################################################
sub parse_grami {
local @ARGV = $_[0];
warn "$0: ----- begin grami file ($_[0]) -----\n";
while (my $line = <>) {
chomp $line;
# Dump every line of the grami file into the log.
warn "$0: $line\n";
my ($name, $value) = split /=/, $line, 2;
next if !$name;
# Remove outer layer of single quotes. Backslash escaped single quotes
# are stripped of their backslashes, and strings protected by single
# quotes are stripped of the single quotes. This is supposed to work
# exactly like Bourne shell quote removal:
#
# foo'bar' --> foobar
# foo\''bar'\' --> foo'bar'
#
{
no warnings 'uninitialized';
$value =~ s/(?:\\('))?'([^']*)'(?:\\('))?/$1$2$3/g;
}
# The variable names are case insensitive, so lowercase them and
# remember to always refer to them by their lowercase names!
$grami{lc $name} = $value;
}
warn "$0: ----- end grami file ($_[0]) -----\n";
}
#
# Run RTE stage 0 scripts. Also update %grami hash to match the variables set by the
# scripts (but only those beginnig with joboption_)
#
sub run_rte0 {
for (my $i = 0; my $r = $grami{"joboption_runtime_$i"}; $i++) {
# Shell script which will source RTE scripts
my $script = '';
# construct randon string to use as separator
my @chars = ("0".."9","A".."Z");
my $sep = "----";
$sep .= $chars[rand(@chars)] for 1..16;
$sep .= "----";
# initialize joboption_* variables
$script .= "$_=\Q$grami{$_}\E\n" for (grep /^joboption_[A-Za-z0-9_]+$/, keys %grami);
# source RTE script
$script .= ". \Q$ENV{RUNTIME_CONFIG_DIR}/$r\E 0 1>&2\n";
$script .= "ret=\$?\n";
# print joboption_* variables to stdout. First export them, then use perl for printing.
$script .= 'export $(set | sed -n "s/^\(joboption_[A-Za-z0-9_]*\)=.*/\1/p")';
$script .= "\n/usr/bin/perl -we 'print \"\$_=\$ENV{\$_}$sep\" for grep /^joboption_[A-Za-z0-9_]+\$/, keys \%ENV'\n";
$script .= "echo STATUS=\$ret\n";
# write script to temporary file and run it.
my $dir = $ENV{TMPDIR} || File::Spec->tmpdir;
my ($fh, $fname) = tempfile('condor_XXXXXXXX', DIR => $dir);
print $fh $script or die "$0: write $fname: $!\n";
close $fh or die "$0: close $fname: $!\n";
my $output = `/bin/sh $fname`;
unlink $fname;
# is output complete?
if (not $output =~ m/^(?:joboption_\w+=.*?$sep)+STATUS=(\d+)$/s) {
die "$0: ERROR: RTE script $r exited unexpectedly\n";
}
delete $grami{$_} for grep /^joboption_/, keys %grami;
$grami{$1} = $2 while ($output =~ m/(\w+)=(.*?)$sep/sg);
}
}
# This function extracts the first string from each line of the file given as
# argument. Handles files like job.*.output which contain two strings
# separated by a space on each line. The second string can be emty. Spaces
# and backslashes within the strings are escaped with a backslash. See the
# FileData class in src/services/a-rex/grid-manager/files/info_types.cpp for
# the c++ implementation.
# OBS: The same can be achieved simpler with the shell built-in 'read'
sub get_outputfiles {
my $file = shift;
my @names = ();
open(F, "<$file") or die "$file: $!\n";
while (<F>) {
chomp;
my @chars = split "";
my $esc = 0;
my $str = "";
for (my $curr = shift @chars; defined $curr; $curr = shift @chars) {
if ($esc) {
$esc = 0;
$str .= $curr;
next;
}
last if $curr eq " ";
$str .= $curr unless $esc = ($curr eq "\\");
}
push @names, $str;
}
return @names;
}
#
# Creates a shell script that:
#
# (1) Sources the runtime scripts with argument "0" before evaluating the job
# executable. (This is in case the job refers to variables set by the
# runtime scripts.) TODO: should variables be expanded in
# joboption_runtime_0?
#
# (2) Sources the runtime scripts with argument "1" before running the job.
#
# (3) Runs the job, redirecting output as requested in the xRSL.
#
# (4) Sources the runtime scripts with argument "2" after running the job.
#
# (5) Exits with the value returned by the job executable in step (3).
#
sub create_shell_wrapper {
# Create the shell commands to run runtime environment files (stages 1-2).
my ($setrte1, $setrte2) = ('', '');
if (notnull($grami{joboption_runtime_0})) {
for (my $i = 0; notnull(my $r = $grami{"joboption_runtime_$i"}); $i++) {
$setrte1 .= qq{. "\$RUNTIME_CONFIG_DIR/$r" 1\n};
$setrte2 .= qq{. "\$RUNTIME_CONFIG_DIR/$r" 2\n};
}
}
# Set $real_exe to the path to the job executable (environment variables
# expanded). $exewrapper contains the script which will be submitted to condor.
$real_exe = $grami{joboption_arg_0};
$exewrapper = File::Temp::tempnam($grami{joboption_directory}, "condorjob.sh.");
# Get the name of the stdout file.
my $stdout = notnull($grami{joboption_stdout}) ?
$grami{joboption_stdout} : '/dev/null';
$stdout =~ s{^\Q$grami{joboption_directory}\E/*}{};
# Get the name of the stderr file.
my $stderr = notnull($grami{joboption_stderr}) ?
$grami{joboption_stderr} : '/dev/null';
$stderr =~ s{^\Q$grami{joboption_directory}\E/*}{};
# Start creating the output script. Note that the script is created
# in-memory, instead of being written to file, part by part. This is
# because we want to test for all I/O errors, and having just a single
# write means that there is only one place we have to test for write
# errors.
my $output = "#!/bin/sh\n";
# If the custom RSL attribute 'wrapperdebug' is set, enable command
# tracing (set -x) and list all files in the session directory. (This
# output is sent to stderr.)
if (notnull($grami{joboption_rsl_wrapperdebug})) {
$output .= "set -x\nexec &>\Q$stderr\E\nls -la\n";
}
# set HOME to the working directory of the job
$output .= "HOME=\`pwd\`\nexport HOME\n";
# Overide umask of execution node
$output .= "umask 077\n";
# Source runtime scripts with argument 1.
$output .= $setrte1;
# Enable the executable bit for non-preinstalled executables.
if ($real_exe !~ m{^/}) {
$output .= "eval chmod +x \"\Q$real_exe\E\"\n";
}
# Incomplete job command; arguments may follow.
$output .= "eval \"\Q$real_exe\E\"";
# Add optional arguments to the command line.
if (defined $grami{joboption_arg_1}) {
for (my $i = 1; defined(my $arg = $grami{"joboption_arg_$i"}); $i++) {
$output .= $arg ne '' ? " \"\Q$arg\E\"" : " ''";
}
}
# Redirect stdout/stderr. These variables are always set to something
# (/dev/null if unspecified), so it's safe to unconditionally add these
# redirections.
$output .= " >\Q$stdout\E";
# If we're debugging the wrapper script, we don't do stderr redirection.
if (!notnull($grami{joboption_rsl_wrapperdebug})) {
if ($stdout eq $stderr) {
# We're here if stdout and stderr is redirected to the same file.
# This will happen when (join = yes) in the xRSL.
$output .= ' 2>&1';
} else {
$output .= " 2>\Q$stderr\E";
}
}
# Always a newline to terminate the job command.
# Preserve the job's exit code.
# Run runtime environment files with argument 2.
$output .= "\n_exitcode=\$?\n$setrte2";
# Now generate code that removes everything but the requested output.
# Note that, bashims have been avoided so that there are less strict
# requirements on /bin/sh on the execute nodes. (Note that the file
# utilities used (mkdir, dirname, find, etc.) may still be
# GNU-centric. TODO: fix this if we're to support non-x86-Linux.)
$output .= <<EOF;
find ./ -type l -exec rm -f "{}" ";"
find ./ -type f -exec chmod u+w "{}" ";"
EOF
my $outputlist = "$grami{joboption_controldir}/job.$grami{joboption_gridid}.output";
if (-e $outputlist) {
my @fileslst = grep {$_ ne ""} get_outputfiles($outputlist);
# Add condor_log (with path stripped) to the list of files to keep.
push @fileslst, (my $basename) = $condor_log =~ m{([^/]+)$};
# Remove leading backslashes, if any
s/^\/*// for @fileslst;
# Make it safe for shell by replacing single quotes with '\''
s/'/'\\''/g for @fileslst;
# Protect from deleting output files including those in the dynamic list
for my $file ( @fileslst ) {
if ($file =~ s/^@//) {
$output .= "dynlist='$file'\n";
$output .= <<'EOF'
chmod -R u-w "./$dynlist" 2>/dev/null
cat "./$dynlist" | while read name rest; do
chmod -R u-w "./$name" 2>/dev/null
done
EOF
} else {
$output .= "chmod u-w './$file' 2>/dev/null\n";
}
}
$output .= <<EOF;
find ./ -type f -perm +200 -exec rm -f "{}" ";"
find ./ -type f -exec chmod u+w "{}" ";"
EOF
}
# Exit with the job's exit code.
$output .= "exit \$_exitcode\n";
# Create the actual shell script from $output.
open EXE, ">$exewrapper" or die "$0: creat $exewrapper: $!\n";
print EXE $output or die "$0: write $exewrapper: $!\n";
close EXE or die "$0: close $exewrapper: $!\n";
chmod 0755, $exewrapper or die "$0: chmod $exewrapper: $!\n";
# Log the Condor job submission script in gmlog/errors.
unless ($debug) {
warn "$0: ----- begin wrapper script ($exewrapper) -----\n";
warn "$0: $_\n" for split /\n/, $output;
warn "$0: ----- end wrapper script ($exewrapper) -----\n";
}
}
#
# Create a Condor job description that submits the wrapper script created
# above. The Condor job description should mirror the xRSL as much as
# possible.
#
sub create_condor_job_description {
# As above, the job description is created in-memory, so that only one I/O
# operation has to be done when writing to disk.
my $output = "Executable = $exewrapper\n" .
"Input = $grami{joboption_stdin}\n";
$output .= "Log = $condor_log\n";
$output .= "Output = $grami{joboption_directory}.comment\n";
$output .= "Error = $grami{joboption_directory}.comment\n";
my @requirements = ();
if (notnull($grami{joboption_queue})) {
my $queue = $grami{joboption_queue};
$output .= "+NordugridQueue = $queue\n";
}
if (notnull($config{condor_rank})) {
$output .= "Rank = $config{condor_rank}\n";
}
if (notnull($config{condor_requirements})) {
$config{condor_requirements} =~ s/\[separator\]//g;
push @requirements, $config{condor_requirements};
}
# This is a custom RSL attribute used for debugging. If the xRSL contains
# (machine = foo), the job will only run on machine "foo".
if (notnull($grami{joboption_rsl_machine})) {
push @requirements, "Machine == \"$grami{joboption_rsl_machine}\"";
}
if (@requirements) {
$output .= "Requirements = (" . (join ") && (", @requirements) . ")\n";
}
# Option to force Condor to transfer of input and output files by itself
if ($config{shared_filesystem} =~ /^no/i && notnull($grami{joboption_rsl_inputfiles})) {
chomp($grami{joboption_rsl_inputfiles});
my @tmp = split / /, $grami{joboption_rsl_inputfiles};
$output .= "Transfer_input_files = ";
for (my $i = 0; $i < @tmp; $i += 2) {
$output .= ',' if $i > 0;
$output .= $tmp[$i];
}
$output .= "\n";
$output .= "should_transfer_files = YES\n";
# No need to specify output files explicitly. Condor will transfer all
# files from the job directory that were created or modified by the job
$output .= "When_to_transfer_output = ON_EXIT_OR_EVICT\n";
if (notnull($grami{joboption_rsl_disk})) {
push @requirements, "Disk >= " . ($grami{joboption_rsl_disk} * 1024);
}
}
if (notnull($grami{joboption_env_0})) {
$output .= "Environment = ";
my $has_globalid = '';
my $i;
for ($i = 0; notnull($grami{"joboption_env_$i"}); $i++) {
$output .= ";" if $i > 0;
$output .= $grami{"joboption_env_$i"};
$has_globalid = 1 if $grami{"joboption_env_$i"} =~ m/^GRID_GLOBAL_JOBID=/;
}
# guess globalid in case not already provided and export it to the job
if (not $has_globalid) {
my $hostname = $config{hostname} || hostname();
my $gm_port = $config{gm_port} || 2811;
my $gm_mount_point = $config{gm_mount_point} || "/jobs";
$output .= ";" if $i > 0;
$output .= "GRID_GLOBAL_JOBID=gsiftp://$hostname:$gm_port$gm_mount_point/$grami{joboption_gridid}";
}
$output .= "\n";
}
my $remove="FALSE";
if (notnull($grami{joboption_cputime})) {
$output .= "+JobCpuLimit = $grami{joboption_cputime}\n";
$remove .= " || RemoteUserCpu + RemoteSysCpu > JobCpuLimit";
warn "$0: Setting CPU limit\n";
}
if (notnull($grami{joboption_walltime})) {
$output .= "+JobTimeLimit = $grami{joboption_walltime}\n";
$remove .= " || RemoteWallClockTime > JobTimeLimit";
warn "$0: Setting time limit\n";
}
# Set a default memory limit
$grami{joboption_memory} ||= $config{nodememory} || 1000;
$output .= "+JobMemoryLimit = ".int(1024*$grami{joboption_memory})."\n";
$remove .= " || ImageSize > JobMemoryLimit";
warn "$0: Setting memory limit\n";
$output .= "GetEnv = True\n" .
"Universe = vanilla\n" .
"Notification = Always\n" .
# "When_to_transfer_output = ON_EXIT\n" .
"Periodic_remove = $remove\n" .
"Queue\n";
if ($debug) {
print $output;
} else {
my $cmd_fh;
($cmd_fh, $cmd_filename) = tempfile('XXXXXXXX',
DIR => $grami{joboption_directory},
SUFFIX => '.cmd');
print $cmd_fh $output or die "$0: write $cmd_filename: $!\n";
close $cmd_fh or die "$0: close $cmd_filename: $!\n";
# Log the Condor job submission script in gmlog/errors.
warn "$0: ----- begin condor job description ($cmd_filename) -----\n";
warn "$0: $_\n" for split /\n/, $output;
warn "$0: ----- end condor job description ($cmd_filename) -----\n";
}
}
sub submit_condor_job {
return if $debug;
chdir $grami{joboption_directory}
or die "$0: chdir $grami{joboption_directory}: $!\n";
my $condor_submit_exe = "$condor_bin_path/condor_submit";
warn "$0: running $condor_submit_exe $cmd_filename\n";
my $submit_out = `\Q$condor_submit_exe\E \Q$cmd_filename\E 2>&1`;
#my $submit_out = `\Qcat\E \Q$cmd_filename\E 2>&1; exit 22`;
my $err = $?;
warn "$0: $_\n" for split /\n/, $submit_out;
die "$0: condor_submit failed!\n" if $err;
warn "$0: appending local job id to grami file $gramifile\n";
my ($localid) = $submit_out =~ /submitted to cluster (\d+)\./;
open GRAMI, ">>$gramifile" or die "$0: $gramifile: $!";
print GRAMI "joboption_jobid=$localid.condor\n" or die "$0: $gramifile: $!";
print GRAMI "condor_log=$condor_log\n" or die "$0: $gramifile: $!";
close GRAMI or die "$0: $gramifile: $!";
}
sub notnull {
return defined $_[0] && $_[0] ne '';
}
|