Sottomettere un job ~$ condor_submit job.submit Submitting job(s). 3 job(s) submitted to cluster 128. Vedere lo stato del job in coda (in coda == in attesa + ): $ condor_q -- Schedd: submit-1.chtc.wisc.edu : <128.104.101.92:9618?... @ 05/09/19 10:35:54 OWNER BATCH_NAME SUBMITTED DONE RUN IDLE TOTAL JOB_IDS alice ID: 128 5/9 11:03 _ _ 3 3 128.0-2 3 jobs; 0 completed, 0 removed, 3 idle, 0 running, 0 held, 0 suspended Per vedere info aggregate su un job (sottomesso con queue > 1) condor_q -nobatch -------------- $ condor_q -nobatch -- Schedd: submit-1.chtc.wisc.edu : <128.104.101.92:9618?... ID OWNER SUBMITTED RUN_TIME ST PRI SIZE CMD 128.0 alice 5/9 11:09 0+00:00:00 I 0 0.0 compare_states wi.dat us.dat 1 jobs; 0 completed, 0 removed, 1 idle, 0 running, 0 held, 0 suspended -------------- SUBMIT FILE PARAMETRICO Esempio: si vuole eseguire tre volte analyze.exe, con tre input files differenti, producendo altrettanti output: analyze.exe file1.in file1.ou analyze.exe file1.in file1.ou analyze.exe file1.in file1.ou multijob.sub: ------------ executable = analyze.exe arguments = file$(ProcId).in file$(ProcId).out transfer_input_files = file$(ProcId).in log = job_$(ClusterId)_$(ProcId).log output = job_$(ClusterId)_$(ProcId).out error = job_$(ClusterId)_$(ProcId).err queue 3 ------------ dopo condor_submit multijob.sub HTCondor prooduce un ClusterId e tre ProcId: 0,1,2 che vengono usati per comporre i nomi dei files di log , output e error. TRASFERIRE files: Nel submit file si specifica: transfer_input_files = file1 file2 ... fileN Ma si puo' anche specificare un'intera directory: #trasferisce l'intera directory "miofolder" con il suo contenuto transfer_input_files = miofolder #trasferisce solo i files contenuti in "miofolder": transfer_input_files = miofolder/ NB: le dir devono gia' esistere al momento del submit! Esempio: nel Submit Node: $ ls analyze.exe file1.in file2.in file3.in # creo le dir $ mkdir input log err $ mv file[123].in input/ E sottometto i job, con questo submit file: ------------ executable = analyze.exe arguments = file$(ProcId).in file$(ProcId).out transfer_input_files = input/file$(ProcId).in log = log/job_$(ProcId).log error = err/job_$(ProcId).err queue 3 ------------ SEPARARE I JOB Vogliamo avere dir separate per ciascuna esecuzione: a parte l'eseguibile, vogliamo tutto in una dir. specifica per ciascuna esecuzione: $ pwd /home/sdp/submit_dir/ $ ls job.submit analyze.exe file1.in file2.in file3.in # preparo le 3 dir, una per escuzione, ciascuna con dentro il suo file # di input $ for n in `seq 0 2` ; do mkdir job${n} ; mv file${n} job${n}/file.in ; done # $ cat job.submit executable = analyze.exe initialdir = job$(ProcId) arguments = file.in file.out transfer_input_files = file.in log = job.log error = job.err queue 3 ######### ALTRE FORME PER SUBMIT MULTIPLO PROBLEMA: i nostri files di input non si chiamano file1, file2 ecc. Invece si chiamano pere.in mele.in noci.in frutti.list Nel submit file ci sono 4 modi possibili: # definisco $(infile) per rappresentare i nomi che serviranno... executable = myprogram arguments = $(infile).in frutti.in $(infile).out transfer_input_files = frutti.in $(infile) # E poi... # 1. Usare "queue" tante volte: (DEPRECATO!!!) # multiple queue statement: infile = pere.in queue 1 infile = mele.in queue 1 infile = noci.in queue 1 # 2. Matching Pattern: queue infile matching *.in # in pratica prende tutti i files che si vedono con # ls *.in # e' meglio verificare prima!!! # 3. in queue infile in (pere.in mele.in noci.in) # 4. from file: queue infile from elenco_input_files.txt # Questa forma e' molto versatile: si puo' usare lo stesso submit file per molti batch differenti, basta cambiare il contenuto di elenco_input_files.txt ALTRI ESEMPI: $ cat job.submit executable = myprog.exe arguments = -y $(option) -i $(file) should_transfer_files = YES when_to_transfer_output = ON_EXIT transfer_input_files = $(file) queue file,option from job_list.txt ------------------- # e job list contiene: $ cat job_list.txt wi.dat, 2010 wi.dat, 2015 ca.dat, 2010 ca.dat, 2015 ia.dat, 2010 ia.dat, 2015 # Per riferimenti: # HTCondor Manual: submit file options ALTRE FEATURES. #Match di files o directories: queue input matching files *.dat Oppure: queue directory matching dirs job* #Submit di multipli job con gli stessi input: queue 10 input matching files *.dat AUTOMATION: - Se pochi job ogni tanto falliscono si puo' fare in modo che vadano automaticamente ri-sottomessi: max_retries = 3 - Sottometto migliaia di job ma non voglio "ingolfare il sistema". Limito il max num di job "pending" max_idle = 1000 - I job dovrebbero durare 2h ma alcuni a volte restano in esecuzione "forever". Metto un limite condizionale che li interrompe e li mette in hold: periodic_hold = (JobStatus == 2) && ((CurrentTime - EnteredCurrentStatus) > (60 * 60 * 2)) # JobStatus == 2 --> Running # CurrentTime - EnteredCurrentStatus --> runtime in secondi - i job falliscono ripetutamente. Eliminiamo quelli falliti piu' di 5 volte: periodic_remove = (NumJobsStarts > 5) Alcuni Attributi "popolari": CurrentTime: current time EnteredCurrentStatus: time of last status change ExitCode: the exit code from the job HoldReasonCode: number corresponding to a hold reason NumJobStarts: how many times the job has gone from idle to running JobStatus: number indicating idle, running, ################### Vedere stato e ClassAd dei job $ condor_q -l Mostra elenco completo dei ClassAd (Molto lungo, molti non "interessanti") NOTA: Molti ClassAd possono essere Custom (user defined) e non e' possibile distinguere quelli sono nativi HTC se non cercandoli nel manuale... SELEZIONARE I CLASSAD Esempio per vederne solo alcuni: $ condor_q -af ClusterId ProcId RemoteHost MemoryUsage 1725 116 slot1_1@e092.chtc.wisc.edu 1709 1725 118 slot1_2@e093.chtc.wisc.edu 1709 1861 0 slot1_5@c025.chtc.wisc.edu 196 1863 0 slot1_3@atlas10.chtc.wisc.edu 269 SELEZIONE CONDIZIONALE (constraint) $ condor_q -constraint 'JobBatchName == "CoolJobs"' OWNER BATCH_NAME SUBMITTED DONE RUN IDLE TOTAL JOB_IDS alice CoolJobs 5/9 11:03 _ _ 3 3 128.0-2 Nota: -cons va altrettanto bene, basta rimanga non confondibile $ condor_q -all --> tutti gli utenti tutti i job CLASSAD degli HOST (Worker Nodes, o Nodes) $ condor_status #Valgono le stesse opzioni ( -af , -cons ) Name OpSys Arch State Activity LoadAv Mem Actvty slot1@c001.chtc.wisc.edu LINUX X86_64 Unclaimed Idle 0.000 673 25+01 slot1_1@c001.chtc.wisc.edu LINUX X86_64 Claimed Busy 1.000 2048 0+01 slot1_2@c001.chtc.wisc.edu LINUX X86_64 Claimed Busy 1.000 2048 0+01 slot1_3@c001.chtc.wisc.edu LINUX X86_64 Claimed Busy 1.000 2048 0+00 [...] Total Owner Claimed Unclaimed Matched Preempting Backfill Drain X86_64/LINUX 10962 Total O 0 10340 613 0 0 0 9 X86_64/WINDOWS 2 2 0 0 0 0 0 0 Total 10964 2 10340 613 0 0 0 9 NOTA: Output molto grande su cluster grandi... Per ridurre (un po'): $ condor_status -compact VEDERE JOB CONCLUSI condor_history Praticamente come condor_q, stesse opzioni. Elenca i job finiti "in tempi recenti" (dimentica le cose vecchie oltre alcuni limiti di spazio) TIP: su cluster grandi l'elenco e' molto vasto, conviene usare -limit: Esempi: condor_history -limit 1 -af Owner ExitStatus condor_history -limit 10 -cons 'Owner=="alice"' -af ClusterId JobId ExitStatus ExitCode ############################################################################# ADVANCED SUBMIT TUT. (cfr. https://agenda.hep.wisc.edu/event/1325/session/3/contribution/40/material/slides/0.pptx) Partiamo da un submit file elementare e aggiungiamo cose man mano. Esempio di partenza: collezione di file video da convertire con ffmpeg job.sub: ------------ Executable = ffmpeg Transfer_executable = false Should_transfer_files = YES file = S1E2 The Train Job.wmv Transfer_input_files = $(file) Args = "-i '$(file)' '$(file).mp4'" Queue ------------ job1.sub: -------------- Transfer_input_files = $(file) Args = "-i '$(file)' '$(file).mp4' " Queue FILE from ( S1E1 Serenity.wmv S1E2 The Train Job.wmv S1E3 Bushwhacked.wmv S1E4 Shindig.wmv ... ) ------------- Problema: i files di input sono del tipo *.wmv, per cui quelli di out si chiameranno *.vmw.mp4 . Vogliamo avere nomi *.mp4 Per manipolare i filename c'e' una famiglia di funzioni $F*() $Fqpdnxba() expande a parti di un filename Esempi. Dato file = "./Video/Firefly/S1E4 Shindig.wmv" $Fp(file) -> ./Video/Firefly/ # File absolute Path $Fqp(file) -> "./Video/Firefly" # Quoted File, relative path $Fqpa(file)-> './Video/Firefly' # Quoted File $Fd(file) -> Firefly/ # Directory padre $Fdb(file) -> Firefly # idem ma senza / $Fn(file) -> S1E4 Shindig # File Name, senza extension $Fx(file) -> .wmv # File Extension $Fnx(file) -> S1E4 Shindig.wmv # File Name, con extension. Esempi: Quindi, usando nel sumit file: Transfer_Input_Files = $(file) Args = "-i '$Fnx(file)' '$Fn(file).mp4'" Gli Outfile diventano corretti: S1E1 Serenity.mp4 S1E2 The Train Job.mp4 S1E3 Bushwhacked.mp4 S1E4 Shindig.mp4 Quirks: i dettagli di $F*() sono abbastanza intricati e non e' sempre facile prevedere cosa combinano. TIP: Si puo' vedere in anticipo come viene interpretato un submit file: Es: FILE = The Train Job.wmv Args = "-i '$Fnx(file)' -w640 '$Fn(file).mp4' " ~$ condor_submit test.sub -dump test.ads ~$ condor_status -ads test.ads -af Arguments -i The' 'Train' 'Job.wmv -w640 The' 'Train' 'Job.mp4 # Su Unix: -i 'The Train Job.wmv' -w640 'The Train Job.mp4‘ # Su Windows: -i "The Train Job.wmv" -w640 "The Train Job.mp4" IN GENERALE: alcuni filename possono contenere caratteri problematici per l'escape e causare problemi a transfer_input_files e/o a Arguments. Per gestire casi ostici: 1. wrappare l'eseguibile in un proprio script, e bypassare problemi di escape nel passaggio degli argomenti 2. Creare un custom job attributes # Due modi per definire un CustomAttr nel job ad +CustomAttr = "value" MY.CustomAttr = "value" You can refer to custom attributes in $() expansion in your submit file transfer_input_files = $F(My.CustomAttr) Esempio, Add custom attributes to the job job.sub: ---------- Executable = xcode.py Args = -s 640x360 Transfer_executable = true Should_transfer_files = true MY.SourceDir = $Fqp(FILE) # <--- MY.SourceFile = $Fqnx(FILE) # <--- +OutFile = "$Fn(FILE).mp4" # <--- Batch_name = $Fdb(FILE) Queue FILE matching files Firefly/*.wmv ----------- Python per interrogare il .job.ad e lanciare l'eseguibile (ffmpeg) #!/usr/bin/env python # xcode.py import htcondor, classad, htchirp, sys, os # load the job ad and get the source filename job = classad.parseOne(open('.job.ad').read()) src = job['SourceFile'] srcpath = job['SourceDir'] + src # fetch the input file htchirp.fetch(srcpath, src) # do the conversion and return the exit code sys.exit(os.system("ffmpeg -i {0} {2} {1}".format( src, job['OutFile'], job['Arguments'])); ############################################################################# DAG Jobs (cfr. https://agenda.hep.wisc.edu/event/1325/session/2/contribution/20/material/slides/0.pdf ) my.dag --------------- JOB A A.sub # Diamo nomi ai singoli job, JOB B1 B1.sub # descritti da altrettanti subm. file JOB B2 B2.sub # nome nodi e nome files.sub possono essere JOB B3 B3.sub # scelti a piacere, no vincoli. JOB C C.sub PARENT A CHILD B1 B2 B3 # Descriviamo le relazioni (edges) PARENT B1 B2 B3 CHILD C --------------- # per es. abbiamo tutti i subm.file nella stessa dir $ ls dag_dir A.sub B1.sub B2.sub B3.sub C.sub my.dag Submit e monitoring: $ condor_submit_dag my.dag ------------------------------------------------------------------ File for submitting this DAG to HTCondor : my.dag.condor.sub Log of DAGMan debugging messages : my.dag.dagman.out Log of HTCondor library output : my.dag.lib.out Log of HTCondor library error messages : my.dag.lib.err Log of the life of condor_dagman itself : my.dag.dagman.log Submitting job(s). 1 job(s) submitted to cluster 87274940. Un DAG Manager (DAGMan) parte nel Submit Node, come un job in coda. All'inizio: $ condor_q -- Schedd: submit-3.chtc.wisc.edu : <128.104.100.44:9618?... OWNER BATCH_NAME SUBMITTED DONE RUN IDLE TOTAL JOB_IDS alice my.dag+128 4/30 18:08 _ _ _ _ 0.0 1 jobs; 0 completed, 0 removed, 0 idle, 1 running, 0 held, 0 suspended $ condor_q -nobatch -- Schedd: submit-3.chtc.wisc.edu : <128.104.100.44:9618?... ID OWNER SUBMITTED RUN_TIME ST PRI SIZE CMD 128.0 alice 4/30 18:08 0+00:00:06 R 0 0.3 condor_dagman 1 jobs; 0 completed, 0 removed, 0 idle, 1 running, 0 held, 0 suspended Dopo un po' si vede il job A: $ condor_q -- Schedd: submit-3.chtc.wisc.edu : <128.104.100.44:9618?... OWNER BATCH_NAME SUBMITTED DONE RUN IDLE TOTAL JOB_IDS alice my.dag+128 4/30 18:08 _ _ 1 5 129.0 2 jobs; 0 completed, 0 removed, 1 idle, 1 running, 0 held, 0 suspended $ condor_q -nobatch -- Schedd: submit-3.chtc.wisc.edu : <128.104.100.44:9618?... ID OWNER SUBMITTED RUN_TIME ST PRI SIZE CMD 128.0 alice 4/30 18:08 0+00:00:36 R 0 0.3 condor_dagman 129.0 alice 4/30 18:08 0+00:00:00 I 0 0.3 A_split.sh 2 jobs; 0 completed, 0 removed, 1 idle, 1 running, 0 held, 0 suspended Poi si vedono gli altri... $ condor_q -- Schedd: submit-3.chtc.wisc.edu : <128.104.100.44:9618?... OWNER BATCH_NAME SUBMITTED DONE RUN IDLE TOTAL JOB_IDS alice my.dag+128 4/30 18:08 1 _ 3 5 130.0 ... 132.0 4 jobs; 0 completed, 0 removed, 3 idle, 1 running, 0 held, 0 suspended $ condor_q -nobatch -- Schedd: submit-3.chtc.wisc.edu : <128.104.100.44:9618?... ID OWNER SUBMITTED RUN_TIME ST PRI SIZE CMD 128.0 alice 4/30 18:08 0+00:20:36 R 0 0.3 condor_dagman 130.0 alice 4/30 18:28 0+00:00:00 I 0 0.3 B_run.sh 131.0 alice 4/30 18:28 0+00:00:00 I 0 0.3 B_run.sh 132.0 alice 4/30 18:28 0+00:00:00 I 0 0.3 B_run.sh 4 jobs; 0 completed, 0 removed, 3 idle, 1 running, 0 held, 0 suspended Alla fine di B1, B2, B3, si vede partire l'ultima fase (C == combine) $ condor_q -- Schedd: submit-3.chtc.wisc.edu : <128.104.100.44:9618?... OWNER BATCH_NAME SUBMITTED DONE RUN IDLE TOTAL JOB_IDS alice my.dag+128 4/30 18:08 4 _ 1 5 133.0 2 jobs; 0 completed, 0 removed, 1 idle, 1 running, 0 held, 0 suspended $ condor_q -nobatch -- Schedd: submit-3.chtc.wisc.edu : <128.104.100.44:9618?... ID OWNER SUBMITTED RUN_TIME ST PRI SIZE CMD 128.0 alice 4/30 18:08 0+00:46:36 R 0 0.3 condor_dagman 133.0 alice 4/30 18:54 0+00:00:00 I 0 0.3 C_combine.sh 2 jobs; 0 completed, 0 removed, 1 idle, 1 running, 0 held, 0 suspended I file di stato vengono creati al momento del submit: $ ls dag_dir A.sub B1.sub B2.sub B3.sub C.sub my.dag my.dag.condor.sub my.dag.dagman.log my.dag.dagman.out my.dag.lib.err my.dag.lib.out my.dag.nodes.log *.condor.sub and *.dagman.log describe the queued DAGMan job process, as for all queued jobs *.dagman.out has detailed logging (look to first for errors) *.lib.err/out contain std err/out for the DAGMan job process *.nodes.log is a combined log of all jobs within the DAG Oooops, qualcosa non va... Come cancello tutto? condor_rm dagman_jobID $ condor_rm 128 All jobs in cluster 128 have been marked for removal NB: il remove produce un "rescue file": $ ls dag_dir/ .... my.dag.rescue001 Contiene info su quali nodi hanno finito correttamente (non conosce la struttura del DAG) Il rescue viene creato automaticamente se qualche parte fallisce o viene interrotta (abort / halt senza unhalt) In caso di resubmit del dagjob, il rescue file viene considerato automaticamente, se esiste. override: condor_submit_dag dag_file -f #Non considera il rescue file Esempio: A /|\ / | \ B1 B2 B3 \ | / \ | / C Se fallisce B2 (exit Status != 0) Gli altri B* continuano Quando Tutti i B* sono finiti il dag Fallisce PRIMA di far partire C. Job Held: $ condor_q -nobatch -- Schedd: submit-3.chtc.wisc.edu : <128.104.100.44:9618?... ID OWNER SUBMITTED RUN_TIME ST PRI SIZE CMD 128.0 alice 4/30 18:08 0+00:20:36 R 0 0.3 condor_dagman 130.0 alice 4/30 18:18 0+00:00:00 H 0 0.3 B_run.sh 131.0 alice 4/30 18:18 0+00:00:00 H 0 0.3 B_run.sh 132.0 alice 4/30 18:18 0+00:00:00 H 0 0.3 B_run.sh 4 jobs; 0 completed, 0 removed, 0 idle, 1 running, 3 held, 0 suspended Look at the hold reason (in the job log, or with ‘condor_q -hold’) Fix the issue and release the jobs (condor_release) -OR- remove the entire DAG, resolve, then resubmit the DAG A DAG completato ci sono dei files relativi al dag: $ ls dag_dir/*.dagman.* my.dag.dagman.log my.dag.dagman.out my.dag.dagman.metrics *.dagman.metrics is a summary of events and outcomes *.dagman.log will note the completion of the DAGMan job *.dagman.out has detailed logging for all jobs (look to first for errors) PRE e POST scripts: Puo' capitare che dopo "fase A" sia necessaria qualche operazione prima di passare a "fase B" e/o prima di "fase C". $ cat my.dag: JOB A A.sub SCRIPT POST A sort.sh # <--- JOB B1 B1.sub JOB B2 B2.sub JOB B3 B3.sub JOB C C.sub SCRIPT PRE C tar_it.sh # <--- PARENT A CHILD B1 B2 B3 PARENT B1 B2 B3 CHILD C -------------- Retry di singoli task fino a N volte: ------------ JOB A A.sub RETRY A 5 # Vedi anche UNLESS_EXIT: retry solo per alcuni exit codes JOB B B.sub PARENT A CHILD B -------------- NB: invece di usare RETRY nel dag, per casi semplici conviene usare "max_retries" in A.sub NB1: se c'e' SCRIPT, viene compreso nel RETRY; NB2: l'exit code e' quello dello script, non del job. Esempio: ------------- SCRIPT PRE A download.sh JOB A A.sub SCRIPT POST A checkA.sh my.out $RETURN RETRY A 5 ------------ Agli script si possono passare argomenti e variabili: $JOB : node name $JOBID : ClusterId.ProcId $RETURN : return code del nodo [...] cfr manuale... ################# Problema: Non vogliamo scrivere B1.sub, B2.sub ... Bn.sub perche' sono uguali a meno di parametri Soluzione: subm. file templates e VARS: Esempio: $ cat my.dag JOB B1 B.sub VARS B1 data="B1" opt="10" JOB B2 B.sub VARS B2 data="B2" opt="12" JOB B3 B.sub VARS B3 data="B3" opt="14" $ cat B.sub ... InitialDir = $(data) arguments = $(data).csv $(opt) ... queue ----------------- SPLICE: permette di snellire l'aspetto del dag file $ cat my.dag JOB A A.sub SPLICE B B.spl JOB C C.sub PARENT A CHILD B PARENT B CHILD C $ cat B.spl JOB B1 B1.sub JOB B2 B2.sub … JOB BN BN.sub Si puo' complicare ulteriormente per ottenere DAG sempre piu' articolati ma espressi in forma "concisa", ma rimane "syntactic sugar". Internamente DAGMan espande tutto in un grande DAG completo. --------------- Problema: "Fase A" produce n files da passare ad altrettanti nodi "Fase B", ma n non e' noto a priori. Si usa il SUBDAG: $ cat my.dag JOB A A.sub SUBDAG EXTERNAL B B.dag JOB C C.sub PARENT A CHILD B PARENT B CHILD C $ cat B.dag #Prodotto da A ! JOB B1 B1.sub JOB B2 B2.sub … JOB BN BN.sub NOTA: combinando SUBDAG, POST script e RETRY si possono ottenere Cicli all'interno di un DAG $ cat my.dag JOB A A.sub SUBDAG EXTERNAL B B.dag SCRIPT POST B iterateB.sh RETRY B 100 # Se iterateB.sh != 0 : RETRY B, fino a 100 volte JOB C C.sub PARENT A CHILD B PARENT B CHILD C -------- Ogni SUBDAG e un DAGMan job, e mandare molti puo' appesantire la coda. I SUBDAG andrebbero usati solo se strettamente necessario. I SUBDAG sono NODI (possono avere i loro PRE e POST script, RETRY etc.) NOTA: DAG molto versatile e potente: possibilita' illimitate, su RISORSE LIMITATE! Da grandi possibilita' non [sempre] derivano grandi risorse. CONTROLLO SUI DAG $ condor_hold mette in pausa il DAG: Non partono nuovi nodi, ma quelli running continuano (compresi i SUBDAG, ma NON girano i PRE / POST script) $ condor_release riprende l'attivita'. Se un DAG ha p. es. O(1000) nodi si rischia di mandare in overload il submit node e la coda puo' rallentare molto. Si rimendia ponendo dei limiti su: Number of jobs in the queue Number of jobs idle (waiting to run) Number of PRE or POST scripts running I limiti si possono specificare come opzioni a condor_submit_dag oppure in un DAG-specific config file (recommended!) $ cat my.dag JOB A A.sub SPLICE B B.dag JOB C C.sub PARENT A CHILD B PARENT B CHILD C CONFIG my.dag.config $ cat my.dag.config DAGMAN_MAX_JOBS_SUBMITTED = 1000 DAGMAN_MAX_JOBS_IDLE = 100 DAGMAN_MAX_PRE_SCRIPTS = 4 DAGMAN_MAX_POST_SCRIPTS = 4