]> git.uio.no Git - u/mrichter/AliRoot.git/blob - PWGPP/benchmark/alirelval
Merge branch 'master' of https://git.cern.ch/reps/AliRoot
[u/mrichter/AliRoot.git] / PWGPP / benchmark / alirelval
1 #!/bin/bash
2
3 #
4 # alirelval -- by Dario Berzano <dario.berzano@cern.ch>
5 #
6 # Controls the release validation submission by managing the validation virtual
7 # cluster.
8 #
9
10 #
11 # Variables
12 #
13
14 # error codes
15 errCfg=1
16 errMissingCmd=2
17 errEc2Auth=3
18 errInvalidOpt=4
19 errSessionDir=5
20 errCreateKey=6
21 errRunVm=7
22 errLaunchValidation=8
23 errSshNotReady=9
24 errStatusUnavailable=10
25 errPickSession=11
26 errCopyKey=12
27 errAttachScreen=13
28 errRecycleSession=14
29
30 # error codes not treated as errors (100 to 140)
31 errStatusRunning=100
32 errStatusNotRunning=101
33 errStatusDoneOk=102
34 errStatusDoneFail=103
35
36 # thresholds
37 maxVmLaunchAttempts=100
38 maxSshConnectAttempts=400
39 maxVmAddressWait=200
40
41 # working directory prefix
42 sessionPrefix="$HOME/.alice-release-validation"
43
44 # screen name: <screenPrefix>-<sessionTag>
45 screenPrefix='AliRelVal'
46
47 # program name
48 Prog=$(basename "$0")
49
50 #
51 # Functions
52 #
53
54 # Pretty print
55 function pr() {
56   local nl
57   if [ "$1" == '-n' ] ; then
58     nl="-n"
59     shift
60   fi
61   echo $nl -e "\033[1m$@\033[m" >&2
62 }
63
64 # Nice date in UTC
65 function ndate() {
66   date -u +%Y%m%d-%H%M%S-utc
67 }
68
69 # Temporary file
70 function tmpf() {
71   mktemp /tmp/alirelval-XXXX
72 }
73
74 # Swallow output. Show only if something goes wrong
75 function swallow() {
76   local tout ret
77   tout=$(tmpf)
78   "$@" > "$tout" 2>&1
79   ret=$?
80   if [ $ret != 0 ] ; then
81     pr "Command failed (exit status: $ret): $@"
82     cat "$tout" >&2
83   fi
84   rm -f "$tout"
85   return $ret
86 }
87
88 # Launch a VM. Create the keypair if the given keyfile does not exist. Syntax:
89 #
90 #   RunVM <image_id> <profile> <user_data> <key_name> <key_file>
91 #
92 # Returns 0 on success, nonzero on failure. IP address is returned on stdout.
93 function RunVM() {
94   local imageId profile userData keyName
95   imageId="$1"
96   profile="$2"
97   userData="$3"
98   keyName="$4"
99   keyFile="$5"
100   local raw iip iid ret attempt createdKeypair error
101
102   # keypair part: if file does not exist, create keypair
103   if [ ! -e "$keyFile" ] ; then
104     pr "Creating a new keypair: $keyName (private key: $keyFile)"
105     swallow euca-create-keypair -f "$keyFile" "$keyName"
106     if [ $? != 0 ] ; then
107       pr 'Problems creating the keypair'
108       return $errCreateKey
109     fi
110     createdKeypair=1
111   fi
112
113   attempt=0
114   pr 'Attempting to run virtual machine'
115
116   # resubmit loop
117   while true ; do
118
119     if [ $((++attempt)) -gt $maxVmLaunchAttempts ] ; then
120       pr " * Reached maximum number of attempts, giving up"
121       if [ "$createdKeypair" == 1 ] ; then
122         ( euca-delete-keypair "$keyName" ; rm -f "$keyFile" ) > /dev/null 2>&1
123       fi
124       return $errRunVm
125     elif [ $attempt != 1 ] ; then
126       pr " * Pausing between retries"
127       sleep 5
128     fi
129
130     pr -n " * Launching VM (attempt #$attempt/$maxVmLaunchAttempts)..."
131     error=0
132
133     raw=$( euca-run-instances "$imageId" -t "$profile" -d "$userData" -k "$keyName" 2>&1 )
134     ret=$?
135     iid=$( echo "$raw" | egrep '^INSTANCE' | head -n1 | awk '{ print $2 }' )
136     if [ $ret != 0 ] || [ "$iid" == '' ] ; then
137       # 'hard' error, but can be temporary
138       pr 'error: message follows'
139       echo "$raw" >&2
140       sleep 1
141       continue
142     else
143       pr 'ok'
144     fi
145
146     pr " * VM has instance ID $iid"
147     pr -n " * Waiting for IP address..."
148
149     # wait for address loop
150     iip=''
151     for ((i=0; i<$maxVmAddressWait; i++)) ; do
152       sleep 1
153       raw=$( euca-describe-instances 2>&1 | grep -E '^INSTANCE' | grep "$iid" | head -n1 )
154
155       # error state?
156       echo "$raw" | grep -i error -q
157       if [ $? == 0 ] ; then
158         pr ; pr " * VM went to error state"
159         error=1
160         break
161       fi
162
163       # no error: try to parse address (NOTE: only IPv4 for the moment)
164       iip=$( echo "$raw" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' )
165       if [ "$iip" != '' ] ; then
166         pr
167         break
168       fi
169
170       # no address
171       pr -n '.'
172
173     done
174
175     # do we have address?
176     if [ "$iip" != '' ] ; then
177       pr " * VM has address $iip"
178       break
179     fi
180
181     # we don't: terminate (timeout)
182     [ "$error" != 1 ] && pr 'timeout'
183     pr " * Terminating instance $iid"
184     euca-terminate-instances "$iid" > /dev/null 2>&1
185
186   done
187
188   # success
189   [ "$createdKeypair" == 1 ] && euca-delete-keypair "$keyName" > /dev/null 2>&1
190   echo "$iid $iip" # must be parsed
191   return 0
192
193 }
194
195 # Prepare the validation session directory. Syntax:
196 #
197 #   PrepareSession <aliroot_tag>
198 #
199 # Returns 0 on success, nonzero on failure. Session tag returned on stdout.
200 function PrepareSession() {
201   local aliRootTag sessionTag sessionDir
202   aliRootTag="$1"
203   shift
204   sessionTag="${aliRootTag}_$(ndate)"
205   sessionDir="$sessionPrefix/$sessionTag"
206
207   # session directory already exists? abort
208   if [ -d "$sessionDir" ] ; then
209     pr "Session directory already exists, aborting"
210     return $errSessionDir
211   fi
212
213   # create working directory
214   mkdir -p "$sessionDir"
215   if [ $? != 0 ] ; then
216     pr "Fatal: cannot create session directory $sessionDir"
217     return $errSessionDir
218   fi
219
220   # aliroot version written to a file
221   echo "$aliRootTag" > "$sessionDir/aliroot-version.txt"
222
223   # benchmark script, configuration and file list
224   cp benchmark.sh benchmark.config files.list "$sessionDir/"
225   if [ $? != 0 ] ; then
226     pr "Cannot copy benchmark configuration and script to $sessionDir"
227     return $errSessionDir
228   fi
229
230   # append local files to the configuration
231   for f in benchmark.config.d/*.config ; do
232     [ ! -e "$f" ] && continue
233     ( echo ''
234       echo "### from $f ###"
235       cat $f
236       echo ''
237     ) >> "$sessionDir/benchmark.config"
238   done
239
240   # command-line options override the configuration
241   if [ $# != 0 ] ; then
242     pr "Note: the following command-line options will override the corresponding ones in the config files:"
243     ( echo ''
244       echo "### from the command line ###"
245       while [ $# -gt 0 ] ; do
246         extraName="${1%%=*}"
247         extraVal="${1#*=}"
248         if [ "$extraName" != "$1" ] ; then
249           pr " * $extraName = $extraVal"
250           echo "$1"
251         fi
252         shift
253       done
254       echo ''
255     ) >> "$sessionDir/benchmark.config"
256   fi
257
258   # success: return the session tag and move to the session directory
259   pr "*** Creating new working session: $sessionTag ***"
260   pr "*** Use this name for future session operations ***"
261   echo "$sessionTag"
262   return 0
263 }
264
265 # Undo the previous action
266 function PrepareSession_Undo() {
267   rm -rf "$sessionPrefix/$1"
268 }
269
270 # Recycle the VM from an existing session
271 function RecycleSession() {
272   local sessionTag="$1"
273   local fromSessionTag="$2"
274   local fromSessionDir="$sessionPrefix/$fromSessionTag"
275   local f
276
277   for f in 'instance-id.txt' 'instance-address.txt' 'key.pem' ; do
278     cp "$fromSessionDir/$f" "$f" > /dev/null 2>&1
279     if [ $? != 0 ] ; then
280       pr "Cannot copy $f from the source session dir $fromSessionDir"
281       return $errRecycleSession
282     fi
283   done
284
285   return 0
286 }
287
288 # Move into the session tag directory. Usage:
289 #
290 #   MoveToSessionDir <session_tag>
291 #
292 # Returns 0 on success, nonzero on error.
293 function MoveToSessionDir() {
294   originalWorkDir="$PWD"
295   cd "$sessionPrefix/$sessionTag" || return $errSessionDir
296   return 0
297 }
298
299 # Undo the previous action
300 function MoveToSessionDir_Undo() {
301   cd "$originalWorkDir"
302 }
303
304 # Load the benchmark configuration
305 function LoadConfig() {
306   source benchmark.config > /dev/null 2>&1
307   if [ $? != 0 ] ; then
308     pr "Cannot load benchmark configuration"
309     return $errCfg
310   fi
311   return 0
312 }
313
314 # Instantiate the validation VM
315 function InstantiateValidationVM() {
316   local sessionTag instanceId instanceIp ret raw
317   sessionTag="$1"
318
319   # check if we already have a vm
320   instanceId="$(cat instance-id.txt 2> /dev/null)"
321   if [ "$instanceId" != '' ] ; then
322     pr "Virtual machine $instanceId is already running"
323     return 0 # consider it a success
324   else
325     rm -f instance-id.txt instance-address.txt
326   fi
327
328   # do we need to create a keypair?
329   if [ "$cloudKeyName" == '' ] ; then
330     pr "Note: temporary SSH keys will be created for this VM"
331     cloudKeyName="$sessionTag"
332     cloudKeyFile="$PWD/key.pem"
333     rm -f "$cloudKeyFile"
334   elif [ -e "$cloudKeyFile" ] ; then
335     # copy key to session dir
336     pr -n "Copying private key $cloudKeyFile to session directory..."
337     rm -f 'key.pem'
338     cp "$cloudKeyFile" 'key.pem' 2> /dev/null
339     if [ $? != 0 ] ; then
340       pr 'error'
341       return $errCopyKey
342     else
343       pr 'ok'
344     fi
345     cloudKeyFile="$PWD/key.pem"
346   else
347     pr "Cannot find private key to access virtual machines: $cloudKeyFile"
348     return $errCopyKey
349   fi
350
351   # launch virtual machine and get its address
352   raw=$( RunVM "$cloudImageId" "$cloudProfile" "$cloudUserData" "$cloudKeyName" "$cloudKeyFile" )
353   ret=$?
354
355   if [ $ret == 0 ] ; then
356     instanceId=$( echo $raw | cut -d' ' -f1 )
357     instanceIp=$( echo $raw | cut -d' ' -f2 )
358
359     # write both parameters to files
360     echo $instanceId > 'instance-id.txt'
361     echo $instanceIp > 'instance-address.txt'
362   fi
363
364   return $ret
365 }
366
367 # Undo the previous action
368 function InstantiateValidationVM_Undo() {
369   local sessionTag
370   sessionTag="$1"
371   if [ -e 'instance-id.txt' ] ; then
372     swallow euca-terminate-instances $(cat instance-id.txt)
373     if [ $? == 0 ] ; then
374       rm -f instance-id.txt instance-address.txt key.pem
375     fi
376   fi
377 }
378
379 # Generic SSH function to the VM
380 function VMSSH() {
381   local instanceIp sshParams ret
382   instanceIp=$(cat instance-address.txt 2> /dev/null)
383   sshParams="-oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no -oPasswordAuthentication=no -i $PWD/key.pem"
384
385   if [ "$1" == '--rsync-cmd' ] ; then
386     shift
387     echo ssh $sshParams "$@"
388     ret=0
389   else
390     ssh $sshParams "$cloudUserName"@"$instanceIp" "$@"
391     ret=$?
392   fi
393   return $ret
394 }
395
396 # Opens a shell to the remote VM
397 function Shell() {
398   local sessionTag
399   sessionTag="$1"
400   VMSSH
401 }
402
403 # Checks status of the validation
404 function Status() {
405   local raw ret screen exitcode sessionTag
406   sessionTag="$1"
407   raw=$( VMSSH -t "screen -ls 2> /dev/null | grep -q .${screenPrefix}-${sessionTag} && echo -n 'screen_yes ' || echo -n 'screen_no ' ; cat $sessionTag/validation.done 2> /dev/null || echo 'not_done' ; true" 2> /dev/null )
408   raw=$( echo "$raw" | tr -cd '[:alnum:]_ ' ) # garbage removal
409   ret=$?
410
411   if [ "$ret" != 0 ] ; then
412     pr "Cannot get status"
413     return $errStatusUnavailable
414   fi
415
416   screen="${raw%% *}"
417   exitcode="${raw#* }"
418
419   if [ "$screen" == 'screen_yes' ] ; then
420     pr 'Status: validation still running'
421     return $errStatusRunning
422   else
423     if [ "$exitcode" == 'not_done' ] ; then
424       pr 'Status: validation not running'
425       return $errStatusNotRunning
426     elif [ "$exitcode" == 0 ] ; then
427       pr 'Status: validation completed successfully'
428       return $errStatusDoneOk
429     else
430       pr "Status: validation finished with errors (exitcode: $exitcode)"
431       return $errStatusDoneFail
432     fi
433   fi
434
435 }
436
437 # Wait for host to be ready
438 function WaitSsh() {
439   local attempt error
440   attempt=0
441   pr -n 'Waiting for the VM to accept SSH connections...'
442
443   while ! VMSSH -Tq true > /dev/null 2>&1 ; do
444     if [ $((++attempt)) -gt $maxSshConnectAttempts ] ; then
445       pr 'timeout'
446       error=1
447       break
448     fi
449     pr -n '.'
450     sleep 3
451   done
452
453   [ "$error" == 1 ] && return $errSshNotReady
454   pr 'ok'
455   return 0
456 }
457
458 # Run the validation
459 function Validate() {
460   local instanceIp sshParams sessionTag
461   sessionTag="$1"
462   instanceIp=$(cat instance-address.txt 2> /dev/null)
463   sshParams="-oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no -oPasswordAuthentication=no -i $PWD/key.pem"
464
465   # create helper script to launch benchmark
466   cat > run-benchmark.sh <<_EoF_
467 #!/bin/bash
468 cd \$(dirname "\$0")
469 v=validation.done
470 rm -f \$v
471 env ALIROOT_VERSION=$(cat aliroot-version.txt) ./benchmark.sh run $sessionTag files.list benchmark.config
472 #sleep 1000
473 ret=\$?
474 echo \$ret > \$v
475 echo ; echo ; echo
476 echo "*** Validation finished with exitcode \$ret ***"
477 echo ; echo ; echo
478 read -p 'Press ENTER to dismiss: automatic dismiss in 60 seconds...' -t 60
479 _EoF_
480   chmod +x run-benchmark.sh
481
482   # transfer files
483   pr 'Transferring files to the VM'
484   rsync -av -e "$(VMSSH --rsync-cmd)" $PWD/ $cloudUserName@$instanceIp:$sessionTag/ || return $errLaunchValidation
485
486   # open a screen that does something; note that the command is not executed if
487   # the screen already exists, which is what we want
488   # note: sleep necessary to avoid "dead" screens
489   VMSSH -t "screen -wipe > /dev/null 2>&1 ; if screen -ls | grep -q ${screenPrefix}-${sessionTag} ; then ret=42 ; else screen -dmS ${screenPrefix}-${sessionTag} $sessionTag/run-benchmark.sh ; ret=0 ; sleep 3 ; fi ; exit \$ret"
490   ret=$?
491
492   # message
493   if [ $ret == 42 ] ; then
494     pr 'Validation already running inside a screen.'
495   else
496     pr 'Validation launched inside a screen.'
497   fi
498
499   pr
500   pr 'Check the progress status with:'
501   pr "  $Prog --session $sessionTag --status"
502   pr 'Attach to the screen for debug:'
503   pr "  $Prog --session $sessionTag --attach"
504   pr 'Open a shell to the virtual machine:'
505   pr "  $Prog --session $sessionTag --shell"
506   pr
507
508   # ignore ssh errors
509   return 0
510 }
511
512 # Attach current validation screen, if possible
513 function Attach() {
514   local sessionTag
515   sessionTag="$1"
516
517   VMSSH -t "( screen -wipe ; screen -rx ${screenPrefix}-${sessionTag} ) > /dev/null 2>&1"
518
519   if [ $? != 0 ] ; then
520     pr "Cannot attach screen: check if validation is running with:"
521     pr "  $Prog --session $sessionTag --status"
522     pr "or connect manually to the VM for debug:"
523     pr "  $Prog --session $sessionTag --attach"
524     return $errAttachScreen
525   fi
526
527   return 0
528 }
529
530 # Pick session interactively
531 function PickSession() {
532   local sessionTag sess listSessions mess
533   mess="$1"
534   listSessions=()
535   mkdir -p "$sessionPrefix"
536
537   while read sess ; do
538     [ ! -d "$sessionPrefix/$sess" ] && continue
539     listSessions+=( $sess )
540   done < <( cd $sessionPrefix ; ls -1t )
541
542   if [ ${#listSessions[@]} == 0 ] ; then
543     pr "No session available in session directory $sessionPrefix"
544     return $errPickSession
545   fi
546
547   # print user message if provided
548   [ "$mess" != '' ] && pr "$mess"
549
550   pr 'Available sessions (most recent first):'
551   for ((i=0; i<${#listSessions[@]}; i++)) ; do
552     pr "$( printf "  % 2d. ${listSessions[$i]}" $((i+1)) )"
553   done
554   pr -n 'Pick one: '
555   read i
556
557   let i--
558   if [ "$i" -lt 0 ] || [ "${listSessions[$i]}" == '' ] ; then
559     pr 'Invalid session'
560     return $errPickSession
561   fi
562
563   sess="${listSessions[$i]}"
564   pr "You chose session $sess"
565   echo $sess
566   return 0
567 }
568
569 # Run an action
570 function RunAction() {
571   local ret
572   type "$1" > /dev/null 2>&1
573   if [ $? == 0 ] ; then
574     #pr "--> $1 (wd: $PWD)"
575     eval "$@"
576     ret=$?
577     #pr "<-- $1 (ret: $ret, wd: $PWD)"
578     return $ret
579   fi
580   return 0
581 }
582
583 # Print help screen
584 function Help() {
585   pr "$Prog -- by Dario Berzano <dario.berzano@cern.ch>"
586   pr 'Controls the Release Validation workflow on the cloud for AliRoot.'
587   pr
588   pr "Usage 1: $Prog [--prepare|--launch|--recycle] [--from-session] --aliroot <aliroot_tag> [-- arbitraryOpt1=value [arbitraryOpt2=value2...]]"
589   pr
590   pr 'A new session is created to validate the specified AliRoot tag.'
591   pr
592   pr '  --prepare  : prepares the session directory containing the files needed'
593   pr '               for the validation'
594   pr '  --recycle  : prepares a new session by recycling the head node from an'
595   pr '               existing one. Source session is specified via the'
596   pr '               --from-session switch or it can be interactively selected'
597   pr '  --launch   : launches the full validation process: prepares session,'
598   pr '               runs the virtual machine, launches the validation program'
599   pr '  --aliroot  : the AliRoot tag to validate, in the form "vAN-20140610"'
600   pr
601   pr 'Arbitrary options (in the form variable=value) can be specified after the'
602   pr 'double dash and will override the corresponding options in any of the'
603   pr 'configuration files.'
604   pr ; pr
605   pr "Usage 2: $Prog [--runvm|--validate|--shell|--status] --session <session_tag>"
606   pr
607   pr 'Runs the validation step by step after a session is created with'
608   pr '--prepare, and runs other actions on a certain session.'
609   pr
610   pr '  --session  : session identifier, e.g. vAN-20140610_20140612-123047-utc:'
611   pr '               if no session is specified an interactive prompt is'
612   pr '               presented'
613   pr '  --runvm    : instantiates the head node of the validation cluster on'
614   pr '               the cloud' 
615   pr '  --validate : runs the validation script on the head node for the'
616   pr '               current session. Head node must be already up, or it'
617   pr '               should be created with --runvm. If validation is running'
618   pr '               already, connects to the existing validation shell'
619   pr '  --attach   : attach a currently running validation screen; remember to'
620   pr '               detach with Ctrl+A+D (and *not* Ctrl-C)'
621   pr '  --shell    : does SSH on the head node'
622   pr '  --status   : returns the status of the validation'
623   pr ; pr
624   pr 'Example 1: run the validation of AliRoot tag vAN-20140610:'
625   pr
626   pr "  $Prog --aliroot vAN-20140610 --launch"
627   pr
628   pr 'Example 2: do the same thing step-by-step:'
629   pr
630   pr "  $Prog --aliroot vAN-20140610 --prepare"
631   pr "  $Prog --runvm"
632   pr "  $Prog --validate"
633   pr
634 }
635
636 # The main function
637 function Main() {
638
639   # local variables
640   local Args aliRootTag EnterShell Actions sessionTag fromSessionTag
641   Actions=()
642
643   # parse command line options
644   while [ $# -gt 0 ] ; do
645     case "$1" in
646
647       # options
648       --aliroot|-a)
649         aliRootTag="$2"
650         shift 2
651       ;;
652       --session)
653         sessionTag="$2"
654         shift 2
655       ;;
656       --from-session)
657         fromSessionTag="$2"
658         shift 2
659       ;;
660
661       # actions
662       --launch)
663         # all actions
664         Actions=( PrepareSession MoveToSessionDir LoadConfig InstantiateValidationVM WaitSsh Validate )
665         shift
666       ;;
667       --prepare)
668         Actions=( PrepareSession MoveToSessionDir )
669         shift
670       ;;
671       --recycle)
672         Actions=( PrepareSession MoveToSessionDir RecycleSession )
673         shift
674       ;;
675       --runvm)
676         Actions=( MoveToSessionDir LoadConfig InstantiateValidationVM )
677         shift
678       ;;
679       --validate)
680         Actions=( MoveToSessionDir LoadConfig WaitSsh Validate )
681         shift
682       ;;
683       --attach)
684         Actions=( MoveToSessionDir LoadConfig WaitSsh Attach )
685         shift
686       ;;
687
688       # extra actions
689       --shell)
690         Actions=( MoveToSessionDir LoadConfig WaitSsh Shell )
691         shift
692       ;;
693       --status)
694         Actions=( MoveToSessionDir LoadConfig WaitSsh Status )
695         shift
696       ;;
697       --help)
698         Help
699         exit 0
700       ;;
701
702       # end of options
703       --)
704         shift
705         break
706       ;;
707
708       *)
709         pr "Invalid option: $1. Use --help for assistance."
710         return $errInvalidOpt
711       ;;
712     esac
713   done
714
715   # check for the presence of the required tools in the $PATH
716   for T in euca-describe-instances euca-describe-regions euca-run-instances euca-create-keypair euca-delete-keypair rsync ; do
717     which "$T" > /dev/null 2>&1
718     if [ $? != 0 ] ; then
719       pr "Cannot find one of the required commands: $T"
720       return $errMissingCmd
721     fi
722   done
723
724   # test EC2 credentials
725   # euca-describe-regions > /dev/null 2>&1
726   # if [ $? != 0 ] ; then
727   #   pr 'Cannot authenticate to EC2.'
728   #   pr 'Note: you must have at least the following variables properly set in your environment:'
729   #   pr "  * EC2_URL (current value: ${EC2_URL-<not set>})"
730   #   pr "  * EC2_ACCESS_KEY (current value: ${EC2_ACCESS_KEY-<not set>})"
731   #   pr "  * EC2_SECRET_KEY (current value: ${EC2_SECRET_KEY-<not set>})"
732   #   return $errEc2Auth
733   # fi
734
735   # what to do?
736   if [ ${#Actions[@]} == 0 ] ; then
737     pr 'Nothing to do. Use --help for assistance.'
738     return $errInvalidOpt
739   fi
740
741   # run actions
742   for ((i=0; i<${#Actions[@]}; i++)) ; do
743
744     A=${Actions[$i]}
745
746     if [ "$A" == 'PrepareSession' ] ; then
747       # special action returning the session tag
748       if [ "$aliRootTag" == '' ] ; then
749         pr 'Specify an AliRoot version with --aliroot <tag>'
750         return $errInvalidOpt
751       fi
752       if [ "$sessionTag" != '' ] ; then
753         pr 'Cannot use --session with --prepare. Use --help for assistance.'
754         return $errInvalidOpt
755       fi
756       sessionTag=$( RunAction "$A" "$aliRootTag" "$@" )
757       ret=$?
758     elif [ "$A" == 'RecycleSession' ] ; then
759       # special action requiring additional parameters
760       if [ "$fromSessionTag" == '' ] ; then
761         fromSessionTag=$( PickSession 'Select a source session to recycle.' )
762         ret=$?
763         [ $ret != 0 ] && break
764       fi
765       RunAction "$A" "$sessionTag" "$fromSessionTag"
766       ret=$?
767     else
768       if [ "$sessionTag" == '' ] ; then
769         sessionTag=$( PickSession )
770         ret=$?
771         [ $ret != 0 ] && break
772       fi
773       RunAction "$A" "$sessionTag"
774       ret=$?
775     fi
776
777     # 100 to 140 --> not errors
778     ( [ $ret != 0 ] && ( [ $ret -ge 100 ] || [ $ret -le 140 ] ) ) && break
779
780   done
781
782   # undo actions
783   let i--
784   if [ $ret != 0 ] && ( [ $ret -ge 100 ] || [ $ret -le 140 ] ) ; then
785     for ((; i>=0; i--)) ; do
786       RunAction "${Actions[$i]}_Undo" "$sessionTag"
787     done
788   fi
789
790   # return last value
791   return $ret
792
793 }
794
795 #
796 # Entry point
797 #
798
799 Main "$@" || exit $?