Università degli Studi di Perugia FACOLTÀ DI SCIENZE MATEMATICHE, FISICHE E NATURALI Corso di Laurea in Informatica Tesi di Laurea Sviluppo di uno strumento per l’analisi e l’ottimizzazione delle politiche di scheduling di un cluster Candidato Relatore Claudio Tanci Prof. Leonello Servoli Correlatore Mirko Mariotti Anno Accademico 2008–2009 Ai miei genitori Ringraziamenti Desidero rivolgere un sentito ringraziamento al Prof. Leonello Servoli per la costante disponibilità e l’opportunità di lavorare ad un progetto stimolante e divertente, a Mirko Mariotti e Francesco Cantini per aver posto le basi per questo lavoro, per il supporto garantito ma soprattutto per l’amicizia dimostrata. Insieme a loro un grazie particolare va a Flavio, Igor, Riccardo, per le mille risate e il tempo trascorso insieme, un grazie allargato alle tante persone con cui ho condiviso pranzi, partite e pause caffè che a vario titolo frequentano il Dipartimento di Fisica, che è diventata per un poco una seconda casa, e ai compagni di università vecchi e nuovi, Daniele, Luca, Mattia, Simone e Tiziano. Grazie a Francesco e a tutti gli ex compagni di squadra, ex compagni di scuola che ancora mi sopportano per una birra o due chiacchere, grazie a Silvia, che c’è sempre ci sia bisogno di parlare o fare spellcheck. Grazie ai miei genitori, a Laura e a tutta la famiglia per troppe cose da ricordare qui. Grazie a tutti quelli che hanno avuto pazienza e sono stati da sprone. V Indice Introduzione 1 1 Sistemi batch 5 1.1 Nascita ed evoluzione del calcolatore elettronico . . . . . . . . . . . . . 5 1.2 L’elaborazione batch . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.3 Il calcolatore oggi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4 Batch computing reprise . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2 Cluster 11 2.1 Computer a grappolo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.2 Cluster per il calcolo: Architettura . . . . . . . . . . . . . . . . . . . . 12 2.3 Cluster per il calcolo: Software . . . . . . . . . . . . . . . . . . . . . . 12 2.3.1 Client delle user interface . . . . . . . . . . . . . . . . . . . . . 12 2.3.2 Il resource manager . . . . . . . . . . . . . . . . . . . . . . . . 12 2.3.3 Lo scheduler . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3 Virtualizzazione 15 3.1 L’astrazione delle risorse . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.2 Tipologie di virtualizzazione . . . . . . . . . . . . . . . . . . . . . . . . 15 3.3 Xen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 4 Algoritmi genetici 4.1 19 Funzionamento degli algoritmi genetici . . . . . . . . . . . . . . . . . . 5 Il cluster INFN Perugia 19 23 5.1 L’architettura hardware . . . . . . . . . . . . . . . . . . . . . . . . . . 24 5.2 Alcune particolarità del cluster . . . . . . . . . . . . . . . . . . . . . . 26 5.3 La configurazione software . . . . . . . . . . . . . . . . . . . . . . . . . 26 VII 6 TORQUE/Maui 31 6.1 TORQUE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 6.2 Maui . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 6.2.1 Il concetto di priorità . . . . . . . . . . . . . . . . . . . . . . . 32 6.2.2 Allocazione dei nodi . . . . . . . . . . . . . . . . . . . . . . . . 33 6.2.3 Meccanismi di Fairness . . . . . . . . . . . . . . . . . . . . . . 35 6.2.4 Controllo dell’accesso alle risorse . . . . . . . . . . . . . . . . . 36 6.2.5 Ottimizzazione del comportamento dello scheduler . . . . . . . 37 6.2.6 Log e simulazioni . . . . . . . . . . . . . . . . . . . . . . . . . . 38 7 L’infrastruttura di analisi, simulazione e ottimizzazione 39 7.1 Alcuni problemi da affrontare . . . . . . . . . . . . . . . . . . . . . . . 39 7.2 Metriche di sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 7.2.1 Metrica di equità . . . . . . . . . . . . . . . . . . . . . . . . . . 42 7.2.2 Metrica di efficienza . . . . . . . . . . . . . . . . . . . . . . . . 42 Anatomia dell’infrastruttura . . . . . . . . . . . . . . . . . . . . . . . . 43 7.3.1 La farm di produzione . . . . . . . . . . . . . . . . . . . . . . . 43 7.3.2 La farm di test . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 7.3.3 Il repository server . . . . . . . . . . . . . . . . . . . . . . . . . 44 7.3.4 Le macchine di simulazione . . . . . . . . . . . . . . . . . . . . 47 7.3.5 Stazioni di controllo e analisi . . . . . . . . . . . . . . . . . . . 47 Fisiologia e software dell’infrastruttura . . . . . . . . . . . . . . . . . . 47 7.4.1 I run . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 7.4.2 Il sottosistema delle feature . . . . . . . . . . . . . . . . . . . . 48 7.4.3 Sottomettere un run: run.conf e command.sh . . . . . . . . . . 50 7.4.4 Tool per la gestione dell’infrastruttura . . . . . . . . . . . . . . 50 7.4.5 Tool di preparazione tracce . . . . . . . . . . . . . . . . . . . . 54 7.4.6 Tool di simulazione, test e ottimizzazione . . . . . . . . . . . . 57 7.4.7 Tool di analisi 60 7.3 7.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Test e simulazioni 63 8.1 Test sul Fairshare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 8.2 Simulazione sul Fairshare n.1 . . . . . . . . . . . . . . . . . . . . . . . 64 8.3 Simulazione sul Fairshare n.2 . . . . . . . . . . . . . . . . . . . . . . . 65 8.4 Simulazione sul Fairshare n.3 . . . . . . . . . . . . . . . . . . . . . . . 66 8.5 Simulazione sul Fairshare n.4 . . . . . . . . . . . . . . . . . . . . . . . 66 8.6 Simulazione sul Fairshare n.5 . . . . . . . . . . . . . . . . . . . . . . . 67 8.7 Test di ottimizzazione genetica . . . . . . . . . . . . . . . . . . . . . . 68 Conclusioni 75 VIII Appendici 79 A File di configurazione 79 run.conf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 config-simul-template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 File di traccia Maui . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 File delle risorse Maui . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 B Sorgenti 85 functions.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 launch.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 ls-feat.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 ls-traces.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 resolve-users.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 resolve-queues.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 resolve-schedule.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 run.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 simulate.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 superexecute.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 sync-traces.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 visualize.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Bibliografia 149 IX Elenco delle figure 1 Mainframe, cluster e griglie computazionali. . . . . . . . . . . . . . . . 2 3.1 L’architettura di Xen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 4.1 Schema di evoluzione genetica . . . . . . . . . . . . . . . . . . . . . . . 20 5.1 Topologia della rete del cluster INFN di Perugi . . . . . . . . . . . . . 25 6.1 Interfaccia Maui/TORQUE . . . . . . . . . . . . . . . . . . . . . . . . 32 7.1 L’infrastruttura hardware . . . . . . . . . . . . . . . . . . . . . . . . . 43 7.2 Struttura del repository rsync . . . . . . . . . . . . . . . . . . . . . . . 46 7.3 Struttura logica dell’infrastruttura di ottimizzazione . . . . . . . . . . 49 7.4 Ciclo di esecuzione di superexecute.sh . . . . . . . . . . . . . . . . . . 52 7.5 Ciclo di esecuzione di run.sh . . . . . . . . . . . . . . . . . . . . . . . . 57 7.6 Ciclo di esecuzione di launch.sh . . . . . . . . . . . . . . . . . . . . . . 58 7.7 Ciclo di esecuzione di simulate.sh . . . . . . . . . . . . . . . . . . . . . 60 7.8 Timeline di scheduling generata da trace-timeliner.pl . . . . . . . . . . 61 8.1 Test sul fairshare, tempo di attesa medio. . . . . . . . . . . . . . . . . 70 8.2 Simulazione sul fairshare n.1, tempo di attesa medio. . . . . . . . . . . 70 8.3 Fairshare, test e simulazione n.1 a confronto. . . . . . . . . . . . . . . 71 8.4 Simulazione sul fairshare n.2, tempo di attesa medio. . . . . . . . . . . 71 8.5 Simulazione sul fairshare n.3, tempo di attesa medio. . . . . . . . . . . 72 8.6 Simulazione sul fairshare n.4, tempo di attesa medio. . . . . . . . . . . 72 8.7 Simulazione sul fairshare n.5, tempo di attesa medio. . . . . . . . . . . 73 8.8 Simulazione sul fairshare n.3 e n.5 a confronto. . . . . . . . . . . . . . 73 8.9 Evoluzione genetica dei valori di priorità. . . . . . . . . . . . . . . . . 74 8.10 Evoluzione genetica dei valori di fitness. . . . . . . . . . . . . . . . . . 74 XI Elenco delle tabelle 5.1 Risorse hardware apportate dai vari gruppi di ricerca . . . . . . . . . . 25 5.2 Principali code locali attive e relativi walltime . . . . . . . . . . . . . . 28 5.3 Principali code grid attive e relativi walltime . . . . . . . . . . . . . . 29 6.1 Componenti e sottocomponenti per il calcolo della priorità in Maui. 34 XIII . Introduzione Sopra la scrivania o sulle ginocchia abbiamo computer che quindici anni fa sarebbero entrati di diritto nella lista dei sistemi più potenti al mondo, eppure rimane per molti il bisogno di avere il massimo che sia disponibile e a portata di budget: astrofisica, fisica nucleare, fluidodinamica, chimica, metereologia, genetica, economia, finanza e tutte le discipline dove le simulazioni sono un importante strumento di ricerca e analisi sono affamate di risorse computazionali. Enti di ricerca, istituti finanziari, forze armate di vari paesi sono sempre alla ricerca di un’espansione delle loro capacità di calcolo: • L’acceleratore di particelle LHC in costruzione presso il CERN vicino a Ginevra produrrà ogni anno a regime collisioni protone-protone i cui risultati, registrati da vari esperimenti attivi 24 ore su 24, saranno pari a circa 15 Petabyte di dati da analizzare.[2] • Google processa oltre 20 Petabyte di dati al giorno.[4] • Il Lawrence Livermore National Laboratory in California, che si occupa delle valutazioni sulle performance, sulla sicurezza e l’affidabilità nel tempo del munizionamento nucleare per il Dipartimento della Difesa USA ospita un’installazione IBM BlueGene/L da 131 072 unità di calcolo.[5] L’architettura dei moderni supercomputer è modellata da questi requisiti, dall’evoluzione dei processi industriali (è nota l’osservazione di Moore sulla crescita esponenziale del numero di transistor in un singolo circuito integrato) e da forze di mercato come le economie di scala che penalizzano architetture e sistemi ad-hoc computazionalmente più efficienti per un uso specifico in favore di soluzioni generalizzate dai minori costi di sviluppo e gestione.[7] L’evoluzione storica ha cosı̀ visto succedersi vari modelli di architetture: negli anni ’60 la CDC fece da pioniere introducendo quello che può essere considerato il primo supercomputer, il CDC 6600, essenzialmente un mainframe molto efficiente. L’introduzione di processori vettoriali, capaci di operare in parallelo sui dati ha dominato 1 Figura 1: Mainframe, cluster e griglie computazionali. Dimensioni e velocità di interconnessione a confronto la scena negli anni ’70, per poi lasciare a sua volta il passo a sistemi che introducevano la parallelizzazione non più (o non solo) all’interno della CPU ma a livello di macchine. Lo sviluppo di sistemi cluster, sistemi collegati in rete locale e gestiti in maniera associata, ma ognuno con una propria copia del sistema operativo, ha permesso cosı̀ di superare i limiti alla crescita dei singoli computer, portando alla nascita nei centri di calcolo di farm, vere e proprie “fattorie” di computer. La necessità di capacità di calcolo ancora superiori è stata infine soddisfatta (per il momento) dalle cosiddette computing grid o griglie computazionali, che raggruppano sistemi geograficamente distanti tipicamente tramite Internet. Da notare in questa successione è il progressivo allontanamento tra unità di elaborazione, che rende adatti supercomputer di tipo mainframe isolati, cluster di calcolatori e griglie computazionali a diversi problemi e paradigmi di calcolo: dove c’è la necessità di comunicazioni veloci e frequenti tra le varie componenti software un veloce calcolatore singolo sarà necessariamente la scelta giusta, viceversa problemi ed algoritmi “a grana grossa”, in cui le comunicazioni tra componenti assumono importanza più ridotta si possono adattare bene alle quasi illimitate capacità di calcolo delle griglie computazionali, non risentendo più di tanto della lentezza delle comunicazioni e di sporadiche disconnessioni (fig. 1). I sistemi cluster hanno conosciuto una fortuna crescente dalla loro introduzione, a Novembre 2007 406 sistemi su 500 nella lista TOP500 sono classificati come cluster.1 [6] Ancora maggiore è l’utilizzo in ambienti dalle minori esigenze computazionali dato il minor costo comparato ad un singolo calcolatore di capacità equivalente, la possibilità di usare hardware off-the-shelf nonchè di natura eterogenea e le notevoli possibilità di upgrade nel tempo. 1 il progetto Top500 pubblica ogni 6 mesi la lista dei 500 supercomputer più potenti secondo il benchmark HPL (http://www.netlib.org/benchmark/hpl/) che è possibile consultare presso http: //top500.org 2 Nonostante le loro qualità i sistemi cluster rimangono difficili da gestire in modo efficiente, soprattutto in ambienti con hardware misto e carico di lavoro variabile nel tempo. Un esempio, anche se forse non proprio tipico, può essere dato dal cluster del Dipartimento di Fisica e I.N.F.N. di Perugia, che oltre a servire vari gruppi di ricerca locali, che partecipano in varia misura con fondi e hardware alla sua costituzione, fa parte della griglia di calcolo europea EGEE.2 La progettazione e la messa a punto del sistema, alla ricerca di un bilanciamento tra le varie e spesso contrastanti esigenze degli utenti in un ambiente eterogeneo con la necessità di un utilizzo efficiente della capacità di calcolo, si rivela una difficile arte, dove l’adeguamento delle configurazioni ai mutevoli carichi di lavoro consiste necessariamente in un graduale aggiustamento dell’esistente; troppo alti infatti i rischi in perdita di produttività per consentire l’esplorazione di scelte più radicali. In questo scenario lo sviluppo di una infrastruttura per la simulazione permette la creazione di un ambiente di test separato dal cluster di produzione, fornendoci la possibilità di studiare la risposta del sistema a variazioni nell’hardware, nella configurazione del software di gestione e nei carichi di lavoro. L’uso di algoritmi genetici permette l’automazione del processo di ottimizzazione della farm e apre scenari di risposta quasi in tempo reale ai cambiamenti nel carico di lavoro. Struttura della tesi La tesi, composta da otto capitoli a cui si aggiungono le conclusioni e le appendici, può essere idealmente suddivisa in tre parti: la prima parte, comprendente i primi quattro capitoli, introduce concetti e tecnologie alla base del progetto, la seconda parte in due capitoli illustra il cluster INFN di Perugia e il software utilizzato per la sua gestione mentre l’ultima parte presenta l’infrastruttura di analisi, simulazione e ottimizzazione del cluster sviluppata e alcuni risultati preliminari ottenuti. Capitolo primo. Un’introduzione all’elaborazione batch, una panoramica storica e la rilevanza attuale dei sistemi batch per la computazione. Capitolo secondo. Descrizione e classificazione di sistemi cluster, con particolare accento sui cluster di calcolo e le loro componenti. Capitolo terzo. Un’introduzione alla astrazione hardware dei sistemi e a Xen, la piattaforma di virtualizzazione scelta per il progetto. Capitolo quarto. Vengono esposti brevemente il funzionamento degli algoritmi genetici e le varie fasi in cui si articola la loro esecuzione. 2 Per ulteriori informazioni consultare i rispettivi siti internet:http://grid.pg.infn.it/ e http: //www.eu-egee.org/ 3 Capitolo quinto. Una presentazione del sistema batch del sito INFN di Perugia, con una discussione sulle particolarità che lo contraddistinguono da altre installazioni simili. Capitolo sesto. Descrizione e configurazione dei software TORQUE e Maui, resource manager e scheduler scelti per il sistema batch INFN di Perugia. Capitolo settimo. Presentazione e implementazione dell’infrastruttura di analisi, simulazione e ottimizzazione con una descrizione dei concetti chiave e delle sue componenti. Capitolo ottavo. Alcuni test realizzati grazie all’infrastruttura e i risultati preliminari di analisi. in Appendice A si descrive il formato dei file di configurazione dello scheduler Maui e degli script per la sottomissione di test e simulazioni all’infrastruttura. in Appendice B è riportato il codice sorgente degli script sviluppati nel corso della tesi. 4 Capitolo 1 Sistemi batch Computers in the future may weigh no more than 1.5 tons. Popular Mechanics (1949) 1.1 Nascita ed evoluzione del calcolatore elettronico Dalla nascita della geometria e della matematica in Egitto e in Grecia ci si è sempre scontrati con la difficoltà del loro utilizzo per la soluzione di problemi pratici. Nei lunghi calcoli manuali un singolo errore può facilmente inficiare la validità del risultato compromettendo tutto lo sforzo fatto. Per questo motivo dispositivi che aiutassero e velocizzassero le ripetitive operazioni di calcolo sono stati l’obbiettivo di inventori e matematici, portando alla nascita, nel tempo, di tutta una serie di “aiutanti meccanici” come abaci, calcolatrici meccaniche e, più recentemente, calcolatori programmabili. Il fatto che quest’ultima classe di dispositivi sia cosı̀ identificata ne sottolinea la peculiarità essenziale: la grande utilità dei calcolatori programmabili, rispetto ad altri dispositivi di calcolo, risiede nella loro versatilità, dovuta alla separazione di due componenti distinte precedentemente non riconosciute come tali, l’hardware, la componente fisica dell’apparecchiatura e il software, la sua logica di funzionamento. L’hardware, per sua stessa natura, è soggetto a un lento e costoso processo di progettazione, sviluppo e produzione. È caratterizzato da funzioni immutabili o comunque difficilmente espandibili, ma progettando un hardware aperto, spostando la parte di logica di funzionamento nel software e riconoscendone la diversa natura immateriale ci si può liberare, almeno in parte, da queste limitazioni: • Si può usare lo stesso hardware per una varietà di problemi differenti. • Si può ottimizzare e riscrivere velocemente il software nel tempo. 5 • Il costo della correzione di eventuali errori in software si presenta molto minore. Nonostante questa importante evoluzione i primi calcolatori programmabili comportavano per gli standard di oggi numerosi problemi. Il lavoro di programmazione, immissione dei dati e preparazione della macchina per uno specifico problema vedeva infatti l’utente-tecnico di turno impegnarsi in lunghe e faticose procedure durante le quali la macchina rimaneva in attesa con conseguente sottoutilizzo di prezione risorse di calcolo. La soluzione al problema fu di prendere in prestito una modalità di produzione tipica di alcuni processi industriali detta produzione a lotti o “batch production”, che si basa sull’idea di riordinare in lotti omogenei i prodotti da processare, cosı̀ da minimizzare i tempi morti nelle operazioni di preparazione della catena produttiva al cambio di prodotto. Applicata all’informatica quest’idea si traduce nel raggruppare i processi da eseguire insieme ai loro dati di input e tutto quanto possa servire alla loro esecuzione prima della loro sottomissione alla macchina. Il lavoro tipico dell’utente-programmatore consisteva cosı̀ nel preparare lotti di schede perforate contenenti i comandi necessari al caricamento del programma in memoria e alla sua inizializzazione. Il programma principale e i suoi dati di input già pronti aspettavano il loro turno di utilizzo del calcolatore e la stampa dei risultati. Con l’introduzione di memorie ausiliare come nastri e dischi magnetici e la nascita di unità satellite al calcolatore principale per la sottomissione e memorizzazione dei job vennero abbattuti ulteriormente i tempi di preparazione. Lo spostamento della coda dei lavori in attesa dalla scrivania dell’operatore di macchina all’interno del calcolatore permise finalmente la possibilità di una sua gestione automatizzata indipendente dall’ordine di arrivo dei job. 1.2 L’elaborazione batch Il paradigma di elaborazione a lotti o “batch computing” sorse quindi come soluzione naturale e perfettamente adeguata all’ambiente e alle problematiche dell’informatica degli anni ’50 e ’60, il costo dei rari computer dell’epoca poteva infatti essere giustificato solo con un loro uso continuo e con il minimo spreco delle limitate risorse di calcolo disponibili. In questo scenario le caratteristiche dell’elaborazione batch si prestano in modo ottimale allo scopo: • In fasi di utilizzo elevato del sistema di calcolo permette di posticipare l’esecuzione di job a momenti in cui le risorse siano meno occupate uniformando il carico di lavoro nel tempo. 6 • La fase di esecuzione dei job non viene ritardata da attese di interazione con l’utente. • Massimizzando l’uso del sistema, permette un più velocemente ammortizzamento dei costi di acquisto e gestione dello stesso. D’altro canto la necessaria scelta di privilegiare l’efficienza del sistema a discapito di altre qualità ha profonde conseguenze sulle modalità di utilizzo dello stesso, molte delle quali non necessariamente gradite da parte degli utenti: • Le fasi di preparazione e postproduzione dei job sono a carico dell’utente. • I tempi di attesa dei risultati sono incerti. • Il sistema non si presta ad un utilizzo interattivo. • Eventuali errori di programmazione si rivelano solo al momento dell’effettiva esecuzione del codice, complicando le attività di sviluppo e debug. 1.3 Il calcolatore oggi L’esperienza descritta fin qui potrebbe sembrare aliena all’utente odierno, la continua rivoluzione informatica ha cambiato il volto del calcolatore e ha favorito la sua evoluzione in diverse direzioni per adattarsi a una molteplicità di scopi e utilizzi. Partendo dal familiare personal computer in un grossolano ordinamento per dimensioni e prestazioni decrescenti possiamo evidenziare alcune classi: • Personal Computer desktop o portatili. • Palmari e telefoni cellulari avanzati. • Embedded computer. Come ci si può aspettare, ad ogni classe evidenziata corrispondono differenti modalità d’uso che si riflettono nel progetto hardware e software dei dispositivi. L’invenzione dei microprocessori nei primi anni ’70, con la crescente integrazione di componenti, ha reso possibile la nascita del Personal Computer, dove, come evidenziato dal nome, l’attenzione non è più puntata sulle necessità della macchina ma su quelle dell’utente, aprendo scenari impensabili fino a quel momento: il nuovo paradigma fa corrispondere una macchina per ogni utente, a sua completa disposizione per tutto il tempo necessario con un’interfaccia amichevole e dotata di capacità multimediali. Il computer torna ad aspettare i lenti tempi umani, ma ora questo non costituisce più un problema dato il costo dell’hardware ridotto di molti ordini di grandezza rispetto al passato. 7 Palmari e Smartphone, insieme ai progetti di ricerca su computer “indossabili” e altri dispositivi ultra-portabili, spingono ancora di più il mercato verso un’informatica personale dove molti dispositivi con interfacce e compiti diversi sono sempre a disposizione. Chip inseriti in tutti i tipi di oggetti quotidiani portano promesse di pervasive computing, un futuro di oggetti intelligenti, consapevoli dell’ambiente circostante e sempre collegati e in comunicazione tra di loro per soddisfare le necessità dell’utente. La discesa dal Personal Computer a dispositivi sempre più piccoli non è però l’unica direzione percorsa dall’evoluzione dei sistemi informatici, salendo verso sistemi più grandi e costosi le prospettive cambiano nuovamente: • Personal Computer, Workstation e piccoli Server. • Mainframe. • Cluster. • Griglie computazionali. Qui le distinzioni si fanno meno nette e numerose sovrapposizioni possono esistere a seconda delle caratteristiche scelte per la classificazione. I Mainframe sono quanto di più vicino ai primi computer elettronici si possa trovare sul mercato, le più grandi macchine disponibili con l’esclusione di calcolatori “one-of-a-kind” progettati su misura e richiesta del cliente. Per questa classe di macchine, più importanti della potenza di calcolo sono le capacità riguardo l’I/O dei dati e le caratteristiche di affidabilità totale derivanti, lato hardware, da una progettazione senza compromessi con un alto grado di ridondanza e controllo dei dati e, lato software, dall’uso massiccio di tecnologie di partizionamento del sistema e di virtualizzazione. Queste caratteristiche rendono i Mainframe adatti ad una serie di applicazioni critiche quali censimenti, transazioni finanziarie e applicazioni ERP (Enterprise Resource Planning o Pianificazione delle risorse d’impresa).1 La classe dei Cluster comprende una eterogeneità di sistemi dalle prestazioni e costi molto variabili la cui caratteristica comune è quella di essere formati da un insieme di computer indipendenti collegati tra loro in rete locale che lavorano in accordo presentando agli utenti molte delle caratteristiche di un singolo computer. Questo permette la progettazione di Cluster che garantiscono, tramite la ridondanza di macchine, un’alta affidabilità, oppure che sfruttino le molteplici risorse per raggiungere prestazioni non ottenibili da un singolo computer, per quanto potente, oppure semplicemente che ne garantiscano il livello di prestazioni equivalente associato ad una riduzione dei costi hardware. 1 Con ERP si identificano sistemi di gestione automatizzata che integrano tutti gli aspetti del business aziendale. 8 La richiesta di prestazioni non si ferma al livello, seppur elevato, che i Cluster rendono disponibile; applicazioni computazionalmente molto intense ma che possono essere suddivise in molte parti indipendenti che non necessitino di condividere grandi quantità di dati sono adatte a girare su sistemi “Grid” o griglie computazionali. I sistemi Grid portano ad un livello superiore di scala l’idea di parallelizzazione e distribuzione del calcolo propria già dei sistemi Cluster, connettendo tra loro risorse geograficamente distanti gestite in modo autonomo da singole istituzioni. 1.4 Batch computing reprise Come abbiamo visto l’elaborazione batch è ormai scomparsa dai computer che ci circondano negli uffici e nelle case. Come è cambiato l’hardware per adattarsi alle nuove e molteplici esigenze cosı̀ sono sorti diversi paradagmi di calcolo e di interfaccia con l’utente. Andando ad analizzare la situazione di macchine Mainframe e grandi sistemi Cluster possiamo, tuttavia, riconoscere che le problematiche e le priorità non sono cosı̀ differenti da quelle che portarono alla nascita del batch computing. Gli elevati costi d’acquisto e gestione possono infatti essere giustificati solamente con una effettiva necessità di risorse di calcolo e con l’utilizzo pieno di queste ultime, anche a discapito dell’esperienza utente cui viene chiesto nuovamente di farsi carico, almeno in parte, dei costi e delle difficoltà derivanti dalla complessità del sistema. L’elaborazione batch dà soluzione in modo eccellente a queste problematiche e per questo non è mai uscita di scena nonostante l’evoluzione tecnologica dell’architettura sottostante. 9 Capitolo 2 Cluster A supercomputer is a device for turning compute-bound problems into I/O-bound problems. Ken Batcher 2.1 Computer a grappolo La caratteristica di un cluster, quella di essere costituito da un insieme di computer distinti (i nodi del cluster) collegati tra loro in un’integrazione più o meno stretta, può essere sfruttata per vari fini. Una distinzione importante può essere fatta tra cluster per l’alta disponibilità (High Availability cluster) e cluster per il calcolo (High Performance Computing cluster): Cluster per l’alta disponibilità. La ridondanza di componenti del cluster evita single point of failure e aumenta l’affidabilità generale del sistema, politiche di bilanciamento del carico possono inoltre assicurare uno sfruttamento omogeneo dei vari nodi. Cluster per il calcolo. I nodi del cluster vengono utilizzati per aumentare le capacità di calcolo del sistema. Come si pone un cluster di calcolo rispetto a un supercomputer classico, costituito da una singola macchina molto veloce? Per quanto riguarda le prestazioni i cluster si prestano bene, per loro natura, a risolvere problemi parallelizzabili mentre soffrono per problemi con una forte componente seriale. Utilizzando hardware off the shelf si possono limitare i costi, a scapito di prestazioni, spazio e consumo energetico, ma soprattutto i cluster possono scalare in prestazioni ben oltre i livelli raggiungibili da una singola macchina. 11 2.2 Cluster per il calcolo: Architettura La distinzione tra HA cluster e HPC cluster si riflette nella loro architettura, mentre per i cluster di alta disponibilità obbiettivi di ridondanza portano sostanzialmente ad una moltiplicazione dei nodi che forniscono i servizi richiesti, per i cluster di calcolo diventa necessaria una maggiore specializzazione delle componenti. Tipicamente si possono riscontrare: Central manager. Occupandosi della gestione di tutto il sistema di calcolo costituisce il “cervello” del cluster; su questo nodo, essenziale al funzionamento del cluster, è in esecuzione il software che governa l’accettazione e l’esecuzione dei job utente e la gestione delle risorse. In sistemi particolarmente estesi queste funzionalità sono suddivise in più nodi di management. Nodi di esecuzione. Questa classe di nodi costituisce la forza lavoro del cluster, dove vengono eseguiti i job utente. I nodi di esecuzione possono essere anche di natura eterogenea, ad esempio quando il cluster è cresciuto nel tempo o per l’utilizzo di macchine specializzate. User interface. Rispetto agli altri nodi presumibilmente non accessibili agli utenti l’interfaccia utente del cluster è costituita dai nodi di frontiera cui gli utenti accreditati possono accedere per lavorare sul cluster, immettere job e ricevere i risultati dei calcoli. Nodi di checkpoint. Non sempre presenti, rappresentano una salvaguardia per eventuali errori che potrebbero vanificare il lavoro di job particolarmente lunghi salvandone lo stato ad intervalli regolari per un loro successivo eventuale ripristino. 2.3 Cluster per il calcolo: Software Dal lato software un cluster per il calcolo implementa un tipico schema di lavoro batch. 2.3.1 Client delle user interface Il software che prende in carico i job sulle user interface. Costituisce l’interfaccia del cluster verso gli utenti autenticati sul sistema, verso i quali mette a disposizione comandi per le sottomissioni e il controllo dei job in corso. 2.3.2 Il resource manager Il resource manager, come indica il nome, è il programma che gestisce le risorse (i nodi di calcolo) del cluster. Necessariamente si tratta di un programma distribuito: 12 una parte gira sui nodi di esecuzione per la gestione e monitoraggio dei job locali e delle stesse macchine, un’altra sul nodo manager per il coordinamento dell’intero cluster, le comunicazioni con i serventi locali e l’interfaccia con lo scheduler e i client. 2.3.3 Lo scheduler La gestione di tipo batch dei job sottomessi porta ad avere una coda di lavori in attesa prima della loro esecuzione. Compito dello Scheduler è quello di gestire le politiche di riordino temporale e spaziale dei job in coda, richiamando il resource manager per la loro effettiva messa in opera per un efficiente utilizzo delle risorse del cluster ed una generale soddisfazione delle esigenze degli utenti. 13 Capitolo 3 Virtualizzazione You have a hardware or a software problem. Manuale di manutenzione della stampante Gestetner 3240 3.1 L’astrazione delle risorse Il concetto di astrazione riveste un ruolo di primo piano nelle scienze informatiche, sia come guida alla comprensione di sistemi complessi e delle interazioni tra componenti sia come vero e proprio principio di progettazione che accomuna la programmazione orientata agli oggetti con architetture software stratificate come il modello per l’interconnessione ISO/OSI o i componenti di un sistema operativo.1 Seguendo la stessa linea di pensiero si è presto pensato, appena la tecnologia lo ha permesso, che fosse possibile astrarre le stesse risorse hardware fornendone una loro versione software “virtuale” più flessibile. 3.2 Tipologie di virtualizzazione A seconda del livello a cui avviene la virtualizzazione e quali risorse coinvolge è possibile identificare varie modalità di virtualizzazione: Virtualizzazione a livello di macchina. Introdotta dall’IBM già alla fine degli anni ’60 con il System/360 Model 67 e il relativo software CP/CMS consiste nell’astrazione delle periferiche hardware, nel caso l’intera architettura di sistema 1 L’Open dardization Systems Interconnection è uno standard dell’International Organization for Stanche stabilisce uno standard di riferimento aperti. 15 per l’interconnessione di sistemi venga coinvolta si parla di virtualizzazione “piena”, che permette l’esecuzione di sistemi operativi non modificati all’interno delle macchine virtuali, se invece esistono istruzioni macchina privilegiate e non virtualizzate si parla di di “para-virtualizzazione”, in genere più semplice da implementare e dalle migliori prestazioni, al costo però di necessarie modifiche al sistema operativo ospite.2 L’astrazione delle risorse viene implementata da un software denominato virtual machine monitor o hypervisor che può interfacciarsi direttamente con l’hardware (hypervisor di tipo 1 o bare-metal ) come nel caso di VMware ESX Server, Xen o KVM o consistere in un software in esecuzione in un sistema operativo tradizionale (hypervisor di tipo 2 o “hosted”) come VMware Workstation, QEMU o VirtualBox.3 Virtualizzazione a livello di sistema operativo. Questo tipo di virtualizzazione fornisce un isolamento e partizionamento delle risorse da parte del sistema operativo che prevede “ambienti virtuali” di esecuzione. Esempi di questa tecnologia usata principalmente come misura di sicurezza sono le “zone” supportate da Solaris, le “jail” di FreeBSD e su ambiente Linux l’uso del comando “chroot” e i server virtuali di VServer. Virtualizzazione a livello di applicazione. Una macchina virtuale al supporto di un singolo processo, creata al suo avvio e distrutta al suo completamento. Lo scopo della virtualizzazione a livello di applicazione è quello di fornire un ambiente per il processo indipendente dalla piattaforma in uso implementando un paradigma di compatibilità multipiattaforma “write once, run anywhere” diffuso da ambienti di programmazione come Java o .NET che prevedono la creazione di “bytecode”, codice intermedio per una generica macchina virtuale interpretato o compilato “just in time” al momento dell’esecuzione. Particolare rilevanza per servizi di elaborazione dati assume la virtualizzazione a livello di macchina che, nata in ambito mainframe, riveste una sempre maggiore importanza in molti scenari. Le macchine virtuali si prestano ad essere “sandbox” naturali dove il codice non può interferire con il sistema sottostante o altre macchine virtuali. Questa caratteristica si rivela utile sia in sistemi di produzione, come misura di limitazione dei privilegi, sia nello sviluppo di sistemi operativi e software di basso livello facilitando il debugging del sistema. In un’ottica di QOS, affidabilità e disponibilità delle risorse le tecnologie di virtualizzazione permettono l’implementazione di architetture dalle componenti ridondanti dove è possibile la migrazione “a caldo”, senza interruzione di 2 Per 3 per macchina virtuale si intende l’implementazione software di un computer. informazioni sui prodotti menzionati è possibile consultare i rispettivi siti web: http://www. vmware.com, http://xen.org, http://kvm.qumranet.com/kvmwiki, http://bellard.org/qemu, http: //www.virtualbox.org, http://linux-vserver.org. 16 Figura 3.1: L’architettura di Xen servizio, degli ambienti virtuali. Per quanto riguarda prospettive di riduzione dei costi è chiaro come la crescenti capacità dell’hardware si possano sfruttare per il consolidamento di macchine e servizi garantendo risparmi nelle risorse e nei costi di gestione loro associati. 3.3 Xen Xen è un progetto di virtualizzazione “FLOSS”.4 Inizialmente sviluppato presso l’università di Cambridge supporta le architetture x86, x86 64, IA64 e PowerPC e un’ampia gamma di sistemi operativi guest come Linux, Solaris e Windows. Come si può osservare in fig. 3.1 l’hypervisor Xen implementa l’interfaccia virtuale tra l’hardware e i sistemi operativi, responsabile dello scheduling della CPU e del partizionamento della memoria tra le varie macchine virtuali; non si occupa però della gestione generale dei dispositivi video, storage, rete o altro il cui controllo è demandato ad una speciale macchina virtuale denominata Domain 0, in genere un kernel modificato Linux. L’uso del Domain 0 permette a Xen di sfruttare l’ampia disponibilità di driver del kernel Linux e di presentare all’utente una interfaccia familiare agli strumenti di controllo e gestione delle macchine virtuali. Le altre macchine virtuali vengono identificate nella terminologia Xen come Domain U e possono consistere in sistemi paravirtualizzati o pienamente virtualizzati, se supportati dall’architettura hardware sottostante. Le caratteristiche evidenziate di Xen, il supporto a Linux come Domain 0 e sistema guest, insieme a funzioni avanzate come la migrazione “live” delle macchine 4 Per FLOSS, Free/Libre/Open Source Software, si intende una metodologia di sviluppo in cui il codice sorgente dei programmi è a libera disposizione degli utenti e la cui licenza di copyright tuteli alcuni diritti verso gli utenti tra cui l’uso, la modifica e la ridistribuzione del software. Per una definizione più dettagliata si rimanda a quella della Open Source Initiative che è possibile trovare all’indirizzo http://www.opensource.org/docs/osd. 17 e al suo riconoscimento come soluzione matura e dalle buone prestazioni hanno indotto a selezionarlo come soluzione per le esigenze sorte in campo di virtualizzazione per la farm di calcolo e altri servizi all’interno del Dipartimento di Fisica dell’Università di Perugia. 18 Capitolo 4 Algoritmi genetici I love fools’ experiments. I am always making them. Charles Darwin Gli algoritmi genetici costituiscono una classe di metodi per problemi di ricerca e ottimizzazione, il loro nome deriva dall’implementazione di alcune idee ispirate alla teoria dell’evoluzione e in modo particolare alla genetica. Gli algoritmi genetici si rivelano utili nell’affrontare una varietà di problemi per cui non è possibile stabilire a priori la forma di una soluzione e lo spazio di ricerca è particolarmente esteso. Data la natura euristica del loro funzionamento gli algoritmi genetici non forniscono certezze sulle soluzioni o sui tempi necessari ma spesso si rivelano efficienti, in particolare quando ci si può accontentare anche di soluzioni quasi-ottimali. Insieme alle procedure gli algoritmi genetici prendono in prestito dalla natura anche una buona parte della terminologia di descrizione, si parlerà quindi nel prosieguo ad esempio di geni, popolazioni o individui ; dove il significato non sia immediatamente comprensibile si procederà alla descrizione dei termini utilizzati nel caso specifico. Un algoritmo genetico richiede la definizione di alcuni elementi dipendenti dal dominio di applicazione: una rappresentazione adeguata degli elementi del dominio delle soluzioni del problema (i geni ) e una funzione obbiettivo o di fitness per la valutazione della loro “bontà”, che permetta di calcolare una distanza tra i geni considerati e la soluzione desiderata. 4.1 Funzionamento degli algoritmi genetici Nel funzionamento di un algoritmo genetico si può tipicamente osservare una successione di stadi reiterati (fig. 4.1 nella pagina seguente): 19 Figura 4.1: Schema di evoluzione genetica Generazione della popolazione iniziale. Una popolazione iniziale di potenziali soluzioni può essere generata in maniera totalmente casuale o scegliendo alcuni individui come “semina”. Il numero ottimale di elementi della popolazione dipende dal problema in esame, tipicamente sull’ordine delle centinaia o migliaia, mentre la scelta delle strutture dati per la codifica degli individui ricade solitamente su alberi o vettori. Valutazione e selezione. Ad ogni individuo viene associato un valore di idoneità calcolato dalla funzione di fitness. Gli individui con un valore di idoneità basso vengono scartati, quelli con valore di idoneità alto vengono selezionati come “genitori” per la successiva generazione. Evoluzione: crossover e mutazioni. Con operazioni di crossover e mutazione sugli individui genitori si genera la nuova popolazione di potenziali soluzioni. L’operazione di crossover consiste nel creare nuovi individui figli a partire da porzioni di codice dei genitori in analogia con il ruolo che la riproduzione sessuata svolge nel campo biologico. Nel caso più semplice di crossover “single point” viene scelta in modo casuale una singola posizione dove i geni di due genitori vengono tagliati e le parti, invertite, vengono riassemblate in due nuovi geni figli. Tramite mutazioni casuali in singoli geni si cerca invece di evitare convergenze premature su ottimi locali migliorando l’esplorazione dello spazio delle soluzioni. La nuova popolazione di potenziali soluzioni cosı̀ ottenuta può essere sottoposta nuovamente a valutazione e selezione fino al raggiungimento di un numero prefissato di generazioni, all’individuazione di una soluzione o di un altro criterio di arresto. 20 Alcune difficoltà nell’applicazione di algoritmi genetici derivano dalla loro natura aleatoria e probabilistica e dalla scelta dei parametri per la selezione degli individui, di crossover e dei tassi di mutazione, legati tra loro da relazioni non lineari, che risulta dipendente dalle condizioni specifiche del dominio di applicazione dell’algoritmo.[8] Spesso può diventare problematica anche la scelta della funzione di fitness, che deve riuscire ad esprimere in modo matematico l’obbiettivo dell’ottimizzazione o la descrizione della soluzione. Ciò nonostante gli algoritmi genetici hanno trovato ampia applicazione in campi disparati per tutta una classe di problemi per cui non si conoscono algoritmi efficienti. La ricerca di politiche di scheduling ottimale ne costituisce un esempio emblematico, con l’elevato numero di elementi che contribuiscono al risultato finale, e che si influenzano tra loro in maniera complessa, che le contraddistingue. 21 Capitolo 5 Il cluster INFN Perugia Marco Polo descrive un ponte, pietra per pietra. “Ma qual è la pietra che sostiene il ponte?” chiede Kublai Kan. “Il ponte non è sostenuto da questa o quella pietra”, risponde Marco, “ma dalla linea dell’arco che esse formano.” Kublai Kan rimane silenzioso, riflettendo. Poi soggiunge: “Perché mi parli delle pietre? È solo dell’arco che mi importa.” Polo risponde: “Senza pietre non c’è arco.” Italo Calvino Il cluster INFN di Perugia non è l’espressione di un unico soggetto, è stato infatti costituito quando da vari gruppi di ricerca locali è stata presa la decisione di accentrare e mettere in condivisione le proprie risorse di calcolo. L’accentramento delle capacità di calcolo ha significato come prevedibile una certa perdita di flessibilità da parte dei singoli gruppi in modo particolare per quanto riguarda la scelta del sistema operativo e dei programmi e librerie a disposizione degli utenti, su cui i vari gruppi ora afferenti alla struttura devono necessariamente trovare un compromesso, d’altra parte si sono concretizzati numerosi vantaggi: in primo luogo le dimensioni di un cluster unico fanno si che l’impatto dei naturali cambiamenti nel tempo dell’attività dei singoli gruppi sia più facilmente ammortizzato, inoltre dato che difficilmente tutti i gruppi richiedono contemporaneamente la totalità della loro percentuale di risorse le risorse 23 disponibili in ogni dato istante sono tipicamente superiori a quelle di ogni singolo gruppo. Ulteriori vantaggi sia di natura economica che di efficienza derivano poi dalla gestione e manutenzione unificata delle risorse. Nella tab. 5.1 nella pagina successiva sono riportate le quote e le tipologie di risorse in carico al cluster anteriormente alla migrazione nella nuova sala farm avvenuta nell’estate 2009. È interessante notare come le economie di scala non si sono fermate a livello locale, dal 2004 il cluster INFN di Perugia partecipa alla griglia di calcolo nazionale INFN che si inserisce nei progetti di griglia EGEE, diventando cosı̀ parte di una rete di calcolo che si compone di circa 300 siti in 50 paesi diversi. 5.1 L’architettura hardware Analizzando il cluster INFN (tab. 5.1 a fronte) possiamo riconoscere alcune tipologie di nodi introdotte precedentemente nella descrizione di un cluster generico insieme ad alcuni nodi specifici dell’installazione. Una distinzione da tenere in considerazione entrando nello specifico di un sistema reale come quello in questione è quella tra nodi interni e nodi di frontiera, le macchine del cluster infatti non sono tutte accessibili direttamente dagli utenti per questioni di sicurezza. I nodi interni sono essenzialmente di due tipi: nodi di tipo file server per lo storage dei dati e nodi di esecuzione, in questo contesto denominati worker node. Per quanto riguarda i nodi di frontiera accessibili agli utenti troviamo una maggiore varietà: le user interface sono le stazioni interattive dove gli utenti del cluster possono sottomettere job e analizzarne i risultati, un install server, che rende più semplici e veloci le operazioni di manutenzione automatizzando l’installazione e la configurazione del software sui numerosi nodi del cluster, e infine il nodo central manager, chiamato computing element, che ospita TORQUE, Maui e, dato che il sito fa parte della griglia di calcolo INFN, gli opportuni programmi per interfacciarsi con la stessa ed accettarne i job. La griglia prevede anche la presenza di nodi storage element con funzione di storage dati, qui implementati principalmente come interfacce trasparenti ai nodi interni di tipo file server. I nodi del sistema sono collegati tramite una lan standard ethernet in una struttura a stella a due livelli di profondità con uno switch primario a cui sono collegati alcuni nodi e switch secondari (fig. 5.1 nella pagina successiva). L’origine non univoca del cluster e i suoi successivi ampliamenti hanno prodotto un ambiente molto eterogeneo sia per quanto riguarda le caratteristiche hardware dei nodi, che spaziano da macchine in fase di dismissione dotate di cpu Intel Pentium III a macchine da 8 core e 16Gb di ram, sia per quanto riguarda le velocità di connessione di vari segmenti di rete, fast ethernet da 100Mbit/s o gigabit ethernet da 1Gbit/s. 24 Figura 5.1: Topologia della rete del cluster INFN di Perugia[3] Esperimento File Spazio User in- Nodi di Altri Totale Totale server disco calcolo nodi mac- core terface chine (TB) GRID 0 1,5 0 17 5 22 44 BABAR 1 0,75 1 1 0 3 6 CMS 3 10+ 2 13 5 22 44 GLAST 3 10+ 1 8 0 18 36 NA48 3 5,5 2 13 0 18 36 Teorici 0 0 1 4 0 5 10 Virgo 0 0 1 6 0 7 14 Altri fondi 0 0 0 0 1 1 2 Risorse totali 8 25+ 8 62 11 89 178 Tabella 5.1: Risorse hardware apportate dai vari gruppi di ricerca 25 5.2 Alcune particolarità del cluster La particolare genesi del cluster INFN di Perugia si riflette nelle scelte organizzative e architetturali che hanno portato alla sua configurazione attuale, in risposta alle necessità peculiari del sito sono state infatti studiate alcune scelte implementative che lo differenziano da altri siti di calcolo. Innanzitutto occorre considerare che nonostante la compartecipazione al cluster la titolarità dell’hardware messo a disposizione rimane agli stessi gruppi di ricerca e questo si traduce in legittime richieste di utilizzo delle risorse di cui tenere conto in fase di configurazione e valutazione dei risultati. Altri aspetti specifici della farm di calcolo derivano dalla sua doppia natura di cluster dipartimentale e sito grid. In genere a un sito grid si può accedere solamente se autorizzati e autenticati da una virtual organization di grid; questo ha posto alcuni problemi per quanto riguarda il sito di Perugia, dato che alcuni gruppi di ricerca che compartecipano al cluster non fanno parte ne avrebbero necessità di aderire a virtual organization per l’accesso alla griglia di calcolo INFN o EGEE.1 La soluzione a questo problema è stata l’implementazione di una duplice interfaccia al cluster, una interna attraverso le user interface e una di griglia tramite il computing element, creando cosı̀ una farm ibrida. Un ulteriore problema nato dall’ingresso in griglia del cluster sorse dal requisito che tutti i nodi del cluster disponessero di indirizzi ip pubblici da parte della versione del middleware grid allora in uso.2 Si sono cosı̀ rese necessarie modifiche al codice per poter configurare i worker node in rete privata, sia per una maggior sicurezza sia per il limitato numero di indirizzi ipv4 disponibili, modifiche che sono state successivamente accettate e sono riconfluite nel ramo di sviluppo principale del middleware grid. Un’ultima peculiarità dell’installazione consiste nell’uso pervasivo di tecnologie di virtualizzazione, numerosi nodi ospitano macchine Xen di test o di produzione, garantendo maggiore flessibilità, una generale praticità di gestione e downtime ridotti in caso di problemi. 5.3 La configurazione software La partecipazione al progetto INFN GRID ha comportato alcune limitazioni sulle scelte di implementazione del cluster, in modo particolare per quanto riguarda la scelta dello scheduler e del resource manager ci si è dovuti attenere a soluzioni supportate dal middleware di griglia. La scelta è caduta come resource manager su TORQUE e come scheduler su Maui. L’accoppiata TORQUE/Maui è considerata una soluzione 1 Per Virtual Organization si intendono qui organizzazioni che coordinano al loro interno le politiche di accesso e condivisione delle risorse. 2 Con middleware si intende un insieme di programmi con funzione di intermediazione, raccordo e supporto tra applicazioni. 26 matura dalle adeguate caratteristiche tecniche, in uso presso molti centri di calcolo, non da ultimo la disponibilità dei codici sorgenti di entrambi i software contribuisce a rassicurare sulle future possibilità di aggiornamento, manutenzione ed adeguamento. Non si è optato per un rigido partizionamento del cluster, a tutti i gruppi e utenti è infatti consentito l’uso di qualsiasi risorsa del cluster. Come si può osservare in tab. 5.2 nella pagina seguente la suddivisione delle risorse secondo i gruppi partecipanti si rispecchia invece nella configurazione delle code di sottomissione dei job al batch system di TORQUE, dove per la sottomissione di job locali sono a disposizione degli utenti quattro code per ogni gruppo che differiscono per valore di walltime e priorità, in modo da assicurare priorità ai job di durata minore su quelli a lunga esecuzione.3 In tab. 5.3 nella pagina 29 si può vedere come una quinta coda per gruppo copra i job sottomessi via grid mentre altre code permettono l’esecuzione ad alta priorità di job di amministrazione e dei job di certificazione di grid che riportano lo stato corrente del sistema. Gli altri parametri di configurazione delle politiche di scheduling messi a disposizione da Maui sono stati via via impostati e sottoposti a verifica con l’intento generale di massimizzare l’efficenza del cluster e nel contempo far corrispondere sul lungo periodo la quota di utilizzo del cluster per ogni gruppo con la quota di risorse computazionali messe a disposizione, cercando di minimizzare i tempi di risposta per gli utenti. 3 Il walltime di un job è un valore temporale che corrisponde al tempo massimo a disposizione del job per terminare la sua esecuzione passato il quale il sistema provvede a terminarlo forzatamente. 27 Esperimento Coda babar-short BABAR 24:00:00 babar-long 52:00:00 cms-short cms-long 52:00:00 glast-medium glast-long glast-infinite na48-short 2:00:00 24:00:00 52:00:00 780:00:00 2:00:00 24:00:00 na48-long 52:00:00 theo-short 780:00:00 2:00:00 theo-medium 24:00:00 theo-long 52:00:00 theo-infinite virgo-short Virgo 780:00:00 na48-medium na48-infinite Teorici 2:00:00 24:00:00 glast-short NA48 780:00:00 cms-medium cms-infinite GLAST 2:00:00 babar-medium babar-infinite CMS Walltime 780:00:00 2:00:00 virgo-medium 24:00:00 virgo-long 52:00:00 virgo-infinite test 780:00:00 24:00:00 Tabella 5.2: Principali code locali attive e relativi walltime 28 Coda Walltime babar 72:00:00 cms 120:00:00 na48 120:00:00 theophys 72:00:00 virgo 120:00:00 grid 72:00:00 cert 72:00:00 Tabella 5.3: Principali code grid attive e relativi walltime 29 Capitolo 6 TORQUE/Maui The goal of a scheduler in the broadest sense is to make users, administrators, and managers happy. Maui Administrator’s Guide TORQUE e Maui costituiscono un’implementazione dell’accoppiata resource manager/scheduler che ha conosciuto molta fortuna ed è quella scelta per la farm INFN di Perugia. 6.1 TORQUE TORQUE è un resource manager open source sviluppato da Cluster Resources il cui codice deriva dal progetto PBS, un resource manager creato originariamente per la N.A.S.A. nei primi anni novanta.1 Anche se dispone per finalità di test di un programma di scheduling minimale TORQUE viene tipicamente affiancato da programmi scheduler più specializzati, mentre a TORQUE rimangono le funzionalità di avvio, cancellazione e monitoraggio dei job e delle risorse hardware. TORQUE prevede due modalità distinte di esecuzione, in genere selezionate al momento della compilazione, che girano come demoni sul nodo central manager (pbs_server) e sui nodi di calcolo (pbs_mom), mentre i comandi utente per la sottomissione e gestione dei job risiedono, se presenti, sui nodi con funzione di interfaccia utente.2 1 Maggiori informazioni su Cluster Resources e lo sviluppo di TORQUE si possono trovare sul sito http://clusterresources.com. 2 In sistemi multitasking un demone è un programma costantemente in esecuzione in background. 31 pbs mom. Il demone TORQUE che risiede sui nodi di calcolo del cluster. pbs_mom riceve istruzioni da pbs_server e si preoccupa di gestire il singolo nodo di calcolo e i job di sua appartenenza. pbs server. Risiede sul nodo con funzionalità di central manager, ricevendo istruzioni dallo scheduler coordina l’attività dei pbs_mom e riporta allo scheduler e all’utente informazioni sullo stato dei nodi e dei job. 6.2 Maui Maui è uno scheduler open source per cluster e supercomputer come TORQUE sviluppato e supportato da Cluster Resources che commercializza anche una sua versione commerciale dal nome Moab attualmente utilizzata sui primi due e su undici dei venti supercomputer più potenti al mondo.3 Maui può interfacciarsi a diversi resource manager per l’effettiva gestione dei job e mette a disposizione un’ampia gamma di strumenti e possibilità di configurazione per le proprie politiche di scheduling (fig. 6.1). 6.2.1 Il concetto di priorità Spesso molteplici obbiettivi indipendenti vengono individuati per il batch system come massimizzare l’utilizzo del sistema, assegnare precedenze a particolari utenti o limitare i tempi di esecuzione di alcuni job, Maui risolve il problema di rappresentare e gestire queste varie esigenze assegnando ad ognuna di esse un peso che contribuisce al calcolo di un valore di priorità unico per ogni job. A questo punto i job potranno essere messi in coda in attesa di esecuzione nel loro ordine di priorità rispettando i vincoli dati. 3 lista top500 06/2008, http://top500.org Figura 6.1: Interfaccia Maui/TORQUE 32 I vari elementi che Maui considera nel calcolo della priorità sono per una più facile gestione raggruppati in una gerarchia ad albero a due livelli di componenti e sottocomponenti come riportato nella tab. 6.1 nella pagina successiva, a cui possono essere associati pesi distinti e valori massimali.4 il contributo al valore di priorità di ogni sottocomponente è calcolato come peso componente ∗ peso sottocomponente ∗ valore associato al sottocomponente. Per le impostazioni di default il valore dei pesi delle componenti è impostato a 1 e quello delle sottocomponenti a 0 con l’eccezione della sottocomponente QUEUETIME il cui peso è impostato a 1. Se non altrimenti specificato quindi Maui adotterà una semplice politica FIFO per la gestione della coda dei job.5 6.2.2 Allocazione dei nodi Oltre che preoccuparsi di gestire temporalmente i job in attesa compito dello scheduler è anche quello di gestire l’allocazione delle risorse disponibili decidendo su quali nodi del cluster ogni particolare job debba essere eseguito. L’allocazione delle risorse presenta particolare criticità in caso di hardware eterogeneo o in presenza di politiche di prenotazione o di garanzia del servizio e Maui mette a disposizione diversi algoritmi allo scopo: CPULOAD Sono selezionati i nodi con la massima capacità di calcolo disponibile, utile per sistemi in timeshare. FIRSTAVAILABLE I nodi sono allocati nell’ordine in cui sono presentati dal rerource manager. LASTAVAILABLE Assegna i job più opportuni nell’intervallo tra prenotazioni successive della stessa risorsa implementando un best fit temporale che minimizza l’impatto di politiche di prenotazione.6 PRIORITY Permette di specificare un set di priorità per i vari attributi statici e dinamici dei nodi da rispettare. MINRESOURCE Alloca i nodi scegliendo quelli con le risorse minime possibili. CONTIGUOUS Implementa un algoritmo di allocazione lineare richiesto su alcuni sistemi di marca HP. 4 Il livello delle componenti è composto dai seguenti gruppi: CRED gruppo delle credenziali, FS Fairshare, RES Risorse, SERV Livello di servizio corrente, TARGET Obbiettivi, USAGE Risorse in uso, solo per job in esecuzione. 5 First In, First Out: i job sono accodati per tempo di sottomissione. 6 Una strategia “best fit” prevede che i job vengano inseriti nella partizione di tempo con dimensione più vicina alla durata stimata del job. 33 Componente Sottocomponente Metrica CRED USER GROUP ACCOUNT QOS CLASS Priorità Priorità Priorità Priorità Priorità FSUSER FSGROUP FSACCOUNT FSQOS FSCLASS Uso storico dell’utente Differenziale sull’uso storico Differenziale sull’uso storico Differenziale sull’uso storico QOS Differenziale sull’uso storico RES NODE PROC MEM SWAP DISK PS PE WALLTIME Numero di nodi richiesti Numero di processori richiesti Memoria totale richiesta (in MB) Memoria virtuale richiesta (in MB) Spazio disco richiesto (in MB) Processori-secondi richiesti Processori equivalenti richiesti Valore di walltime richiesto (in secondi) SERV QUEUETIME XFACTOR BYPASS Permanenza in coda (in minuti) Fattore di espansione minimo Numero di volte che il job è stato sorpassato tramite backfill TARGETQUEUETIME Tempo rimanente al raggiungimento dell’obbiettivo di permanenza in coda (esponenziale) Distanza dall’obbiettivo di espansione (esponenziale) FS TARGET TARGETXFACTOR USAGE CONSUMED REMAINING PERCENT dell’utente del gruppo dell’account relativa alla QOS della coda del gruppo dell’account relativo alla della coda Processori-secondi dedicati Processori-secondi rimanenti Tempo del walltime richiesto consumata (in percentuale) Tempo dall’avvio del job (in secondi) EXECUTIONTIME Tabella 6.1: Componenti e sottocomponenti per il calcolo della priorità in Maui. 34 MAXBALANCE Cerca di allocare ad un job un set di nodi il più possibile bilanciato, in genere riguardo alla velocità di calcolo. FASTEST Alloca i nodi in ordine di velocità se l’informazione è presente. LOCAL Se nessuno degli algoritmi proposti risultasse adeguato è prevista la possibilità di interfacciarsi a programmi di allocazione esterni appositamente realizzati. 6.2.3 Meccanismi di Fairness Come per la vita reale cosı̀ per le decisioni di scheduling ognuno ha una propria definizione o sensazione di cosa è giusto o non è giusto fare. Mentre Maui non ci può aiutare nella decisione prettamente politica di scegliere una definizione di giustizia o fairness il più possibile condivisa con gli utenti del batch system ci offre però un’ampia gamma di strumenti per la sua implementazione. Politiche di “throttling” Le politiche di throttling specificano limiti all’uso del sistema da parte di utenti, gruppi, account, classi QOS o code. Questi limiti possono riguardare una molteplicità di aspetti quali il numero di job già in esecuzione, il numero di processori o la memoria occupata e altri, per partire un job deve rispettare tutti i limiti imposti. È permesso anche l’utilizzo di coppie di limiti, un primo limite di tipo hard e un secondo limite di tipo soft meno stretto da utilizzare in condizioni di basso carico del cluster per mitigare il rischio che strette politiche di throttling riducano l’efficenza complessiva del cluster. Fairshare I meccanismi di Fairshare introducono una visione storica dell’uso delle risorse tra i fattori che influenzano la priorità dei job. Un amministratore può impostare specifici target percentuali di utilizzo delle risorse per utenti, gruppi, account, classi QOS e code insieme alla durata e al numero di finestre temporali da tenere in considerazione. I parametri a livello di sistema da considerare sono la durata e il numero delle finestre, il loro apporto al calcolo del valore di fairshare (un fattore di decadimento permette di far “pesare” maggiormente le finestre più recenti) e la metrica da utilizzare. La finestra temporale corrente viene aggiornata fino al raggiungimento della durata impostata quando il sistema la chiude e ne inizia una nuova e le statistiche sui dati cosı̀ compilati vengono salvate su disco per una futura consultazione da parte dell’amministratore. Nel caso si voglia intervenire se l’utilizzo di una certa risorsa risultasse minore o 35 maggiore di un dato livello il sistema di fairshare prevede inoltre la possibilità di impostare valori di floor o caps oltre che valori target. Manager delle allocazioni Un manager delle allocazioni (detto anche banca delle allocazioni o banca di cpu) implementa un meccanismo simile al sistema bancario per la gestione dei limiti d’uso delle risorse a lungo termine. Una volta specificato come il sistema debba ripartire le risorse in una finestra temporale abbastanzanza lunga ad ogni account o gruppo di account del sistema viene assegnato un numero di “gettoni” corrispondenti e monitorato il loro utilizzo. I gettoni di allocazione possono riguardare la totalità del sistema o specifiche risorse e presentare date di attivazione e scadenza. Possono inoltre essere previsti “sconti” o maggiorazioni per l’utilizzo delle risorse in particolari fasce orarie o l’impostazione di requisiti elevati. 6.2.4 Controllo dell’accesso alle risorse Prenotazioni Il sistema di prenotazioni (advance reservation) permette di garantire la disponibilità di alcune risorse ad un dato istante futuro nel tempo. Ogni prenotazione consiste in una lista delle risorse da prenotare, un intervallo temporale di prenotazione e una lista di accesso alle risorse. Partizioni In sistemi particolarmente grandi o complessi si possono riscontrare suddivisioni interne al cluster per motivi tecnici come la topologia scelta per la rete di collegamento o politici (il cluster è stato formato da gruppi indipendenti che mantengono il controllo sulle proprie risorse); Maui permette di rispettare i confini di queste suddivisioni forzando i job a non utilizzare risorse che appartengano a partizioni diverse e consentendo differenti politiche di gestione, limiti e anche algoritmi di scheduling per partizione. Se non altrimenti specificato una singola partizione di default coprirà l’intero intervallo di risorse del cluster. QOS L’ultimo meccanismo di controllo di accesso implementato da Maui è quello denominato Quality of Service, una serie di liste di privilegi riguardanti priorità e eccezioni sulle politiche di accesso alle risorse a cui gli utenti, i gruppi, gli account o le code possono accedere. L’utente aventene diritto può quindi richiedere una specifica classe di QOS alla sottomissione di un job e la sua successiva modifica. 36 6.2.5 Ottimizzazione del comportamento dello scheduler Backfill Il sistema di backfill, se abilitato, permette eccezioni alla regola che l’avvio dei job eleggibili per l’esecuzione segua il loro valore di priorità. Poichè tutti i job e le prenotazioni possiedono un valore limite al loro tempo di esecuzione Maui può stimare quanto tempo potrà passare prima che il job in coda in cima alla lista di priorità abbia a disposizione le risorse richieste, mentre normalmente non sarebbe possibile avviare altri job in questo periodo di attesa a backfill attivato si può procedere a creare una prenotazione per il momento in cui le risorse si libereranno e lanciare altri job che non interferiscano con la prenotazione. Dovendosi affidare per una stima di durata dei job ai valori massimi dati dal loro tempo limite il sistema tende per sua natura a favorire job di breve durata che richiedono poche risorse che più facilmente possono entrare nelle finestre di backfill mentre può rallentare l’esecuzione di grandi job ad alta priorità. Questo effetto tipicamente viene però più che compensato dall’aumento di efficienza del sistema che porta spesso anche i job che sono penalizzati ad essere eseguiti in un tempo precedente a quello ipotizzabile senza l’intervento del backfill. Per quanto riguarda la scelta dell’algoritmo da utilizzare per selezionare il job da eseguire in una data finestra di backfill Maui propone varie alternative, tra cui best-fit e first-fit. Un’altro parametro da considerare è quello riguardante la profondità delle prenotazioni dalla lista di priorità da effettuare, dove valori alti garantiscono che job ad alta priorità non vengano scavalcati mentre valori bassi (il valore predefinito è 1) portano ad un backfill più efficiente. Set di nodi In presenza di cluster eterogenei job di natura parallela possono soffrire se non effettuano un adeguato bilanciamento del carico di lavoro tra processi e si conformano alle prestazioni dei nodi più lenti, per evitare questo problema e forzare i job su nodi omogenei possono essere richiesti degli insiemi di nodi dotati di caratteristiche simili senza che ci sia bisogno di specificarne i valori. Job preemption Spesso job a lunga esecuzione non richiedono criticità temporali di esecuzione e potrebbero rilasciare almeno temporaneamente le risorse da loro occupate ad altri job dalle richieste più pressanti. Questo meccanismo non è solo prerogativa dei sistemi multitasking ma può essere utile anche in sistemi batch dove calcoli a lungo termine si alternano a job ad altissima priorità o dove i nodi di calcolo siano ad esempio computer desktop riadattati a risorse del cluster quando non utilizzati per il loro scopo primario. 37 La preemption di un job può essere iniziata attraverso l’intervento manuale, le politiche di QOS o l’uso di un algoritmo di backfill che lo preveda. La sorte del job sottoposto a preemption può essere la sua sospensione sul nodo, l’inizializzazione di un suo punto di checkpoint se sono presenti nodi di checkpoint o la sua cancellazione con o senza la sua reimmissione in coda processi. 6.2.6 Log e simulazioni Data la flessibilità e la complessità dei vari meccanismi che influenzano il comportamento dello scheduler una precisa raccolta di informazioni risulta chiaramente un passo necessario per ogni valutazione sul sistema e su eventuali modifiche alla configurazione. Al fine di consentire un’analisi approfondita dello storico i comandi diagnostici che Maui mette a disposizione per l’interrogazione dello stato del cluster e dei job non sono sufficienti, per questo il sistema di fairshare, anche se non abilitato, salva su disco le statistiche d’uso per ogni finestra temporale e ogni job, una volta terminato, viene registrato come record in un file di tracce di lavoro (workload traces) insieme alle risorse richieste e utilizzate, le credenziali associate, i tempi di sottomissione e lancio e altre informazioni, permettendo la compilazione di statistiche dettagliate sull’uso del batch system e sulle scelte dello scheduler. Una caratteristica di Maui rilevante è il suo supporto alle simulazioni. Una volta attivata la modalità di simulazione Maui non effettuerà più chiamate al resource manager nè avrà bisogno di occupare risorse di calcolo ma andrà a simulare in tempi molto condensati l’andamento dello scheduling. La simulazione dovrà essere per forza di cose non interattiva, Maui si aspetta infatti di trovare oltre al proprio file di configurazione un file delle risorse del batch system simulato (resourse trace) e un file con i job da lanciare in sequenza (workload trace). Sia il formato del file di descrizione delle risorse sia quello dei job da lanciare corrisponde a quello che i due file hanno in caso di uso reale del sistema e questo permette (superando qualche problema riscontrato) di ri-simulare situazioni riscontrate nella realtà oltre che situazioni di test ipotetiche. 38 Capitolo 7 L’infrastruttura di analisi, simulazione e ottimizzazione Mi domando che cosa facciano ora tutti questi server, se nessuno è più qui ad usarli? Questo dice che offre una stabilità del 99,9% garantita! È quasi il 100%! il Cartellonista Virtuale 7.1 Alcuni problemi da affrontare Osservando il numero delle variabili di configurazione e di meccanismi di regolazione che mette a disposizione un software di scheduling come Maui si possono immediatamente apprezzare la potenza e la flessibilità messe al servizio dell’amministratore. Queste caratteristiche, necessarie per affrontare le problematiche di progettazione e gestione di un sistema batch, portano in dote d’altra parte anche una notevole complessità. Una volta che le decisioni “politiche” sugli obiettivi di gestione del sistema batch siano state prese, come quote di utilizzo, percentuali di efficenza complessiva del sistema e varie particolari esigenze, una questione questa che può essere già di suo problematica e delicata ma al di fuori del quadro tecnico vero e proprio preso qui in esame, rimane da stabilire con quali mezzi e regole queste debbano essere implementate. In questo contesto la configurazione del sistema batch e in particolare delle complesse politiche di scheduling può rivelarsi spesso un’arte più che una scienza, dove l’esperienza dell’amministratore fa da guida empirica e, in mancanza di 39 un ambiente di test simile a quello di produzione, improponibile per i costi associati, trovata una soluzione più o meno soddisfacente si procede per piccoli e graduali aggiustamenti, per non compromettere la produttività del sistema in uso. La vastità dello spazio di configurazioni in gioco, insieme alla necessità di mantenere stabile il sistema batch preclude quindi l’esplorazione di scelte radicali nella configurazione e limita fortemente la reattività dell’ottimizzazione del sistema alla variabilità nel tempo dei carichi di lavoro. Un inizio di soluzione al problema ci viene offerto da Maui e dalla modalità di simulazione di scheduling che questo supporta. In linea di principio Maui consente in una frazione del tempo e senza l’utilizzo effettivo delle risorse di calcolo di procedere alla simulazione di situazioni ipotetiche o effettivamente affrontate dallo scheduler, permettendo l’osservazione delle conseguenze sulla simulazione di variazioni dei job in input, delle risorse hardware a disposizione, o della configurazione dello stesso Maui. L’effettiva utilità di tutto questo è però subordinata ad alcune precondizioni: • Una validazione del simulatore. Una necessaria confidenza nei risultati delle simulazioni è stata acquisita tramite un’analisi di confronto per alcune configurazioni tra un ambiente di test reale e uno simulato e una successiva validazione di tipo Monte Carlo.1 Il precedente lavoro di confronto ha messo in luce una divergenza tra casi reali e simulati che è possibile addebitare soprattutto a fenomeni di quantizzazione dei tempi e overhead presente nei sistemi reali.2 Anche se questi fenomeni non permettono una coincidenza uno a uno delle assegnazioni job-nodo effettuate dallo scheduler d’altra parte non inficiano la validità delle simulazioni per quanto riguarda previsioni a scale più grandi, quelle più interessanti per una valutazione delle politiche di scheduling.[3] • Alcuni problemi non documentati sono stati riscontrati durante le predette analisi del simulatore Maui. Una prima difficoltà è sorta quando ci si è resi conto che importando schemi di sottomissione di job prelevati da log, una traccia reale dalla farm o dal sistema di test per esempio, tutti i tempi fanno riferimento al passato e il simulatore al suo avvio si comporta come se le sottomissioni fossero già tutte effettive, anche se nella realtà erano presenti distanze temporali anche 1I metodi Monte Carlo costituiscono una classe di algoritmi che sfruttano una campionatura casuale. 2 La quantizzazione dei tempi deriva dal fatto che Maui permette di impostare una risoluzione temporale minima di un secondo tra le interrogazioni al resource manager e questo si riflette in tempi discreti nello scheduling e nei log. Il fenomeno risulta via via più modesto all’aumentare del rapporto tra tempi di esecuzione dei job e risoluzione temporale e si rivela trascurabile per le durate in termini di ore o giorni tipiche di sistemi batch reali. Per overhead si intende qui l’insieme di tutti i tempi necessari al completamento di una operazione non derivanti dalla computazione vera e propria ma da limitazioni di velocità di componenti come i dischi e la rete alcuni dei quali sono naturalmente assenti in una simulazione. 40 notevoli. Questo non succede se i riferimenti temporali sono nel futuro rispetto all’ora di sistema. Un secondo problema sorge dalla sfortunata circostanza che in simulazione Maui si aspetta di trovare i job in ordine di sottomissione ma al momento della scrittura delle tracce di log questi vengono scritti solo quando il sistema dispone di tutti i parametri presenti sulla traccia, cosa che accade solamente alla chiusura del job. Nella realtà quindi i job vengono registrati secondo l’ordine di fine di esecuzione.[3] Questi inconvenienti compromettono il lavoro del simulatore proprio nella sua modalità più interessante e promettente, quando cioè si vuole simulare una precedente situazione reale con gli stessi parametri per assicurarsi della bontà del simulatore, o con una differente configurazione per valutarne l’impatto. Un uso efficace del simulatore non può prescindere dalla soluzione di queste problematiche, è stato necessario quindi lo sviluppo di tool per il riordino delle tracce e per lo spostamento dei relativi riferimenti temporali.[3] • Il simulatore richiede alcune procedure manuali per il suo set-up e un’analisi difficoltosa dei risultati a partire dalle tracce di output, per questo risulta utile per simulazioni sporadiche ma mal si pone per un suo uso intensivo. Lo sviluppo di un’infrastruttura di automazione costituisce quello che potrebbe essere chiamato in termini militari un “moltiplicatore di forze” per il simulatore Maui consentendone invece l’utilizzo in estese campagne di simulazione. Questa infrastruttura, che in un certo modo finisce per reimplementare un batch system specializzato, affianca alla simulazione strumenti per l’analisi e l’ottimizzazione tramite algoritmi genetici permettendo lo studio del sistema al variare di configurazioni hardware, parametri di scheduling e carichi di lavoro e la sua ottimizzazione, aprendo scenari di risposta quasi in tempo reale ai cambiamenti nel carico di lavoro. 7.2 Metriche di sistema Ogni utente ha in genere la propria opinione su quanto il sistema batch funzioni più o meno bene ma per analizzare in modo obiettivo e procedere poi ad un’ottimizzazione del sistema occorre passare da una valutazione informale ad una misura per quanto possibile oggettiva della qualità di funzionamento dello scheduler. A questo scopo è necessario individuare una o più “unità di misura” o metriche che ci possano aiutare nella quantificazione dei vari aspetti del problema. 41 7.2.1 Metrica di equità Una prima metrica che è stata proposta è stata quella di equità, che si propone di misurare quanto il sistema riesca a rispettare le percentuali di risorse attribuite ad ogni utente o gruppo, un importante parametro di soddisfazione degli utenti.[3] L’equità del sistema verso un utente (o gruppo) viene definita come “piena” o uguale a 1 se per ogni dato istante almeno la quota di risorse di cui ha diritto è assegnata a suoi job, o non ci sono suoi job in coda. É definita come “nulla” o uguale a 0 quando non ha risorse allocate e ci sono suoi job in coda. Per il calcolo dell’equità si procede innanzitutto a calcolare un’equità “elementare” per ogni intervallo di tempo ∆Ti senza nuove assegnazioni o rilasci di risorse secondo la formula min(Q, ris − R) (7.1) ris dove Q è il numero di job in coda di attesa, ris il numero di risorse (nodi di calcolo) Eq = 1 − che spettano all’utente o gruppo in esame, R il numero di job in esecuzione. Ora è possibile definire l’equità globale del sistema come n P Eqi ∆Ti Eq = i=1P n ∆Ti (7.2) i=1 dove n è il numero di intervalli temporali presi in considerazione. 7.2.2 Metrica di efficienza La metrica di efficienza cerca di misurare il grado di utilizzo del sistema in condizioni di carico. L’efficienza è definita come piena o uguale a 1 quando tutti i nodi di calcolo sono impegnati nell’esecuzione di job e nulla quando nessun job è in stato di esecuzione nonostante esistano job in coda.[3] L’efficienza elementare, in ogni intervallo temporale, è definita come il rapporto tra i job in esecuzione e le risorse di sistema: job (7.3) ris Per ogni job viene inoltre definita l’“efficienza relativa alla sua attesa” come piena se Ef fi = il suo tempo di attesa in coda è stato nullo Ef fjob = 1 (7.4) o come media pesata sulla durata degli intervalli di tempo ∆T passato in coda delle efficienza elementari negli altri casi m P Ef fjob = Ef fi ∆Ti i=1 m P i=1 42 (7.5) ∆Ti Figura 7.1: L’infrastruttura hardware Definitito come ∆Q il tempo in coda per ogni processo l’efficienza complessiva del sistema risulta essere n P Ef f = Ef fjobi ∆Qi i=1 n P (7.6) ∆Qi i=1 7.3 Anatomia dell’infrastruttura L’implementazione dell’infrastruttura visibile in fig. 7.1 ha comportato la preparazione di alcune macchine, sia reali che virtuali, per una serie di mansioni: un cluster di test TORQUE/Maui consistente di 4 nodi per i test e la validazione del simulatore, una macchina con funzione di repository per conservare le tracce di scheduling reali, quelle simulate e le direttive al sistema di simulazione e un numero variabile di macchine virtuali per le simulazioni vere e proprie. 7.3.1 La farm di produzione La farm di produzione consiste nel cluster della sede locale INFN precedentemente descritto. Il cluster costituisce sia una fonte di preziosi dati da analizzare derivanti da un uso reale “sul campo” di TORQUE e Maui sia l’obbiettivo ultimo delle ottimizzazioni delle politiche di scheduling generate con l’infrastruttura di ottimizzazione. 43 7.3.2 La farm di test Il valore e il limite dei dati generati dalla farm locale INFN risiede proprio nella sua natura di cluster di produzione, se da un lato fornisce tracce “reali” che non sarebbe possibile ottenere altrimenti e che forniscono un necessario riscontro tra la teoria delle simulazioni e la pratica di un batch system “vero” dall’altro per la sua complessità non si presta a una facile comprensione dei meccanismi che legano i carichi di lavoro e i parametri di configurazioni alle decisioni dello scheduler. Per questo si è resa necessaria l’implementazione di un cluster di test che costituisca un ambiente semplificato da usare come modello su cui mettere alla prova e verificare semplici configurazioni, non rinunciando al contempo alla genuinità di un sistema reale. Il cluster di test è costituito da una macchina central manager e 4 nodi di tipo worker node in rete locale, lo stesso central manager viene usato per il ruolo di user interface del cluster, mentre non sono necessari nodi con funzioni di file server o checkpoint dato il compito esclusivo di ricerca e valutazione. Come sistema operativo per i 5 nodi è stato scelta la distribuzione GNU/Linux Debian 4.0, avendo scelto il protocollo SSH per i trasferimenti file tra i nodi del cluster si è poi provveduto a installare e configurare su tutti i nodi OpenSSH in modalità host based authentication che prevede l’autenticazione per tutti gli utenti a livello di macchine tramite chiavi crittografiche pre-distribuite, senza bisogno di password o chiavi crittografiche distinte per ogni utente.3 Conclusa la configurazione dell’ambiente operativo si è passati alla installazione del batch system vero e proprio. TORQUE versione 2.3.0 è stato installato con supporto per i trasferimenti tramite scp, nel nodo centrale nella sola modalità server con le opzioni di compilazione --disable-mom e --with-scp, nei nodi di esecuzione è stato disabilitato il demone server e abilitato il client con --disable-server e --with-scp, per quanto riguarda la sua configurazione è poi bastato impostare i nomi dei worker node nel file nodes sul central manager e il nome della macchina central manager su server_name sui worker node. Sul central manager è poi stato compilato ed installato come scheduler Maui nella versione 3.2.6p19, configurato per interfacciarsi con il demone TORQUE server locale con la direttiva di compilazione --with-pbs e le apposite opzioni nel file maui.cfg. 7.3.3 Il repository server La macchina di repository costituisce il cuore dell’infrastruttura hardware, tra i suoi compiti ricadono i servizi di gestione e deposito delle tracce reali e simulate, viene inoltre usato per l’avvio e la sincronizzazione delle operazioni e per servizi di comunicazione del resto dell’infrastruttura. Come server di repository è stata allestita una 3 SSH o Secure Shell è un protocollo di rete per lo scambio di dati su connessioni criptate, OpenSSH è una suite di programmi che implementano il protocollo sviluppati all’interno del progetto OpenBSD. 44 macchina con sistema operativo GNU/Linux Debian. Per poter assolvere alla funzione di deposito di tracce Maui si è scelto di implementare un repository rsync. rsync è un programma di trasferimento file sviluppato come alternativa più sicura ed efficente a rcp che, dove possibile, trasferisce solamente le differenze tra file minimizzando il volume di dati. Usato sul repository server in modalità demone accetta connessioni da macchine remote per servizi di trasferimento file anche con connessioni sicure usando il protocollo ssh.4 Allo stesso modo ssh viene usato in modalità demone come interfaccia dei tool di gestione al repository. Struttura del repository Il repository rsync si occupa dell’immagazzinamento sia delle tracce di log Maui, derivate dalla farm di produzione, dal cluster di test o da simulazioni, sia delle direttive alle macchine di simulazione. Per la gestione di queste differenti informazioni è stato necessario prevedere una precisa struttura logica del repository illustrata in fig. 7.2 nella pagina successiva. traces. Contiene alcune sottodirectory speciali e tutte le directory con gli input e output del sistema di simulazione. La directory di ogni lavoro di simulazione contiene alcuni file di configurazione e le eventuali sottodirectory con le tracce. traces/000000-production-farm. La prima directory speciale contiene in sottodirectory, il cui nome segue la convenzione ANNO-MM-GG, le tracce reali provenienti dalla farm di produzione, opportunamente compresse. Viene popolata giornalmente da uno script in cron sul CE. traces/000000-jobqueue. Una seconda directory speciale che contiene una serie di direttive per le macchine di simulazione, raggruppate in un singolo run. All’interno delle sottodirectory un file denominato command.sh contiene i comandi da eseguire e, a loro esecuzione terminata, gli eventuali output ed errori delle macchine di simulazione coinvolte sotto forma di file dal nome corrispondente all’identificativo della macchina ed estensione .o (gli output) e .e (gli errori). traces/test-foo. Una tipica directory contenente un lavoro di simulazione od ottimizzazione. Al suo interno possiamo trovare alcuni file di configurazione da preparare anticipatamente e una serie di sottodirectory, una per ogni singola simulazione o test effettuato. Nel file di configurazione run.conf andremo a scrivere le necessarie opzioni del run, resources specifica le risorse del cluster nel formato richiesto da Maui, config-simul-template è un template del file 4 Un programma demone è un programma di servizio in genere in esecuzione in background dal momento di avvio della macchina al suo arresto. ssh o secure shell è un protocollo e un programma per la connessione remota che fornendo un canale criptato per la comunicazione. 45 Figura 7.2: Struttura del repository rsync 46 di configurazione dove opportune variabili verranno poi sostituite per la generazione dei file di configurazione Maui per ogni simulazione o test del run, workload-real-unscaled è l’eventuale file di log di traccia da ridare in pasto al simulatore. Le sottodirectory create via via che l’esecuzione del run procede vengono denominate concatenando ad un prefisso comune il genotipo identificativo della singola simulazione o test, cioè l’effettivo valore assunto dalle variabili del template di configurazione. All’interno delle sottodirectory si trovano tutti i file necessari alla simulazione o test in esame come i file config e resources generati a partire dal template e dal file resources del run, un file denominato duration con il tempo di esecuzione della simulazione o test e i file di traccia workload reali e simulati nelle varie versioni riordinate o meno, spostate o meno nel tempo. Come si vedrà ad ogni lavoro e ad ogni simulazione o test sono ulteriormente associate alcune proprietà che ne descrivono tipologia, stato e altri parametri. 7.3.4 Le macchine di simulazione Le macchine di simulazione sono macchine virtuali xen lanciate e fermate alla bisogna su cui è stato installato lo scheduler Maui e gli script di interfaccia con il server di repository. Possono eseguire job di simulazione e test dello scheduling o qualsiasi altra direttiva impartita loro impostando run nel repository. Le macchine di simulazione sono impostate per lanciare all’avvio lo script superexecute.sh che interroga ad intervalli regolari il server di repository alla ricerca di job. 7.3.5 Stazioni di controllo e analisi Qualsiasi macchina su cui siano stati installati i comandi client forniti da Maui e i necessari script sviluppati può essere utilizzata per il lancio di job di simulazione o ottimizzazione e per la successiva analisi dei risultati. 7.4 Fisiologia e software dell’infrastruttura L’ambiente unix tipico delle installazioni TORQUE/Maui offre una vasta scelta di linguaggi di script e potenti comandi di sistema di cui è possibile avvalersi, il software per l’infrastruttura di analisi, simulazione e ottimizzazione è stato quindi implementato sotto la forma di una nutrita serie di script interagenti tra loro, la maggioranza dei quali si è scelto di implementare in linguaggio di shell Bash o in Perl per considerazioni 47 di standardizzazione dei requisiti software e praticità nel richiamare comandi e utility di sistema.5 7.4.1 I run Con il nome di “run” si è chiamato una serie automatizzata di job di test o simulazione in correlazione tra loro ma corrispondenti a specifiche variazioni nella configurazione di scheduling. Un numero variabile di run di vario tipo coesiste nel repository server in vari stadi di avanzamento: fulltree run. Un run di tipo fulltree o enumerativo implementa l’idea di simulare tutte le possibili combinazioni di una o più variabili di configurazione, con l’intento di esaminarne successivamente l’impatto sul sistema. Necessariamente ci si dovrà limitare a un numero di variabili limitato dato che al crescere delle variabili da considerare il numero di configurazioni da simulare cresce velocemente fino a oltrepassare facilmente le risorse di simulazione e il tempo realisticamente disponibile. I run di tipo fulltree possono essere eseguiti in modalità locale (l’intera esecuzione avviene sulla macchina locale e i risultati rimangono a disposizione in locale) o remota, nel qual caso i risultati vengono caricati sul server di repository e l’esecuzione può essere coordinata in collaborazione in parallelo tra più macchine di simulazione. random run. Un run random consiste di un certo numero di configurazioni di combinazioni casuali per le variabili date. L’idea è quella di sottoporre una serie di configurazioni casuali a sistemi di simulazione e sistemi cluster di test per la successiva comparazione dei risultati e un’eventuale validazione Monte Carlo del simulatore Maui limitatamente ai parametri selezionati. genetic run. Il tipo di run genetico, raffigurato in fig. 7.3 a fronte è quello più interessante per quanto riguarda l’obbiettivo finale di ottimizzazione del cluster di produzione: si tratta infatti di un run di ottimizzazione genetica sulle variabili date. Particolare cura deve essere posta alla scelta della funzione di fitness che deve rispecchiare la totalità degli obbiettivi desiderati rappresentandoli in modo matematico. 7.4.2 Il sottosistema delle feature Allo scopo di memorizzare lo stato di avanzamento di run e tracce e alcuni altri parametri e meta-dati loro associati è stato sviluppato un sistema per impostare per 5 Bash (Bourne Again SHell) è una shell unix scritta per il progetto GNU della Free Software Foundation usata in molte distribuzioni linux come shell predefinita di sistema. Perl è un potente linguaggio interpretato di scripting. 48 Figura 7.3: Struttura logica dell’infrastruttura di ottimizzazione ogni directory sul server di repository delle feature o caratteristiche a cui sono associati uno o più stati a piacere.6 La natura arbitraria delle feature e degli stati associati assicura la versatilità del sistema e la possibilità di accomodare facilmente successive espansioni. Dato che le feature sono una proprietà di tracce e job che risiedono sul server e non hanno un corrispettivo locale sulle macchine di simulazione o analisi sono state implementate sotto la forma di file di testo nascosti sulle directory del repository corrispondenti il cui nome è del tipo .\{nome-feature}-status.lock e il cui contenuto corrisponde agli stati associati alla feature, uno per riga se multipli. L’interfaccia alle feature per l’utente e gli script delle macchine di simulazione è costituita dagli script sync-traces.sh, con il quale è possibile leggere, scrivere e cancellare gli stati relativi alle feature e ls-feat.sh che ritorna una lista di tracce con uno specifico stato di una feature a scelta. L’uso delle feature nel sistema di simulazione e ottimizzazione coinvolge i job in traces/000000-jobqueue, gli alberi delle tracce di ogni singolo run e le tracce al loro interno. Per quanto riguarda i job la prima feature letta dal sistema è type, che indica se il run è da eseguirsi in modo parallelo o meno. Gli script per la gestione dei job tengono poi traccia dei stati di avanzamento attraverso la feature status, che può assumere i valori ready, per un job pronto ad essere eseguito, running, per un job di tipo parallelo in fase di esecuzione, taken, per un job non parallelo già preso in carico e in esecuzione, exitwait, usato durante l’esecuzione di job paralleli per segnalare un lock temporaneo da parte di una macchina di simulazione e ok che indica l’avvenuta esecuzione e completamento del job. Con la feature crunning vengono infine memorizzati via via gli identificativi delle macchine che hanno in esecuzione il job. Per i run viene invece utilizzata la feature status, che assume i valori ready, pronto, generating, in fase di creazione dell’albero delle tracce, running, in lavorazione, runned, completato, le feature startgenerating e stopgenerating con le date di inizio e fine generazione dell’albero delle tracce, start{idclient} e stop{idclient} con le date di start e stop per ogni client e nuovamente crunning con gli identificativi delle macchine che stanno eseguendo il run. 6 Per meta-dati, “dati sui dati”, si intendono gli attributi e le informazioni descrittive dei dati veri e propri, nel caso specifico tracce e run. 49 Alle singole tracce sono infine associate le feature status che assume i valori ready, simulating e simulated in successive fasi, clid con l’identificativo della macchina che simula la traccia, duration con la durata in secondi della simulazione. In caso di ottimizzazioni genetiche è poi presente la feature fitness con indicato il valore di fitness della traccia. 7.4.3 Sottomettere un run: run.conf e command.sh La sottomissione di un run all’infrastruttura inizia con la creazione di una directory locale con all’interno gli eventuali file di traccia necessari, il file scheletro di configurazione di Maui config-simul-template, il file delle risorse Maui resources e un file chiamato run.conf che contiene tutte le informazioni sul tipo e i parametri del run. Per la descrizione delle opzioni di configurazione, che prendono la forma OPZIONE=valore, si rimanda all’appendice A. Una volta completata la directory di base del run quest’ultima deve essere immessa in traces nel server di repository con sync-traces.sh. command.sh è il file che le macchine di simulazione andranno a scaricare ed eseguire. Si tratta di un semplice file eseguibile, in genere un batch file che richiama lo script run.sh passandogli i parametri opportuni come la directory di base del run precedentemente creata e il server di repository. Il file command.sh va a sua volta caricato nel repository, in una directory all’interno di traces/000000-jobqueue. Una volta caricati tutti i file nel repository l’unica cosa che rimane da fare è impostare a ready la feature status della directory di base del run, a parallel la feature type della directory del job dentro traces/000000-jobqueue che contiene il file command.sh se il tipo di job è parallelo, e, successivamente, a ready la sua feature status. Le macchine di simulazione procederanno autonomamente all’individuazione e all’esecuzione dei nuovi job sottomessi. 7.4.4 Tool per la gestione dell’infrastruttura functions.sh La necessità di raggruppare alcune funzioni che implementano operazioni comuni in vari script ha portato alla nascita di questo script con funzioni di libreria che mette a disposizione le seguenti subroutine: check prerequisite(). Controlla l’esistenza di un file su disco, anche in formato compresso. check variable(). Controlla se una variabile è vuota. 50 check status(). Controlla se lo stato di una feature di traccia sul repository server corrisponde allo stato dato. set status(). Imposta uno specifico stato per una data feature di traccia sul repository server. unset status(). Cancella, se esiste, uno specifico stato per una data feature di traccia sul repository server. flush status(). Cancella tutti gli stati per una data feature di traccia sul repository server. superexecute.sh Lanciato una volta effettuato il boot dalle macchine di simulazione, contatta ad intervalli il repository server per cercare nuovi job di simulazione o test a cui la macchina può partecipare, quando ne trova disponibili provvede a scaricarli in locale ed eseguirli, preoccupandosi poi di ricaricare sul server gli output e gli eventuali errori (fig. 7.4 nella pagina successiva). Le operazioni di interrogazione e aggiornamento dal server dei job vengono effettuate in mutua esclusione con le altre macchine di simulazione tramite l’utilizzo di procedure di lock/unlock richiamando scripts/manager/client.py, le operazioni di interrogazione, impostazione e lettura dello stato dei job e trasferimento file si appoggiano invece a scripts/utils/ls-traces.sh, scripts/utils/ls-feat.sh e scripts/utils/sync-traces.sh. scripts/manager/client.py e scripts/manager/scheduler.py L’accesso parallelo delle macchine impegnate in simulazioni ed ottimizzazioni può costituire un pericolo per l’integrità dello stato e la correttezza dei dati nel repository, se l’uso delle feature tiene aggiornati i vari client sullo stato corrente di job e tracce rimangono finestre temporali critiche nell’accesso vero e proprio al repository che, per quanto strette, potrebbero portare a risultati imprevedibili in caso di alcune sfortunate operazioni simultanee se non protette. A questo scopo è stato implementato un sistema di messaggi tra client per l’accesso in mutua esclusione al repository scritto in python che sfrutta la libreria Spread.7 client.py costituisce la parte client del sistema di messaggistica in esecuzione sulle macchine di simulazione, scheduler.py la parte server in esecuzione sulla macchina di repository, accetta connessioni dalle macchine di simulazione e implementa una coda di accesso alla risorsa. 7 Spread è un toolkit per il message passing, per maggiori informazioni è possibile consultare il sito web del progetto all’indirizzo http://www.spread.org. 51 Figura 7.4: Ciclo di esecuzione di superexecute.sh scripts/utils/ls-feat.sh A little script to list remote traces with a specific feature/characteristic. Usage: ./scripts/utils/ls-feat.sh [-t trace] [-s server] -f feature -c characteristic [-n num] [-h] -t target trace pathname (default: root) -s server remote traces server (default: mauirep.fisica.unipg.it) -f feature feature -c characteristic characteristic -n num max number of trace to return -h this help. Riporta una lista di tracce presenti sul repository server caratterizzate dallo specifico stato di una feature richiesto. Il numero di risultati ottenuti può essere limitato. Questo script viene sfruttato da superexecute.sh per l’interrogazione del server di repository sull’esistenza di job in stato ready o running e collaborativi, viene inoltre richiamato da run.sh in caso di run con esecuzione di tipo parallelo per la selezione di un lancio o simulazione da eseguire. scripts/utils/ls-traces.sh A little script to list remote traces. Usage: ./scripts/utils/ls-traces.sh [-r] [-g] [-t trace] [-s server] [-h] -t target trace pathname (default: root) -s server remote traces server (default: mauirep.fisica.unipg.it) 52 -r recursive -g graph, output a tree like vista -h this help. Uno script che riporta una lista di tracce remote disponibili sul repository server. Accetta opzioni per la richiesta di elencare in modo ricorsivo le sottodirectory o per generare una vista ad albero simile a quella messa a disposizione dal comando unix tree. scripts/utils/sync-traces.sh Usage: ./scripts/utils/sync-traces.sh [-g|-p] [-d] [-n] [-q] -t trace -s server [-h] ./scripts/utils/sync-traces.sh -e [-q] -t trace -s server [-h] ./scripts/utils/sync-traces.sh -r [-q] -t trace [-h] ./scripts/utils/sync-traces.sh [-S|-U] [-q] -c char -f feat -t trace -s server [-h] ./scripts/utils/sync-traces.sh -R [-q] -f feat -t trace -s server [-h] ./scripts/utils/sync-traces.sh -A [-q] -t trace -s server [-h] Perform various tasks relative to traces syncronization. -s server remote traces server. -t target name of the trace. -g get, download remote trace from server. -p put, upload trace to the remote server. -d delete on sync, files not present on source will be removed from destination. -n do NOT sync subdirectories. -r remove, delete a local trace. -e exists, check if target exists on remote server, -s, -t required. -q quiet, output only error messages. -f -c -S -U -R feature feature to set, unset or read. characteristic value of the feature to set, unset. set, set a characteristic of a feature, -f, -c, -s, -t required. unset, remove a characteristic of a feature, -f, -c, -s, -t required. read, report the characteristics of a feature, -f, -c, -s, -t required. -A read all, report all the characteristics of all the features, -s, -t required. -h this help. Usage examples: to get target from server: ./scripts/utils/sync-traces.sh -g -t target -s server to put target in server: ./scripts/utils/sync-traces.sh -p -t target -s server to remove a local target: ./scripts/utils/sync-traces.sh -r -t target to check existance of a target on server: ./scripts/utils/sync-traces.sh -e -t target -s server to set a characteristc of a feature: ./scripts/utils/sync-traces.sh -S -c char -f feat -t target -s server to unset a characteristc of a feature: ./scripts/utils/sync-traces.sh -U -c char -f feat -t target -s server 53 to -s to -s read the characteristcs of a feature: ./scripts/utils/sync-traces.sh -R -f feat -t target server read the characteristcs of all the features: ./scripts/utils/sync-traces.sh -A -t target server sync-traces.sh implementa l’interfaccia al repository, pensato per essere lanciato direttamente dall’utente in fase di preparazione e in fase di analisi o richiamato da altri script mette a disposizione funzioni che è possibile raggruppare in due tipologie: la sincronizzazione delle tracce e la gestione delle feature loro associate. Per quanto riguarda la sincronizzazione delle tracce viene supportato l’upload e il download tra la macchina locale e il repository, interrogazioni al repository e cancellazioni locali, per quanto riguarda le feature è possibile la lettura, la scrittura e la cancellazione degli stati associati a una feature o l’interrogazione di tutte le feature impostate con i loro stati associati. Al suo interno lo script usa ssh per eseguire comandi sul repository e rsync per le operazioni di trasferimento file. scripts/utils/trace-uploader.pl Un uploader di file di traccia, specificati direttamente o selezionati indicando la data desiderata, dalla macchina locale al repository server. Viene utilizzato per l’update giornaliero delle tracce dal cluster di produzione al repository. 7.4.5 Tool di preparazione tracce Le macchine di un cluster di calcolo necessitano di una configurazione armonizzata per quanto riguarda batch system, utenti e gruppi. Questa famiglia di script si preoccupa della configurazione del cluster e della preparazione dei file di traccia prima di procedere alla fase di esecuzione. scripts/batch-utils/make-queues.sh Richiamando il comando di Maui qmgr questo tool resetta la configurazione corrente delle code nell’istallazione locale dello scheduler e le ricrea a partire da un file di configurazione passato come parametro. scripts/launcher/clear-master-node Cancella sulla macchina locale i file di log, le tracce e le directory degli utenti del batch system. 54 scripts/launcher/farm prerequisites.sh Dopo aver letto una lista di nodi del cluster da un file passato come parametro accerta che i nodi siano raggiungibili, che accettino comandi e che gli orologi di sistema siano sincronizzati. scripts/launcher/sleep-launcher.sh Si rivela utile spesso per la realizzazione di test la creazione di job di tipo sleep di durata a piacere, la cui unica funzione è quella di occupare tempo di cpu. Questo script legge da un file una sequenza di sottomissione specifica e procede a sottomettere i job sleep a tempo appropriato. scripts/users-utils/users-get.sh Stampa la lista di utenti e gruppi del sistema locale, per procedere successivamente alla loro clonazione sulle altre macchine del cluster come richiesto dal batch system. scripts/users-utils/users-make.sh Crea utenti e gruppi da un file di input sulla macchina locale. Lo script permette la creazione di utenti e gruppi con medesimo uid e gid su tutte le macchine del cluster. scripts/utils/resolve-users.sh Generate a random config-users file from run.conf directives Usage: ./scripts/utils/resolve-users.sh tracedir Sulla base delle direttive NUMGROUPS e USERSPERGROUP nel file locale run.conf procede alla creazione di una configurazione di utenti e gruppi del batch system in loro accordo. Le direttive su utenti e gruppi possono specificare valori assoluti o range di valori, nel qual caso lo script sceglie casualmente un numero di utenti o gruppi nei range dati. scripts/utils/resolve-queues.sh Generate a random queues file from run.conf directives to pass to make-queues.sh Usage: ./scripts/utils/resolve-queues.sh tracedir In modo speculare allo script precedente genera una configurazione casuale delle code del batch system secondo i valori o i range impostati nelle direttive QUEUES e QUEUEWALLTIME presenti nel file locale run.conf. scripts/utils/resolve-schedule.sh Generate a random schedule file from run.conf directives to pass to sleep-launcher.sh Usage: ./scripts/utils/resolve-schedule.sh tracedir 55 Come terzo script di generazione di configurazioni casuali resolve-schedule.sh si occupa della generazione di uno schedule di lancio da passare a sleep-launcher.sh. Le direttive interessate in run.conf sono JOBS per il numero di job, JOBSTARTTIME per i tempi di lancio dei job e JOBLIFESPAN per la loro durata, ad ognuna delle quali è possibile associare un valore specifico o un range di possibilità. scripts/utils/resolve-variables.pl Questo script interpretando il file run.conf risolve le direttive di tipo VARINTERVAL e VARENUM che specificano range o insiemi di valori per le variabili usati per i run di ottimizzazione genetica e per quelli di tipo fulltree. scripts/utils/run-at.pl Lancia lo scheduler Maui al raggiungimento di una data e ora specificata come argomento dello script. scripts/utils/trace-assembler.pl Concatenando tracce giornaliere, come quelle provenienti dalla farm di produzione, crea una nuova traccia Maui. scripts/utils/workload-clean.pl Le tracce che scrive Maui contengono alcuni commenti e altri elementi non essenziali, questo script ripulisce una traccia in modo da renderla utilizzabile per l’analisi o il suo utilizzo nel simulatore. scripts/utils/workload-first-submitted.pl Individua e scrive in output il più piccolo tra i tempi di accodamento dei job nella traccia passata come argomento allo script. scripts/utils/workload-time-updater.pl Questo script prende in input il nome di un file di traccia, ne sposta i riferimenti temporali di un offset indicato come opzione e salva un nuovo file di traccia. Questa operazione risulta necessaria per ovviare alle limitazioni ad operare con file di traccia che presentino riferimenti temporali passati da parte di Maui quando usato in simulazione. 56 Figura 7.5: Ciclo di esecuzione di run.sh 7.4.6 Tool di simulazione, test e ottimizzazione run.sh Usage: ./run.sh -r|-l -t trace [-s server] [-h] -s server -t trace remote traces server. name of the trace. -r|-l remote or local operations. -h this help. Usage examples: local operations: ./run.sh -l -t trace remote operations: ./run.sh -r -t target -s server Lo script che si preoccupa della gestione ad alto livello dei run. Come si può osservare in fig. 7.5 quando run.sh viene lanciato dalle macchine di simulazione su di un run procede a leggere configurazione e tipo del run dal file run.conf e il suo stato di esecuzione leggendo le feature dal server di repository. In caso di run di tipo fulltree ad esecuzione locale o remota non parallela, o in caso di run ad esecuzione parallela collaborativa in cui la macchina che esegue lo script è la prima macchina a prendersi carico del run è necessario che lo script generi l’intera struttura di tracce corrispondenti alle possibili combinazioni delle variabili del run. Una volta che l’albero delle tracce sia stato generato run.sh può lanciare simulate.sh o launch.sh su ogni singola traccia Maui per procedere alle effettive simulazioni o test. launch.sh Usage: ./launch.sh [-l|-r|-p|-k] [-u] -t trace [-s server] [-h] -t target pathname of target -s server remote traces server -r, -l download target from remote server or use local target -p put result on remote server -k do not erase local target on exit 57 Figura 7.6: Ciclo di esecuzione di launch.sh -u check & set launching/ed status -h this help. Usage examples: launch from local to local: ./launch.sh -l -k -t target launch from remote server to local: ./launch.sh -r -k -t target -s server launch from local to remote server: ./launch.sh -l -p -t target -s server launch from local to remote server and keep a local copy: ./launch.sh -l -p -k -t target -s server launch from remote server to remote server: ./launch.sh -r -p -t target -s server launch from remote server to remote server and keep a local copy: ./launch.sh -r -p -k -t target -s server Lo script launch.sh esegue un test di scheduling Maui sulle macchine di test gestendone le fasi pre e post esecuzione. Sia per quanto riguarda le tracce in input sia per quanto riguarda l’archiviazione dei risultati si può operare in locale o appoggiarsi al server di repository. La sequenza di lancio prevede prima del lancio dello scheduler il controllo dello stato della traccia di esecuzione da lanciare con la lettura della feature “status” corrispondente sul repository, allo scopo di evitare il lancio di test già in corso o precedentemente effettuati, il controllo dei file di traccia e di configurazione necessari all’esecuzione di Maui e il controllo dello stato dei nodi di calcolo indicati in configurazione e la loro riconfigurazione. Terminata l’esecuzione di Maui lo script imposta a lauched la feature “status” sul repository e, se richiesto, procede all’upload dei risultati (fig. 7.6). 58 simulate.sh Usage: ./simulate.sh [-l|-r|-p|-k] [-u] -t trace [-s server] [-h] -t target pathname of target -s server remote traces server -r, -l download target from remote server or use local target -p put result on remote server -k do not erase local target on exit -u check & set simulating/ed status -h this help. Usage examples: simulate from local to local: ./simulate.sh -l -k -t target simulate from remote server to local: ./simulate.sh -r -k -t target -s server simulate from local to remote server: ./simulate.sh -l -p -t target -s server simulate from local to remote server and keep a local copy: ./simulate.sh -l -p -k -t target -s server simulate from remote server to remote server: ./simulate.sh -r -p -t target -s server simulate from remote server to remote server and keep a local copy: ./simulate.sh -r -p -k -t target -s server Uno script parallelo a launch.sh per il lancio di Maui come simulatore che accetta le stesse opzioni per l’interfaccia con il server di repository. Si può vedere in fig. 7.7 nella pagina seguente come i controlli che simulate.sh effettua sulle tracce siano simili a quelli di launch.sh, ulteriori operazioni sia in fase di preparazione che in fasi di postsimulazione sono però necessarie a causa delle limitazioni della modalità di simulazione Maui. Per ottenere da una traccia di log una traccia usabile in input dal simulatore devono essere prima richiamati in sequenza workload-first-submitted.pl, per riordinare i job in ordine di sottomissione, e workload-time-updater.pl, per spostare in avanti i riferimenti temporali, mentre a simulazione terminata workload-time-updater.pl e workload-clean.pl devono essere applicati alla traccia in output per reimpostare i riferimenti temporali originali e pulirla da commenti e dati non essenziali. scripts/genetic/analyze-generation.pl Questo è il primo di una coppia di script che implementano le procedure per la selezione ed evoluzione per run di ottimizzazione genetica. analyze-generation.pl legge i parametri genetici del run dal file run.conf per poi usare la procedura di calcolo della fitness indicata dalla istruzione FITNESS lı̀ contenuta per il calcolo del valore di fitness per ogni individuo della popolazione corrente. scripts/genetic/generate-generation.pl Dopo aver letto le opzioni genetiche del run dal file di configurazione run.conf (variabili da evolvere e tassi di crossover e mutazione) e i valori di fitness di ogni indi59 Figura 7.7: Ciclo di esecuzione di simulate.sh viduo precedentemente calcolati da analyze-generation.pl provvede alle operazioni di crossover e mutazione degli individui presi in esame e genera una nuova popolazione di potenziali soluzioni. 7.4.7 Tool di analisi visualize.sh Usage: ./visualize.sh [-k] [-p plug-in] [-s server] -t trace [-h] [plug-in options] Trace visualizer. -k keep trace after download and visualization. -p visualizer plug-in. [default timeliner]. -s server remote traces server. -t trace name of the trace. -h this help. Other options will be passed to the plug-in. Usage examples: to visualize a local trace: ./visualize.sh -t trace [-p timeliner] [plug-in options] to visualize a remote trace: ./visualize.sh -s server -t trace [-p timeliner] [plug-in options] to visualize and keep a local copy of a remote trace: ./visualize.sh -k -s server -t trace [-p timeliner] [plug-in options] 60 Figura 7.8: Timeline di scheduling generata da trace-timeliner.pl Lo script visualize.sh genera grafici a partire da tracce Maui locali o sul server di repository. Grazie ad una struttura a plug-in varie tipologie di grafici possono essere supportate, ad esempio la visualizzazione di timeline di tracce tramite trace-timeliner.pl come si può vedere in fig. 7.8. scripts/run-analyzer.pl Un tool sviluppato per l’analisi dei run sottomessi all’infrastruttura, richiama workload-analyzer sulle singole tracce del run per l’estrazione dei dati di traccia da visualizzare direttamente o usare per il calcolo di valori aggregati relativi all’intero run. scripts/workload-analyzer.pl Uno strumento molto potente che permette l’estrazione, la manipolazione e l’analisi dei contenuti informativi racchiusi nella traccia di Maui. Lo script supporta operazioni di selezione di record e campi della traccia, sorting dei risultati, calcolo di valori medi semplici e aggregati su altri campi e il calcolo delle metriche per l’analisi dei risultati. scripts/trace-timeliner.pl Un tool per il confronto grafico che a partire da file di traccia Maui genera grafici 2d con linee temporali di attività dei nodi del cluster. I grafici, costruiti richiamando il pacchetto Gnuplot, presentano sulle ascisse lo scorrere del tempo e sulle ordinate i nodi di calcolo, dove sono disposti i vari segmenti di linea corrispondenti ai tempi di esecuzione e terminazione e alla durata di ogni job. L’utilizzo di questa particolare visualizzazione permette facilmente di valutare fenomeni di overhead ed eventuali errori nel caso di simulazioni. 61 scripts/workload-compare.pl Un semplice script che permette di calcolare lo scarto quadratico medio nel tempo di attesa in coda tra i job di due tracce Maui. 62 Capitolo 8 Test e simulazioni Essentially, all models are wrong, but some are useful. George E. P. Box Una serie di test dell’infrastruttura sono stati eseguiti allo scopo di verificarne l’implementazione e avviare uno studio sull’impatto di vari parametri e configurazioni relativi al meccanismo di fairshare di Maui. 8.1 Test sul Fairshare Un primo test sull’impatto del Fairshare sul tempo di attesa medio dei job effettuato sulla farm di test. Configurazione Per osservare e misurare l’impatto del fairshare si sono studiati una serie di casi semplici in ambiente controllato. In questo caso lo scheduler della farm di test è stato impostato con due soli utenti, il primo ha valore di FSTARGET fisso a 50 e tutti i possibili valori di FSTARGET tra 1 e 100 per il secondo utente sono stati testati. Per far si che la politica di scheduling risponda esclusivamente ai valori di target per il fairshare tutti gli altri parametri sono stati impostati come non influenti. Le finestre di analisi dell’algoritmo di fairshare sono impostate a 10 secondi, con decadimento quasi nullo a 0.01. La sequenza di lancio dei job per i due utenti è la stessa: ogni 10 secondi viene sottomesso un job della durata di 30 secondi. FSWEIGHT 1 FSPOLICY DEDICATEDPS 63 FSDEPTH 6 FSINTERVAL 10 FSDECAY 0.01 USERCFG[red] FSTARGET=50 USERCFG[blue] FSTARGET=[1..100] Osservazioni Come si può osservare in fig. 8.1 nella pagina 70 i tempi di attesa media registrati per i job dei due utenti sono sostanzialmente equivalenti per valori di FSTARGET dell’utente blue inferiori a 50. Per valori superiori lo scheduler dà maggiore priorità all’utente blue e si assiste ad una crescente divergenza tra i valori di attesa. 8.2 Simulazione sul Fairshare n.1 Con gli stessi parametri del test precedente è stata effettuata una simulazione per confrontare i risultati. Configurazione I parametri di configurazione sono i medesimi del test effettuato con il cluster di test: un cluster di 4 nodi con due utenti, il primo con FSTARGET=50, il secondo con FSTARGET variabile tra 1 e 100, FSINTERVAL=10 e FSDECAY=0.01, stessa sequenza di lancio job per entrambi gli utenti con job da 30 secondi sottomessi ogni 10 secondi. FSWEIGHT 1 FSPOLICY DEDICATEDPS FSDEPTH 6 FSINTERVAL 10 FSDECAY 0.01 USERCFG[red] FSTARGET=50 USERCFG[blue] FSTARGET=[1..100] Osservazioni In fig. 8.2 nella pagina 70 sono riportati i tempi di attesa media registrati per i job dei due utenti. Equivalenti per valori di FSTARGET dell’utente blue inferiori a 64 50; per valori superiori si assiste ancora ad una crescente divergenza tra i valori di attesa. Si può osservare in fig. 8.3 nella pagina 71, dove sono riportati anche i risultati del test precedente, l’accordo tra il simulatore e la realtà della farm di test, con l’interessante eccezione di una maggiore efficienza generale del batch system riportata dalla simulazione, riconducibile a overhead dell’hardware “reale”. 8.3 Simulazione sul Fairshare n.2 Simile alla prima simulazione, sono stati usati gli stessi parametri di configurazione ma una sequenza di lancio dei job più lunga e casuale (la stessa per i due utenti) allo scopo di introdurre un tipo di sottomissione dei job più realistica e limitare eventuali effetti di risonanza. Configurazione Due utenti, il primo con FSTARGET=50, il secondo con FSTARGET variabile tra 1 e 100. FSINTERVAL=10 e FSDECAY=0.01 Cluster di 4 nodi. Sequenza di lancio job casuale uguale per entrambi gli utenti. FSWEIGHT 1 FSPOLICY DEDICATEDPS FSDEPTH 6 FSINTERVAL 10 FSDECAY 0.01 USERCFG[red] FSTARGET=50 USERCFG[blue] FSTARGET=[1..100] Osservazioni In fig. 8.4 nella pagina 71 sono riportati i tempi di attesa media registrati per i job dei due utenti. Si nota la sostanziale equivalenza con i risultati della precedente simulazione, la maggior durata delle sequenze di lancio dei job permette di ottenere risultati meno soggetti ad oscillazioni casuali. 65 8.4 Simulazione sul Fairshare n.3 In un tentativo di approfondire la conoscenza sull’impatto del parametro FSTARGET sulla simulazione si è ripetuta una simulazione con sequenza di lancio job lunga e FSTARGET del primo utente fisso a 20. Configurazione Due utenti, il primo con FSTARGET=20, il secondo con FSTARGET variabile tra 1 e 100. FSINTERVAL=10 e FSDECAY=0.01 Cluster di 4 nodi. Sequenza di lancio job casuale uguale per entrambi gli utenti. FSWEIGHT 1 FSPOLICY DEDICATEDPS FSDEPTH 6 FSINTERVAL 10 FSDECAY 0.01 USERCFG[red] FSTARGET=20 USERCFG[blue] FSTARGET=[1..100] Osservazioni L’abbassamento del valore di FSTARGET dell’utente red non modifica in modo significativo i tempi di attesa per valori di FSTARGET dell’utente blue minori di 80, per valori superiori l’aumento di priorità è tale che tutti i job sottomessi dall’utente blue vengono eseguiti immediatamente, scavalcando completamente i job sottomessi dall’utente red (fig. 8.5 nella pagina 72). 8.5 Simulazione sul Fairshare n.4 Una nuova simulazione, ancora con gli stessi parametri di configurazione e sequenza di lancio dei job ma FSTARGET del primo utente fisso a 80. Configurazione Due utenti, il primo con FSTARGET=80, il secondo con FSTARGET variabile tra 1 e 100. FSINTERVAL=10 e FSDECAY=0.01 66 Cluster di 4 nodi. Sequenza di lancio job casuale uguale per entrambi gli utenti. FSWEIGHT 1 FSPOLICY DEDICATEDPS FSDEPTH 6 FSINTERVAL 10 FSDECAY 0.01 USERCFG[red] FSTARGET=80 USERCFG[blue] FSTARGET=[1..100] Osservazioni La parte più a sinistra del grafico in fig. 8.6 nella pagina 72 (FSTARGET dell’utente blue ¡ 20 e FSTARGET dell’utente red = 80) rispecchia in modo speculare la parte più a destra del grafico precedente (fig. 8.5 nella pagina 72) come ci si aspettava, qui con i job dell’utente red con attese in coda praticamente nulle. Al crescere del valore di FAIRSHARE dell’utente blue i tempi di attesa medi si avvicinano per poi invertirsi quando i valori di FSTARGET per i due utenti sono uguali. 8.6 Simulazione sul Fairshare n.5 La simulazione n.3 con valore di FAIRSHARE dell’utente blue a 20 è stata replicata con diversi valori di configurazione per le finestre temporali prese in considerazione. Il parametro FSINTERVAL da 10 è stato portato a 60, il parametro FSDECAY da 0.01 a 0.1. Configurazione Due utenti, il primo con FSTARGET=20, il secondo con FSTARGET variabile tra 1 e 100. FSINTERVAL=10 e FSDECAY=0.01 Cluster di 4 nodi. Sequenza di lancio job casuale uguale per entrambi gli utenti. FSWEIGHT 1 FSPOLICY DEDICATEDPS FSDEPTH 6 FSINTERVAL 60 67 FSDECAY 0.1 USERCFG[red] FSTARGET=20 USERCFG[blue] FSTARGET=[1..100] Osservazioni In fig. 8.7 nella pagina 73 sono riportati i tempi di attesa media registrati per i job dei due utenti, come si può vedere in fig. 8.8 nella pagina 73, dove sono messi a confronto con i tempi di attesa relativi alla simulazione n.3 la variazione dei parametri relativi alle finestre temporali da considerare non ha provocato significativi cambiamenti nello scheduling per quanto riguarda la situazione semplificata simulata. 8.7 Test di ottimizzazione genetica Allo scopo di testare l’algoritmo genetico e l’intera infrastruttura di ottimizzazione si è deciso di sottomettere al sistema un semplice problema per il quale fosse nota la configurazione ottimale. Fissata una medesima sequenza di lancio per tutti gli utenti e la priorità per il primo utente si è lasciata al sistema l’ottimizzazione delle priorità degli altri utenti con l’obbiettivo di minimizzare le differenze tra le medie dei tempi in coda dei processi di ogni utente. La soluzione ottimale prevista vede tutte le priorità equivalenti a quella fissata. Configurazione La configurazione prevede otto utenti su di un sistema consistente di quattro nodi da un processore per nodo, una sequenza di lancio dei job generata casualmente è stata impostata per essere la medesima per tutti gli utenti. La priorità del primo utente è stata fissata ad un valore di 50, le altre priorità sono state lasciate libere di evolvere per venti generazioni ognuna con una popolazione di cento individui. BACKFILLPOLICY FIRSTFIT RESERVATIONPOLICY CURRENTHIGHEST NODEALLOCATIONPOLICY MINRESOURCE CREDWEIGHT 1 USERWEIGHT 1 USERCFG[Group_01_User_01] PRIORITY=50 68 USERCFG[Group_01_User_02] PRIORITY=<<prio1>> USERCFG[Group_01_User_03] PRIORITY=<<prio2>> USERCFG[Group_01_User_04] PRIORITY=<<prio3>> USERCFG[Group_01_User_05] PRIORITY=<<prio4>> USERCFG[Group_01_User_06] PRIORITY=<<prio5>> USERCFG[Group_01_User_07] PRIORITY=<<prio6>> USERCFG[Group_01_User_08] PRIORITY=<<prio7>> L’eq. (8.1) riporta il tempo medio in coda dei job per l’utente i-esimo, dove n è il numero di job dell’utente i-esimo e Qij il tempo in coda per il job j-esimo per l’utente i-esimo, la funzione di fitness scelta è riportata in eq. (8.2) dove u è il numero degli utenti e n e m indici di utenti. n P Qi = Qij j=1 n u X u X |Qn − Qm | F itness = 1 − 2 n=1 m=1 (8.1) (8.2) Osservazioni Come si può osservare in fig. 8.9 nella pagina 74 e fig. 8.10 nella pagina 74 con il passare del tempo le soluzioni selezionate dall’algoritmo genetico si avvicinano all’ottimo teorico mentre l’evoluzione temporale del valore di massima fitness tende a 1 come ci si aspetta. Il test ci rassicura quindi sulla bontà dell’implementazione e sulla solidità dell’infrastruttura di ottimizzazione. 69 Figura 8.1: Test sul fairshare, tempo di attesa medio dei job al crescere del target di fairshare per l’utente blue. Figura 8.2: Simulazione n.1, tempo di attesa medio dei job al crescere del target di fairshare per l’utente blue. 70 Figura 8.3: Risultati del test a confronto con quelli della simulazione n.1. Figura 8.4: Simulazione n.2, tempo di attesa medio dei job al crescere del target di fairshare per l’utente blue. 71 Figura 8.5: Simulazione n.3, tempo di attesa medio dei job al crescere del target di fairshare per l’utente blue. Figura 8.6: Simulazione n.4, tempo di attesa medio dei job al crescere del target di fairshare per l’utente blue. 72 Figura 8.7: Simulazione n.5, tempo di attesa medio dei job al crescere del target di fairshare per l’utente blue. Figura 8.8: Risultati della simulazione n.5 a confronto con quelli della n.3. 73 Figura 8.9: Evoluzione temporale dei valori di priorità per la configurazione di massima fitness. Figura 8.10: Evoluzione temporale del valore di massima fitness. 74 Conclusioni L’opportunità di svolgere il tirocinio e di lavorare al progetto di tesi presso il Dipartimento di Fisica si è rivelata preziosa dal punto di vista professionale e umano e ha permesso di affrontare una serie di argomenti e problematiche stimolanti. L’architettura della infrastruttura, che coinvolge operazioni di analisi, simulazione e ottimizzazione, ha richiesto un approfondimento delle tecnologie di virtualizzazione e di Xen in particolare per la sua realizzazione, mentre la fase di ottimizzazione basata su algoritmi genetici mi ha permesso di apprezzare le potenzialità della programmazione genetica. Per quanto riguarda i sistemi batch, la preparazione di una farm di test con l’installazione di TORQUE e Maui ha apportato conoscenze pratiche e l’analisi dei risultati dei primi test mi ha fatto comprendere la complessità della gestione e delle interdipendenze tra le numerose variabili che intervengono nel funzionamento e nei risultati di un sistema batch. La possibilità di interagire giornalmente con persone preparate e disponibili nello sviluppo del progetto ha permesso una crescita personale e una grande soddisfazione per il lavoro di gruppo svolto. Al momento la fase di sviluppo del progetto continua, si sta procedendo alla definizione di nuove metriche, in uno sforzo di quantificare ulteriormente i molteplici aspetti relativi a un sistema batch e fornire supporto alle decisioni degli amministratori, ulteriori validazioni del simulatore Maui sono necessarie e verranno portate avanti grazie al supporto ai test Monte Carlo dell’infrastruttura. Per quanto riguarda le prospettive di sviluppo e ricerca future si può pensare ad una generalizzazione degli elementi legati a Maui e all’ottimizzazione genetica per svincolare l’infrastruttura dal particolare problema che ha portato alla sua realizzazione, rimanendo invece nel campo di applicazione interessato si aprono interessanti scenari di studio sulle politiche di scheduling ma anche sull’impatto di cambiamenti hardware e di topologia di rete. Un ambizioso obbiettivo rimane, una volta ottenuta la necessaria confidenza nel simulatore e nell’infrastruttura, il monitoraggio e l’ottimizzazione quasi-realtime della farm di produzione. 75 Appendici 77 Appendice A File di configurazione run.conf Il file run.conf raccoglie le opzioni di configurazione del run nella forma opzione=valore. Opzione Possibili Valori Dettagli RUNTYPE fulltree, random o genetic RUNFEATURES parallel o altro MAXPROC INTERO MAXWORLDS INTERO GENERATIONS INTERO POPULATION INTERO MUTATIONRATE INTERO DA 0 a 100 CROSSOVERRATE INTERO DA 0 a 100 Il tipo di run da lanciare: completa esplorazione dello spazio delle variabili, test di validazione Monte Carlo oppure ottimizzazione genetica. Se parallel il run verrà eseguito in modo parallelo, altrimenti verrà eseguito da un’unica macchina. Se il run è di tipo parallelo indica il numero massimo di macchine che possono contemporaneamente lavorare sul run. Se uguale a 0 non esiste un limite al numero di macchine. In run di ottimizzazione genetica indica il numero di esplorazioni dello spazio delle soluzioni da eseguire per considerare completato il run. Se uguale a 0 il run continua fino a quando non viene fermato manualmente. In run genetici indica il numero di generazioni da generare. In run genetici indica il numero di elementi della popolazione in ogni generazione. Probabilità di mutazione per run genetici (in percentuale). Probabilità di crossover per run genetici (in percentuale). 79 Opzione Possibili Valori Dettagli GENOTYPE foo[:bar]. . . VARINTERVAL foo VALORE1..VALORE2 VARENUM bar VALORE[:VALORE]. . . FITNESS PATH RUNPREFIX STRINGA NUMGROUPS INTERO o INTERO..INTERO USERSPERGROUP INTERO o INTERO..INTERO GROUPSLIST INTERO[:INTERO]. . . QUEUES INTERO o INTERO..INTERO QUEUEWALLTIME INTERO o INTERO..INTERO JOBS INTERO o INTERO..INTERO JOBSTARTTIME INTERO o INTERO..INTERO JOBLIFESPAN INTERO o INTERO..INTERO Il genotipo del run: una serie di variabili di configurazione i cui valori vengono calcolati a partire dalle corrispondenti direttive VARINTERVAL e VARENUM. L’intervallo di variazione della variabile “foo”. Una serie di valori da assumere per la variabile “bar”, separati da “:”. Percorso dell’eseguibile che calcola il valore di fitness per gli individui di un run genetico. Un prefisso da aggiungere al nome dei run. In run di tipo random il numero di gruppi da creare o un intervallo tra cui scegliere casualmente il numero di gruppi. In run di tipo random il numero di utenti per gruppo da creare o un intervallo tra cui scegliere casualmente il numero di utenti per gruppo. In run di tipo random una lista con il numero di utenti dei vari gruppi, separati da “:”. In run di tipo random il numero delle code del batch system da creare o un intervallo tra cui scegliere casualmente il numero delle code. In run di tipo random i valori di WALLTIME delle code del batch system o un intervallo tra cui scegliere casualmente per ogni coda. In run di tipo random il numero di job da lanciare o un intervallo tra cui scegliere casualmente. In run di tipo random il tempo di lancio dei job o un intervallo tra cui scegliere casualmente per ogni job. In run di tipo random il tempo di esecuzione dei job o un intervallo tra cui scegliere casualmente per ogni job. 80 config-simul-template config-simul-template è un file di tipo template per il file di configurazione di Maui maui.cfg. Accetta tutti i parametri accettati da maui.cfg e supporta l’uso di variabili tra doppi segni di minore e maggiore per indicare una loro successiva sostituzione, secondo le istruzioni in run.conf. Si riporta qui un esempio di file di configurazione di run tramite config-simul-template, per le principali opzioni di configurazione si rimanda alla sezione 6.2 e per una trattazione esaustiva dei parametri accettati da Maui alla “Maui Administrator’s Guide”, appendice F.[1] RMPOLLINTERVAL 00:00:01 LOGFILE LOGFILEMAXSIZE LOGLEVEL maui.log 10000000 0 QUEUETIMEWEIGHT 1 BACKFILLPOLICY RESERVATIONPOLICY FIRSTFIT CURRENTHIGHEST NODEALLOCATIONPOLICY MINRESOURCE CREDWEIGHT 1 USERWEIGHT 1 USERCFG[Group_01_User_01] USERCFG[Group_01_User_02] USERCFG[Group_01_User_03] USERCFG[Group_01_User_04] USERCFG[Group_01_User_05] USERCFG[Group_01_User_06] USERCFG[Group_01_User_07] USERCFG[Group_01_User_08] PRIORITY=50 PRIORITY=<<prio1>> PRIORITY=<<prio2>> PRIORITY=<<prio3>> PRIORITY=<<prio4>> PRIORITY=<<prio5>> PRIORITY=<<prio6>> PRIORITY=<<prio7>> 81 File di traccia Maui Ogni traccia consiste in una singolo record da 44 campi delimitati da spazi contenente il log dei job terminati in caso di sistemi reali o da simulare per sistemi in simulazione.[1] Nome Indice Formato Default JobID Nodes Requested 1 2 STRINGA INTERO [NESSUNO] 0 Tasks Requested 3 INTERO User Name Group Name 4 5 STRINGA STRINGA Wallclock Limit 6 INTERO Job Completion State 7 STRINGA Required Class Submission Time Dispatch Time 8 9 10 STRINGA INTERO INTERO Start Time 11 INTERO Completion Time 12 INTERO Required Network Adapter Required Node Architecture Required Node Operating System Required Node Memory Comparison Required Node Memory Required Node Disk Comparison 13 STRINGA 14 STRINGA 15 STRINGA 16 uno tra >, ≥, =, ≤, < 17 INTERO 18 uno tra >, ≥, =, ≤, < Required Node Disk Required Node Attributes System Queue Time 19 INTERO 20 STRINGA 21 INTERO Tasks Allocated 22 INTERO Required Per Node 23 INTERO Tasks 82 Note Nome del job Numero di nodi richiesti 1 Numero di task richiesti [NESSUNO] Nome dell’utente [NESSUNO] Gruppo primario dell’utente 1 Durata massima del job permessa (in sec.) Completed Stato di completamento, uno tra Completed, Removed, NotRun [DEFAULT:1] Classe richiesta 0 Data di sottomissione 0 Data di invio per l’esecuzione 0 Data effettiva di esecuzione 0 Data di fine esecuzione [NONE] Interfaccia di rete del nodo richiesta [NONE] Architettura del nodo richiesta [NONE] Sistema operativo del nodo richiesto ≥ Operatore di comparazione per la memoria 0 Memoria richiesta (in MB) ≥ Operatore di comparazione per lo spazio disco locale 0 Spazio disco locale richiesto [NONE] Lista delle feature del nodo richieste 0 Data di riscontro positivo alle politiche di fairness TASK Numero di task alloRICHIESTI cati -1 Numero di task per nodo richiesti Nome Indice Formato Default Note QOS 24 STRINGA[:STRINGA] [NONE] JobFlags 25 STRINGA[:STRINGA]... [NONE] Account Name 26 STRINGA [NONE] Executable Comment 27 28 STRINGA STRINGA [NONE] [NONE] Bypass Count 29 INTERO -1 ProcSeconds Utilized Partition Name 30 DOUBLE 0 31 STRINGA [DEFAULT] Dedicated Processors per Task Dedicated Memory per Task Dedicated Disk per Task 32 INTERO 1 33 INTERO 0 34 INTERO 0 Dedicated per Task 35 INTERO 0 Start Date 36 INTERO 0 End Date 37 INTERO 0 Allocated Host List Resource Manager Name Required Host Mask Reservation 38 STRINGA[:STRINGA]... [NONE] Livello di QOS richiesto Lista di attributi del job Account associato al job Nome dell’eseguibile Lista di attributi del job fornita dal resource manager Numero di volte in cui il job è stato scavalcato tramite backfill Secondi*Processore utilizzati Partizione di esecuzione Processori richiesti per task Memoria richiesta per task (in MB) Spazio disco locale richiesto per task (in MB) Memoria virtuale richiesta per task (in MB) Data di esecuzione presunta minima Data di fine esecuzione massima Nodi allocati 39 STRINGA 40 STRINGA[STRINGA]... [NONE] 41 STRINGA [NONE] Set Description 42 [NONE] Application Simutator Data RISERVATO 43 STRINGA:STRINGA [:STRING] STRINGA[:STRINGA] [NONE] 44 STRINGA [NONE] Swap [NONE] 83 Nome del resource manager Nodi richiesti dal job Reservation richiesta dal job Restrizioni richieste dal job Modulo di simulazione File delle risorse Maui Il file delle risorse resources descrive tutti gli aspetti delle risorse computazionali di un batch system Maui reale o simulato. Ogni riga del file descrive un singolo nodo di calcolo con le informazioni relative in 21 campi delimitati da spazi.[1] Nome Indice Formato Default Resource Type Event Type 1 2 COMPUTENODE Unico valore ammissibile [NONE] Il nodo partirà rispettivamente in stato Idle, Down, o Drained Event Time Resource ID Resource Manager Name Configured Swap 3 4 5 COMPUTENODE uno tra AVAILABLE, DEFINED, o DRAINED EPOCHTIME STRINGA STRINGA 6 INTERO 1 7 INTERO 1 8 INTERO 1 9 INTERO 1 10 INTERO 1 11 INTERO 1 12 INTERO 1 13 STRINGA [NONE] Usato in ambienti IBM SP2 Usato in ambienti IBM SP2 Usato in ambienti IBM SP2 Sistema operativo 14 STRINGA [NONE] Architettura del nodo 15 STRINGA [NONE] Features 16 STRINGA [batch:1] Classi associate 17 STRINGA [NONE] Interfacce di rete 18 DOUBLE 1.0 Velocità relativa del nodo 19 20 21 STRINGA STRINGA STRINGA [NONE] [NONE] [NONE] Configured Memory Configured Disk Configured Processors Resource Frame Location Resource Slot Location Resource Slot Use Count Node Operating System Node Architecture Configured Node Features Configured Run Classes Configured Network Adapters Relative Resource Speed RISERVATO RISERVATO RISERVATO 84 1 N/A [NONE] Note ignorato Nome del nodo Nome del resource manager che gestisce il nodo Memoria virtuale (in MB) Memoria (in MB) Spazio disco locale (in MB) Numero di processori Appendice B Sorgenti functions.sh #!/bin/bash SYNC TRACES SCRIPT="./scripts/utils/sync-traces.sh" RSYNC USER="maui" SERVER URL="mauirep.fisica.unipg.it" RSYNC DIR="/rep/" check prerequisite() { RETURN=0 if [ −f "$1" ] then if [ $QUIET == elif [ −f "$1.gz" ] then if [ $QUIET == gzip −d $1.gz elif [ −f "$1.bz" ] then if [ $QUIET == bzip2 −d $1.gz else if [ $QUIET == RETURN=1 fi } 10 0 ]; then echo " . . Found."; fi 0 ]; then echo " . . Found."; fi 0 ]; then echo " . . Found."; fi 20 0 ]; then echo " . . Not found."; fi check variable() { RETURN=0 if [ "a$1" == "a" ] then if [ $QUIET == 0 ]; then echo " . . Not found."; fi RETURN=1 else if [ $QUIET == 0 ]; then echo " . . Found."; fi fi } 85 30 # check status trace feature status check status() { # is feature set to status on this trace? if [ −z "$1" ] | | [ −z "$2" ] | | [ −z "$3" ] 40 # parameters of zero length or no parameter passed. then return 2 else $SYNC TRACES SCRIPT −R −f $2 −t $1 −s $SERVER URL | egrep "^${3}$" RCHECK=$? return $RCHECK # 0 founded, 1 not founded, 2 error fi } 50 # set status trace feature status set status() { if [ −z "$1" ] | | [ −z "$2" ] | | [ −z "$3" ] # parameters of zero length or no parameter passed. then return 2 else RETURN=0 $SYNC TRACES SCRIPT −e −t $1 −s $SERVER URL # exists on server? RETURN=$? 60 case $RETURN in 0)# trace exists, set feature $SYNC TRACES SCRIPT −S −c $3 −f $2 −t $1 −s $SERVER URL RETURN=$? return $RETURN;; 2)# trace does not exists, create trace. . ssh −− $RSYNC USER@$SERVER URL mkdir −p −− $RSYNC DIR/$1 RETURN=$? if [ $RETURN != 0 ] then 70 # error return 1 fi # . .and set feature $SYNC TRACES SCRIPT −S −c $3 −f $2 −t $1 −s $SERVER URL RETURN=$? return $RETURN;; *)# error return 1;; esac 80 fi } # unset status trace feature status unset status() { if [ −z "$1" ] | | [ −z "$2" ] | | [ −z "$3" ] # parameters of zero length or no parameter passed. then return 2 else 90 RETURN=0 $SYNC TRACES SCRIPT −q −U −c $3 −f $2 −t $1 −s $SERVER URL RETURN=$? 86 return $RETURN # 0 unset ok, 1 unset error fi } # flush a feature flush status() { if [ −z "$1" ] | | [ −z "$2" ] # parameters of zero length or no parameter passed. then return 2 else RETURN=0 for i in ‘$SYNC TRACES SCRIPT −R −f $2 −t $1 −s $SERVER URL‘ do $SYNC TRACES SCRIPT −q −U −c $i −f $2 −t $1 −s $SERVER URL RETURN=$? if [ $RETURN != 0 ] then return $RETURN # 0 unset ok, 1 unset error fi done return $RETURN fi } 100 110 launch.sh #!/bin/bash # NOTE: # DONE: do not upload if trace is in similating/ed state # added -u option to check if trace is in launching/ed status # set checking source "./functions.sh" # INITIALIZATION SYNC TRACES SCRIPT="./scripts/utils/sync-traces.sh" # path of sync-traces script # Path of the scripts SCRIPTS PATH="./scripts" ERROR CODE=39 # default error code HELP="Usage: $0 [-l|-r|-p|-k] [-u] -t trace [-s server] [-h]\n\n -t target pathname of target\n -s server remote traces server\n -r, -l download target from remote server or use local target\n -p put result on remote server\n -k do not erase local target on exit\n\n -u check & set launching/ed status\n\n -h this help.\n\n Usage examples:\n launch from local to local: $0 -l -k -t target\n launch from remote server to local: $0 -r -k -t target -s server\n launch from local to remote server: $0 -l -p -t target -s server\n 87 10 20 launch from local to remote server and keep a local copy: 30 $0 -l -p -k -t target -s server\n launch from remote server to remote server: $0 -r -p -t target -s server\n launch from remote server to remote server and keep a local copy: $0 -r -p -k -t target -s server\n" # Var initialization NO ARGS=0 SERVER VAR=0 TARGET VAR=0 40 REMOTE VAR=0 LOCAL VAR=0 PUT VAR=0 KEEP VAR=0 QUIET=0 VERBOSE=0 STATUS VAR=0 CLIENTNAME="randomize" 50 if [ $CLIENTNAME == "randomize" ] then TEMP=‘echo $USER $HOSTNAME $RANDOM | md5sum | cut −d" " −f1‘ CLIENTNAME=${TEMP:24} fi launch() { if [ $STATUS VAR == 1 ] then 60 # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME CHECK=2 # check status return 0 if found, 1 if not found, 2 if an error occur check status $LOCAL PATH status launching > /dev/null CHECK=$? if [ $CHECK == 0 ] then if [ $QUIET == 0 ]; then echo " Trace $LOCAL_PATH is already launching" ; fi 70 # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME return 2 # trace in launching state fi CHECK=2 # check status return 0 if found, 1 if not found, 2 if an error occur check status $LOCAL PATH status launched > /dev/null CHECK=$? if [ $CHECK == 0 ] 80 then if [ $QUIET == 0 ]; then echo " Trace $LOCAL_PATH already launched" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME return 2 # trace in launching state 88 fi CHECK=0 # Check for $LOCAL PATH existence 90 $SYNC TRACES SCRIPT −q −e −t $LOCAL PATH −s $SERVER URL CHECK=$? if [ $CHECK == 2 ] then if [ $QUIET == 0 ]; then echo " Trace $LOCAL_PATH does not exits: assuming lready" ; fi # trace not in launching/ed state yet, locking now setting status=launching set status $LOCAL PATH status launching > /dev/null 100 # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME else CHECK=2 # check status return 0 if found, 1 if not found, 2 if an error occur check status $LOCAL PATH status lready > /dev/null CHECK=$? 110 if [ $CHECK == 0 ] then if [ $QUIET == 0 ]; then echo " Trace $LOCAL_PATH: lready" ; fi # trace not in launching/ed state yet, locking now setting status=launching set status $LOCAL PATH status launching > /dev/null # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME else 120 if [ $QUIET == 0 ]; then echo " Trace $LOCAL_PATH: unknown status" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME return 1 # trace in error state fi fi fi # now proper launch 130 if [ $QUIET == 0 ]; then echo " Checking headers"; fi if [ $QUIET == 0 ]; then echo " Checking datadir: $LOCAL_PATH"; fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of a resources trace: \ $LOCAL_PATH/resources . ."; fi check prerequisite "$LOCAL_PATH/resources" if [ $RETURN != 0 ] then return 1 else 140 WORKLOAD RESOURCES="$LOCAL_PATH/resources" fi 89 if [ $QUIET == 0 ]; then echo " Checking the farm prerequisites . ."; fi RETURN=0 $SCRIPTS PATH/launcher/farm prerequisites.sh −p " " \ −r $WORKLOAD RESOURCES | | RETURN=1 if [ $RETURN != 0 ] then return 1 150 fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of a config \ file: $LOCAL_PATH/config . ."; fi check prerequisite "$LOCAL_PATH/config" if [ $RETURN != 0 ] then return 1 else CONFIG RESOURCES="$LOCAL_PATH/config" 160 fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of a header \ config file: $LOCAL_PATH/config-real-header . ."; fi check prerequisite "$LOCAL_PATH/config-real-header" if [ $RETURN != 0 ] then if [ $QUIET == 0 ]; then echo −n " Checking the existence of a header \ config file: traces/config-real-header . ."; fi check prerequisite "traces/config-real-header" 170 if [ $RETURN != 0 ] then return 1 else CONFIG REAL HEADER="traces/config-real-header" fi else CONFIG REAL HEADER="$LOCAL_PATH/config-real-header" fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of a user/group \ 180 config file: $LOCAL_PATH/config-users . ."; fi check prerequisite "$LOCAL_PATH/config-users" if [ $RETURN != 0 ] then return 1 else CONFIG USERS="$LOCAL_PATH/config-users" fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of a batch \ 190 system config file: $LOCAL_PATH/config-batch . ."; fi check prerequisite "$LOCAL_PATH/config-batch" if [ $RETURN != 0 ] then return 1 else CONFIG BATCH="$LOCAL_PATH/config-batch" fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of launch \ 90 200 schedule file: $LOCAL_PATH/schedule . ."; fi check prerequisite "$LOCAL_PATH/schedule" if [ $RETURN != 0 ] then return 1 else CONFIG SCHEDULE="$LOCAL_PATH/schedule" fi WORKLOAD REAL UNSCALED=$LOCAL PATH/workload−real−unscaled 210 if [ $QUIET == 0 ]; then echo " Starting pre launch tasks:"; fi # Getting the farm nodes if [ $QUIET == 0 ]; then echo " Getting the farm nodes from \ $WORKLOAD_RESOURCES" ; fi NODES=‘cat $WORKLOAD RESOURCES | grep COMPUTENODE | \ cut −d" " −f 4‘ if [ $QUIET == 0 ] then 220 for i in $NODES do echo " Node $i" done fi if [ $QUIET == 0 ]; then echo " Starting nodes users reconfiguration:"; fi # Coping files to nodes if [ $QUIET == 0 ]; then echo " Copying scripts to nodes" ; fi 230 for i in $NODES do scp $CONFIG USERS $i:/root/config−users > /dev/null 2>&1 if [ $QUIET == 0 ]; then echo " $CONFIG_USERS -> Node $i"; fi scp $SCRIPTS PATH/users−utils/users−make.sh $i:/root/users−make.sh > \ /dev/null 2>&1 if [ $QUIET == 0 ]; then echo " $SCRIPTS_PATH/users-utils/users-make.sh -> \ Node $i"; fi done 240 # Configuring users if [ $QUIET == 0 ]; then echo " Appling changes" ; fi for i in $NODES do ssh $i "chmod a+xr /root/users-make.sh ; /root/users-make.sh /root/config-users" if [ $QUIET == 0 ]; then echo " Changing users on node $i"; fi done # Removing files from nodes if [ $QUIET == 0 ]; then echo " Removing scripts from nodes" ; fi for i in $NODES do ssh $i "rm -f /root/users-make.sh" if [ $QUIET == 0 ]; then echo " Removing \ users-make.sh from node $i"; fi ssh $i "rm -f /root/config-users" if [ $QUIET == 0 ]; then echo " Removing config-users from node $i"; fi 91 250 done if [ $QUIET == 0 ]; then echo " Starting server users reconfiguration . ."; fi 260 $SCRIPTS PATH/users−utils/users−make.sh $CONFIG USERS if [ $QUIET == 0 ]; then echo " Changing users on server"; fi if [ $QUIET == 0 ]; then echo " Cleaning old files"; fi rm −f maui.cfg stats/* if [ $QUIET == 0 ]; then echo " Starting server queues reconfiguration:"; fi $SCRIPTS PATH/batch−utils/make−queues.sh −p " " −r $CONFIG BATCH if [ $QUIET == 0 ]; then echo " Copying configuration"; fi 270 cat $CONFIG REAL HEADER $CONFIG RESOURCES > maui.cfg if [ $QUIET == 0 ]; then echo " Restarting scheduler"; fi killall maui ; sleep 2 ; maui CURRENTTIME=‘date +%s‘; STARTTIME=$(($CURRENTTIME)) if [ $QUIET == 0 ]; then echo −n " Starting launch at "; echo $STARTTIME ; fi 280 $SCRIPTS PATH/launcher/sleep−launcher.sh $CONFIG SCHEDULE ENDTIME=‘date +%s‘ if [ $QUIET == 0 ]; then echo " Writing launch duration:"; fi let "SIMTIME= $ENDTIME - $STARTTIME" echo $SIMTIME > $LOCAL PATH/duration if [[ $CHECKED == "s2s" | | $CHECKED == "l2s" ]] then 290 # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME flush status $LOCAL PATH duration > /dev/null set status $LOCAL PATH duration $SIMTIME > /dev/null # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi 300 if [ $QUIET == 0 ]; then echo " Starting post launch tasks:"; fi if [ $QUIET == 0 ]; then echo " Moving results"; fi cat stats/* * * * | sort −n > $WORKLOAD REAL UNSCALED if [ $QUIET == 0 ]; then echo " Creating Human readable formats:"; fi $SCRIPTS PATH/utils/workload−clean.pl \ $WORKLOAD REAL UNSCALED $LOCAL PATH/workload−real−unscaled−cleaned 310 if [ $QUIET == 0 ]; then echo " Cleaning files"; fi if [ $VERBOSE == 0 ] then rm −f maui.cfg stats/* 92 else rm −fv maui.cfg stats/* fi # Compressing results if [ $QUIET == 0 ]; then echo " Compressing files" ; fi 320 if [ $VERBOSE == 0 ] then gzip −fq $LOCAL PATH/workload* else gzip −f $LOCAL PATH/workload* fi if [ $STATUS VAR == 1 ] then # Lock 330 $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME # setting status=launched set status $LOCAL PATH status launched > /dev/null # now we can unset status launching unset status $LOCAL PATH status launching > /dev/null # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi 340 } clean local() { if [ $QUIET == 0 ]; then echo " Cleaning $LOCAL_PATH"; fi $SYNC TRACES SCRIPT −q −r −t $LOCAL PATH RETURN=$? if [[ $RETURN != 0 ]]; then echo " Error: cleaning $LOCAL_PATH error"; fi } # checkinput, check input variables, reporting “bad” or a code for each command 350 checkinput() { CHECKED="bad" if (( $TARGET VAR + $LOCAL VAR == 2 )) && (( $SERVER VAR + \ $REMOTE VAR + $PUT VAR == 0 )) then # from local to local CHECKED="l2l" fi if (( $TARGET VAR + $SERVER VAR + $REMOTE VAR == 3 )) && \ (( $LOCAL VAR + $PUT VAR == 0 )) 360 then # from server to local CHECKED="s2l" fi if (( $TARGET VAR + $SERVER VAR + $LOCAL VAR + $PUT VAR == 4 )) && \ (( $REMOTE VAR == 0 )) then # from local to server (remove local target) CHECKED="l2s" fi 370 if (( $TARGET VAR + $SERVER VAR + $REMOTE VAR + $PUT VAR == 4 )) && \ 93 (( $LOCAL VAR == 0 )) then # from server to server (remove local target) CHECKED="s2s" fi } l2l() { # local to local launch 380 if [ $QUIET == 0 ]; then echo "Local to local launching $LOCAL_PATH"; fi launch RETURN=$? if [[ $RETURN != 0 ]]; then echo "Error: launching $LOCAL_PATH error"; fi } s2l() { # server to local launch if [ $QUIET == 0 ]; then echo "Server to local launching $LOCAL_PATH \ from $SERVER_URL"; fi 390 if [ $QUIET == 0 ]; then echo "Downloading $LOCAL_PATH from $SERVER_URL"; fi $SYNC TRACES SCRIPT −g −t $LOCAL PATH −s $SERVER URL RETURN=$? # Simulate if [ $RETURN == 0 ] then launch RETURN=$? if [[ $RETURN != 0 ]]; then echo "Error: launching $LOCAL_PATH"; fi 400 else echo "Error: Downloading $LOCAL_PATH from $SERVER_URL failed" fi } l2s() { # local to server launch if [ $QUIET == 0 ]; then echo "Local to server launching $LOCAL_PATH \ to $SERVER_URL"; fi # launch 410 launch RETURN=$? # check launch return (0 ok, 1 error, 2 launching/ed) if [ $RETURN == 1 ] then echo "Error: launching $LOCAL_PATH error" return $RETURN fi 420 if [ $RETURN == 2 ] then if [ $QUIET == 0 ]; then echo " $LOCAL_PATH already launching/ed"; fi UPLOAD="no" fi # Check for $LOCAL PATH existence $SYNC TRACES SCRIPT −q −e −t $LOCAL PATH −s $SERVER URL 94 RETURN=$? 430 # if trace exist do not perform a clean, print a warning if [ $RETURN == 1 ] then echo "Error: cannot check trace on server, upload aborted, local \ launched trace not deleted" return $RETURN fi # upload to server if [ "a$UPLOAD" != "ano" ] 440 then if [ $QUIET == 0 ]; then echo "Uploading trace to server"; fi $SYNC TRACES SCRIPT −q −p −t $LOCAL PATH −s $SERVER URL RETURN=$? if [ $RETURN == 0 ] then if [ $QUIET == 0 ]; then echo "Uploading ok"; fi # clean local if requested if [ $KEEP VAR == 0 ]; then clean local; fi 450 else echo "Error: Uploading failed" fi else if [ $QUIET == 0 ]; then echo "Skipping uploading trace to server"; fi RETURN=0 fi } 460 s2s() { # server to server launch if [ $QUIET == 0 ]; then echo "Server to server launching - local \ path=$LOCAL_PATH; server=$SERVER_URL"; fi # Control status to save time if [ $STATUS VAR == 1 ] then # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME 470 CHECK=2 # check status return 0 if found, 1 if not found, 2 if an error occur check status $LOCAL PATH status launching > /dev/null CHECK=$? if [ $CHECK == 0 ] then if [ $QUIET == 0 ]; then echo " Trace is already launching" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME return 0 # not an error but trace is in launching state -> exit fi CHECK=2 # check status return 0 if found, 1 if not found, 2 if an error occur 95 480 check status $LOCAL PATH status launched > /dev/null CHECK=$? if [ $CHECK == 0 ] then if [ $QUIET == 0 ]; then echo " Trace already launched" ; fi 490 # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME return 0 # not an error but trace is in launched state -> exit fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi # download from server 500 $SYNC TRACES SCRIPT −g −t $LOCAL PATH −s $SERVER URL RETURN=$? # launch if [ $RETURN == 0 ] then launch RETURN=$? else echo "Error: downloading error" fi 510 # check launch return (0 ok, 1 error, 2 launching/ed) if [ $RETURN == 1 ] then echo "Error: launching $LOCAL_PATH error" return $RETURN fi if [ $RETURN == 2 ] then if [ $QUIET == 0 ]; then echo " $LOCAL_PATH already launching/ed"; fi 520 if [ $QUIET == 0 ]; then echo " Skipping uploading trace to server"; fi RETURN=0 fi if [ $RETURN == 0 ] then # upload to server $SYNC TRACES SCRIPT −p −t $LOCAL PATH −s $SERVER URL RETURN=$? # 4) clean local 530 if [ $RETURN == 0 ] then if [ $KEEP VAR == 0 ]; then clean local; fi RETURN=$? else echo "Error: uploading error" fi if [[ $RETURN != 0 ]]; then echo "Error: local cleaning error"; fi fi } 540 if [ $# -eq “$NO ARGS” ] # Script invoked with no command-line args. 96 then echo −e $HELP exit $ERROR CODE # Exit and explain usage, if no argument(s) given. fi while getopts "s:t:rlpkuhvq" Option # Command arguments: # -t target (local path) 550 # -s server # -r, -l download target from remote server or use local path for target # -p put result on remote server # -k do not erase local target on exit # -u check launching/ed status do case $Option in s ) SERVER VAR=1 SERVER URL=$OPTARG;; 560 t ) TARGET VAR=1 LOCAL PATH=$OPTARG TARGET=‘basename $OPTARG‘;; r ) REMOTE VAR=1;; l ) LOCAL VAR=1;; p ) PUT VAR=1;; k ) KEEP VAR=1;; v ) VERBOSE=1;; q ) QUIET=1;; u ) STATUS VAR=1;; 570 h ) echo −e $HELP exit 1;; * ) echo −e $HELP exit 1;; esac done checkinput if [ $CHECKED == "bad" ] then 580 echo "Error: bad parameters" echo −e $HELP exit 1 else if [ $CHECKED == "l2l" ] then l2l if [[ $QUIET == 0 && $RETURN == 0 ]]; \ then echo "Local to Local simulation ok"; fi exit $RETURN fi if [ $CHECKED == "s2l" ] then s2l if [[ $QUIET == 0 && $RETURN == 0 ]]; \ then echo "Server to local simulation ok"; fi exit $RETURN 97 590 fi 600 if [ $CHECKED == "l2s" ] then l2s if [[ $QUIET == 0 && $RETURN == 0 ]]; \ then echo "Local to server simulation ok"; fi exit $RETURN fi if [ $CHECKED == "s2s" ] then s2s if [[ $QUIET == 0 && $RETURN == 0 ]]; \ then echo "Server to server simulation ok"; fi exit $RETURN fi 610 fi ls-feat.sh #!/bin/bash # NOTE: A little script to list remote traces with a specific feature/characteristic # version 0.1 # source "./functions.sh" # initialization RSYNC USER="maui" RSYNC DIR="/rep" LSTRACE="./scripts/utils/ls-traces.sh" SYNCTRACE="./scripts/utils/sync-traces.sh" FEATURE VAR=0 CHARACTERISTIC VAR=0 # defaults SERVER URL="mauirep.fisica.unipg.it" TARGET="/" ALL="yes" NUM=0 10 20 HELP="A little script to list remote traces with a specific feature/characteristic.\n Usage: $0 [-t trace] [-s server] -f feature -c characteristic [-n num] [-h]\n\n -t target -s server trace pathname (default: root)\n remote traces server (default: $SERVER_URL)\n\n -f feature feature\n -c characteristic characteristic\n\n -n num -h 30 max number of trace to return\n\n this help.\n" 98 while getopts "s:t:f:c:n:h" Option do case $Option in s ) SERVER URL=$OPTARG;; t ) TARGET=$OPTARG;; f ) FEATURE VAR=1 FEATURE=$OPTARG;; c ) CHARACTERISTIC VAR=1 CHARACTERISTIC=$OPTARG;; n ) ALL="no" NUM=$OPTARG;; h ) echo −e $HELP exit 1;; * ) echo −e $HELP exit 1;; esac done 40 50 if (( $CHARACTERISTIC VAR + $FEATURE VAR == 2 )) then for i in ‘$LSTRACE −r −t $TARGET‘ do if [[ $ALL == "yes" | | $NUM != 0 ]] then check status $i $FEATURE $CHARACTERISTIC>/dev/null CHECK=$? if [ $CHECK == 0 ] then echo $i NUM=$(( $NUM − 1 )) fi if [ $CHECK == 2 ] then echo "Error" fi fi done else echo −e $HELP fi 60 70 ls-traces.sh #!/bin/bash # NOTE: A little script to list remote traces # version 0.1 # # initialization RSYNC USER="maui" RSYNC DIR="/rep" 10 # defaults SERVER URL="mauirep.fisica.unipg.it" 99 TARGET="" RECURSIVE=0 GRAPH=0 HELP="A little script to list remote traces.\n Usage: $0 [-r] [-g] [-t trace] [-s server] [-h]\n\n -t target trace pathname (default: root)\n -s server remote traces server (default: $SERVER_URL)\n -r recursive\n -g graph, output a tree like vista\n\n -h 20 this help.\n" while getopts "s:t:rgh" Option do case $Option in s ) SERVER URL=$OPTARG;; t ) TARGET=$OPTARG;; r ) RECURSIVE=1;; g ) GRAPH=1;; h ) echo −e $HELP exit 1;; * ) echo −e $HELP exit 1;; esac done 30 40 if [ $RECURSIVE == 0 ] then ssh −− $RSYNC USER@$SERVER URL ls −− $RSYNC DIR/$TARGET else if [ $GRAPH == 0 ] then ssh −− $RSYNC USER@$SERVER URL "tree -d -i -f $RSYNC_DIR/$TARGET | \ sed -e ’s/\’$RSYNC_DIR’//’ | sed -e ’s/\///’ " else ssh −− $RSYNC USER@$SERVER URL "tree -d $RSYNC_DIR/$TARGET" fi fi 50 resolve-users.sh #!/bin/bash # NOTE: version 0.1 # CHANGES: 0.1: support for direct and range value for NUMGROUPS and \ USERSPERGROUP directives in run.conf # initialization NO ARGS=0 # var initialization # run.conf parameters NUMGROUPS="" # number of groups ex. NUMGROUPS=3 or NUMGROUPS=3. .10 \ for a range (max 9999 groups) USERSPERGROUP="" # users per group ex. USERSPERGROUP=12 or \ 100 10 USERSPERGROUP=50. .100 (max 9999 users) # note: every user belongs to a single group GROUPSLIST="" # groups, extended version ex. GROUPSLIST=3:1:12:3 # defaults ERROR CODE=39 # default error code 20 HELP="Generate a random config-users file from run.conf directives\n Usage: $0 tracedir\n\n Note: use either NUMGROUPS+USERSPERGROUP or GROUPSLIST directives in \ run.conf,\n" if [ $# -eq “$NO ARGS” ] # script invoked with no command-line args then echo −e $HELP exit $ERROR CODE # exit and explain usage, if no argument(s) given fi 30 RUNCONF=$1 if [ −f $RUNCONF/run.conf ] # run.conf found, parse it then NUMGROUPS=‘cat $RUNCONF/run.conf|egrep \ "^NUMGROUPS=[0-9]{1,4}(\.\.[0-9]{1,4})?$"‘ # extract NUMGROUPS NUMGROUPS=${NUMGROUPS#NUMGROUPS=} #echo $NUMGROUPS 40 USERSPERGROUP=‘cat $RUNCONF/run.conf|egrep \ "^USERSPERGROUP=[0-9]{1,4}(\.\.[0-9]{1,4})?$"‘ # extract USERSPERGROUP USERSPERGROUP=${USERSPERGROUP#USERSPERGROUP=} #echo $USERSPERGROUP GROUPSLIST=‘cat $RUNCONF/run.conf|egrep \ "^GROUPSLIST=([0-9]+:)*[0-9]+$"‘ # extract GROUPSLIST GROUPSLIST=${GROUPSLIST#GROUPSLIST=} #echo $GROUPSLIST 50 if [[ $NUMGROUPS && $USERSPERGROUP ]] && [[ ! $GROUPSLIST ]] then # if NUMGROUPS is a range, \ if [[ ‘echo "$NUMGROUPS"|egrep "\.\."‘ ]] select a random number of groups in that range then BASE=$(( ‘echo "$NUMGROUPS"|egrep −o "^[0-9]*"‘ )) RANGE=$(( ‘echo "$NUMGROUPS"|egrep −o "[0-9]*$"‘ − $BASE )) NUMGROUPS=$[ ( $RANDOM % ( RANGE + 1 ) ) + $BASE ] 60 fi # now NUMGROUPS is properly defined G=0 while (( $G <= $NUMGROUPS )) do FORMATTEDG=$G # Formatted GRUOUP number (ex. 13 -> 0013) while (( ${#FORMATTEDG} < 4 )) 70 101 do FORMATTEDG=0$FORMATTEDG done if [[ ‘echo "$USERSPERGROUP"|egrep "\.\."‘ ]] # if USERSPERGROUP is a range, \ select a random number of groups in that range then BASE=$(( ‘echo "$USERSPERGROUP"|egrep −o "^[0-9]*"‘ )) RANGE=$(( ‘echo "$USERSPERGROUP"|egrep −o "[0-9]*$"‘ − $BASE )) NEWUSERSPERGROUP=$[ ( $RANDOM % ( RANGE + 1 ) ) + $BASE ] else NEWUSERSPERGROUP=$USERSPERGROUP fi 80 # now USERSPERGROUP is properly defined as NEWUSERSPERGROUP \ for this group U=0 while (( $U <= $NEWUSERSPERGROUP )) do FORMATTEDU=$U while (( ${#FORMATTEDU} < 4 )) do FORMATTEDU=0$FORMATTEDU done echo "g${FORMATTEDG}u${FORMATTEDU} : g$FORMATTEDG" U=$((U + 1)) done G=$((G + 1)) done elif [[ ! $NUMGROUPS && ! $USERSPERGROUP ]] && [[ $GROUPSLIST ]] then echo "GROUPSLIST directive not yet implemented, sorry." exit 2 else echo "users/group directives not found in $RUNCONF/run.conf" exit 2 fi else echo "$RUNCONF/run.conf not found" # run.conf not found exit 1 fi resolve-queues.sh resolve-schedule.sh #!/bin/bash # NOTE: version 0.1 # CHANGES: 0.1: support for direct and range value for JOBS, JOBSTARTTIME \ 102 90 100 110 and JOBLIFESPAN directives in run.conf # initialization NO ARGS=0 # var initialization # run.conf parameters 10 JOBS="" # number of jobs ex. NUMJOBS=3 or NUMJOBS=3. .10 for a \ range (max 99999 jobs) JOBSTARTTIME="" # start time for every job ex. JOBSTARTTIME=12 or \ JOBSTARTTIME=50. .100 (max 99999 start time) JOBLIFESPAN="" # groups, extended version ex. JOBLIFESPAN=123 or \ JOBLIFESPAN=100. .1000 (max 99999 life span time) # defaults ERROR CODE=39 # default error code 20 HELP="Generate a random schedule file from run.conf directives to pass \ to sleep-launcher.sh\n Usage: $0 tracedir\n\n" if [ $# -eq “$NO ARGS” ] # script invoked with no command-line args then echo −e $HELP exit $ERROR CODE # exit and explain usage, if no argument(s) given fi 30 RUNCONF=$1 if [ −f $RUNCONF/run.conf ] # run.conf found, parse it then JOBS=‘cat $RUNCONF/run.conf|egrep \ "^JOBS=[0-9]{1,5}(\.\.[0-9]{1,5})?$"‘ # extract JOBS JOBS=${JOBS#JOBS=} #echo $JOBS 40 JOBSTARTTIME=‘cat $RUNCONF/run.conf|egrep \ "^JOBSTARTTIME=[0-9]{1,5}(\.\.[0-9]{1,5})?$"‘ # extract JOBSTARTTIME JOBSTARTTIME=${JOBSTARTTIME#JOBSTARTTIME=} #echo $JOBSTARTTIME JOBLIFESPAN=‘cat $RUNCONF/run.conf|egrep \ "^JOBLIFESPAN=[0-9]{1,5}(\.\.[0-9]{1,5})?$"‘ # extract JOBLIFESPAN JOBLIFESPAN=${JOBLIFESPAN#JOBLIFESPAN=} #echo $JOBLIFESPAN 50 if [[ $JOBS && $JOBSTARTTIME && $JOBLIFESPAN ]] then if [[ ‘echo "$JOBS"|egrep "\.\."‘ ]] \ # if JOBS is a range, select a random number of jobs in that range then BASE=$(( ‘echo "$JOBS"|egrep −o "^[0-9]*"‘ )) RANGE=$(( ‘echo "$JOBS"|egrep −o "[0-9]*$"‘ − $BASE )) JOBS=$[ ( $RANDOM % ( RANGE + 1 ) ) + $BASE ] fi 103 60 # now JOBS is properly defined as a specific number #echo $JOBS while (( $JOBS >= 0 )) do if [[ ‘echo "$JOBSTARTTIME"|egrep "\.\."‘ ]] \ # if JOBSTARTTIME is a range, select a random number in that range then BASE=$(( ‘echo "$JOBSTARTTIME"|egrep −o "^[0-9]*"‘ )) RANGE=$(( ‘echo "$JOBSTARTTIME"|egrep −o "[0-9]*$"‘ − $BASE )) NEWJOBSTARTTIME=$[ ( $RANDOM % ( RANGE + 1 ) ) + $BASE ] else NEWJOBSTARTTIME=$JOBSTARTTIME fi 70 # now JOBSTARTTIME is properly defined as NEWJOBSTARTTIME for this job 80 if [[ ‘echo "$JOBLIFESPAN"|egrep "\.\."‘ ]] \ # if JOBSLIFESPAN is a range, select a random number in that range then BASE=$(( ‘echo "$JOBLIFESPAN"|egrep −o "^[0-9]*"‘ )) RANGE=$(( ‘echo "$JOBLIFESPAN"|egrep −o "[0-9]*$"‘ − $BASE )) NEWJOBLIFESPAN=$[ ( $RANDOM % ( RANGE + 1 ) ) + $BASE ] else NEWJOBLIFESPAN=$JOBLIFESPAN fi 90 # now JOBSLIFESPAN is properly defined as NEWJOBSLIFESPAN for this job echo "user ${NEWJOBSTARTTIME} queue ${NEWJOBLIFESPAN}" JOBS=$JOBS−1 done else echo "job schedule directives not found in $RUNCONF/run.conf" exit 2 fi else echo "$RUNCONF/run.conf not found" # run.conf not found exit 1 fi run.sh #!/bin/bash source "./functions.sh" # INITIALIZATION SYNC TRACES SCRIPT="./scripts/utils/sync-traces.sh" # path of sync-traces script SCRIPTS PATH="./scripts" 104 100 REPSERVER="mauirep.fisica.unipg.it" CLIENTNAME="randomize" 10 if [ $CLIENTNAME == "randomize" ] then TEMP=‘echo $USER $HOSTNAME $RANDOM | md5sum | cut −d" " −f1‘ CLIENTNAME=${TEMP:24} fi ERROR CODE=39 # default error code HELP="Usage: $0 -r|-l -t trace [-s server] [-h] 20 -s server remote traces server. -t trace name of the trace. -r|-l remote or local operations. -h this help. Usage examples: local operations: $0 -l -t trace\ remote operations: $0 -r -t target -s server" 30 # Var initialization NO ARGS=0 SERVER VAR=0 TARGET VAR=0 REMOTE VAR=0 LOCAL VAR=0 QUIET=0 IAMTHEFIRST=0 40 COLLAB=0 run() { if [ ! −f whatami ] then echo "Node type not defined" return 1 fi 50 WHATAMI=‘cat whatami‘ if [ "a$WHATAMI" != "asimulator" ] then echo "Not running on a simulator" return 1 fi echo "Checking datadir: $LOCAL_PATH" 60 if [ $QUIET == 0 ]; then echo −n " Checking the existence of the \ configuration file: $LOCAL_PATH/run.conf . ."; fi check prerequisite "$LOCAL_PATH/run.conf" if [ $RETURN != 0 ] 105 then return 1 else RUN CONF="$LOCAL_PATH/run.conf" fi 70 # Importing all the needed variables from the configuration file source $RUN CONF if [ "a$RUNFEATURES" == "aparallel" ] then if [ "a$MAXPROC" == "a" ] then if [ $QUIET == 0 ]; then echo "Collaborative run without client limit" ; fi MAXPROC=0 fi 80 fi # Different type of run: if [ $RUNTYPE == "fulltree" ] then echo "Starting fulltree run:" if [ $CHECKED == "remote" ] then 90 # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME STATUS=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f status −t \ $LOCAL PATH −s $REPSERVER‘ if [ "a$STATUS" == "aready" ] then if [ $QUIET == 0 ]; then echo "Run ready to be done"; fi 100 # The job is ready for computation if [ $QUIET == 0 ]; then echo " Switching $LOCAL_PATH: ready -> generating"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c ready \ −t $LOCAL PATH −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c generating \ −t $LOCAL PATH −s $REPSERVER flush status $LOCAL PATH startgenerating $SCRIPTS PATH/utils/sync−traces.sh −q −S −f startgenerating \ −c ‘date +%s‘ −t $LOCAL PATH −s $REPSERVER 110 flush status $LOCAL PATH start$CLIENTNAME $SCRIPTS PATH/utils/sync−traces.sh −q −S −f start$CLIENTNAME \ −c ‘date +%s‘ −t $LOCAL PATH −s $REPSERVER if [ "a$RUNFEATURES" == "aparallel" ] then if [ $MAXPROC == 0 ] then if [ $QUIET == 0 ]; then echo "Generating a collaborative \ run (no client limit)"; fi 106 120 $SCRIPTS PATH/utils/sync−traces.sh −q −S −f crunning \ −c $CLIENTNAME −t $LOCAL PATH −s $REPSERVER else if [ $QUIET == 0 ]; then echo "Generating a collaborative run"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −S −f crunning \ −c $CLIENTNAME −t $LOCAL PATH −s $REPSERVER fi COLLAB=1 fi 130 # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME IAMTHEFIRST=1 elif [ "a$STATUS" == "arunned" ] then if [ $QUIET == 0 ]; then echo "Someone has already runned"; fi 140 # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME if [ $QUIET == 0 ]; then echo "Cleaning local copy"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −r −t $LOCAL PATH exit 0 elif [ "a$STATUS" == "agenerating" ] then 150 if [ $QUIET == 0 ]; then echo "Someone is generating traces" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME if [ $QUIET == 0 ]; then echo "Cleaning local copy"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −r −t $LOCAL PATH exit 0 160 elif [ "a$STATUS" == "arunning" ] then if [ "a$RUNFEATURES" != "aparallel" ] then if [ $QUIET == 0 ]; then echo "Not a collaborative run and someone \ is already running it" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME 170 if [ $QUIET == 0 ]; then echo "Cleaning local copy"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −r −t $LOCAL PATH exit 0 else if [ $MAXPROC == 0 ] then if [ $QUIET == 0 ]; then echo "Join collaborative run (no client limit)" ; fi 107 COLLAB=1 180 $SCRIPTS PATH/utils/sync−traces.sh −q −S −f crunning \ −c $CLIENTNAME −t $LOCAL PATH −s $REPSERVER flush status $LOCAL PATH start$CLIENTNAME $SCRIPTS PATH/utils/sync−traces.sh −q −S −f start$CLIENTNAME \ −c ‘date +%s‘ −t $LOCAL PATH −s $REPSERVER # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME 190 else RUNNINGPROC=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f crunning \ −t $LOCAL PATH −s $REPSERVER | wc −l‘ if [ $MAXPROC −gt $RUNNINGPROC ] then if [ $QUIET == 0 ]; then echo "Join collaborative run" ; fi COLLAB=1 $SCRIPTS PATH/utils/sync−traces.sh −q −S −f crunning \ 200 −c $CLIENTNAME −t $LOCAL PATH −s $REPSERVER flush status $LOCAL PATH start$CLIENTNAME $SCRIPTS PATH/utils/sync−traces.sh −q −S −f start$CLIENTNAME \ −c ‘date +%s‘ −t $LOCAL PATH −s $REPSERVER # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME else if [ $QUIET == 0 ]; then echo "Collaborative run already full \ 210 of clients" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME if [ $QUIET == 0 ]; then echo "Cleaning local copy"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −r −t $LOCAL PATH exit 0 fi 220 fi fi else if [ $QUIET == 0 ]; then echo "Unknown status"; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME exit 0 230 fi fi 108 if [[ $IAMTHEFIRST == 1 | | $CHECKED != "remote" ]] then if [ $QUIET == 0 ]; then echo −n " Checking the existence of a config \ 240 template file: $LOCAL_PATH/config-simul-template . ."; fi check prerequisite "$LOCAL_PATH/config-simul-template" if [ $RETURN != 0 ] then return 1 else CONFIG SIMUL TEMPLATE=\ "$LOCAL_PATH/config-simul-template" fi 250 echo " Generating run config files:" $SCRIPTS PATH/utils/resolve−variables.pl $CONFIG SIMUL TEMPLATE \ $RUN CONF $LOCAL PATH echo " Generating run datadirs and moving config files:" cd $LOCAL PATH for i in $RUNPREFIX* do mkdir datadir−$i mv $i datadir−$i/config 260 cp resources datadir−$i cp workload−real−unscaled datadir−$i mv datadir−$i $i done cd $OLDPWD if [ $CHECKED == "remote" ] then if [ "a$RUNFEATURES" == "aparallel" ] 270 then if [ $QUIET == 0 ]; then echo " Syncing local-generated traces \ with server for parallel execution"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −p −t $LOCAL PATH \ −s $REPSERVER # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME if [ $QUIET == 0 ]; then echo " Setting ready flags on all traces"; fi 280 for i in $LOCAL PATH/$RUNPREFIX* do if [ $QUIET == 0 ]; then echo " $i: -> ready"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c ready \ −t $i −s $REPSERVER done # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME 290 else if [ $QUIET == 0 ]; then echo " local-generated traces will not \ 109 be propagated to server until simulated."; fi fi # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME # Set the job ready for computation if [ $QUIET == 0 ]; then echo " Switching $LOCAL_PATH: \ 300 generating -> running"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status \ −c generating −t $LOCAL PATH −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status \ −c running −t $LOCAL PATH −s $REPSERVER flush status $LOCAL PATH stopgenerating $SCRIPTS PATH/utils/sync−traces.sh −q −S −f stopgenerating \ −c ‘date +%s‘ −t $LOCAL PATH −s $REPSERVER 310 # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi fi if [ $CHECKED == "remote" ] then if [ "a$RUNFEATURES" == "aparallel" ] then if [ $QUIET == 0 ]; then echo " Starting server to server simulations:" ; fi 320 while [ 1 ] do # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME NEXTREADY=‘$SCRIPTS PATH/utils/ls−feat.sh −t $LOCAL PATH \ −f status −c ready −n 1‘ if [ "a$NEXTREADY" != "a" ] 330 then if [ $QUIET == 0 ]; then echo " Switching $NEXTREADY: ready -> simulating"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c ready \ −t $NEXTREADY −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c simulating \ −t $NEXTREADY −s $REPSERVER # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME 340 if [ $QUIET == 0 ]; then echo " " ; \ echo "Entering simulate.sh:"; fi ./simulate.sh −t $NEXTREADY −s $REPSERVER −r −p if [ $QUIET == 0 ]; then echo "Exiting simulate.sh" ; echo " " ; fi # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME if [ $QUIET == 0 ]; then echo " Switching $NEXTREADY: \ 110 simulating -> simulated"; fi 350 $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c simulating \ −t $NEXTREADY −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c simulated \ −t $NEXTREADY −s $REPSERVER # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME else RUNNINGPROC=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f crunning \ 360 −t $LOCAL PATH −s $REPSERVER | wc −l‘ flush status $LOCAL PATH stop$CLIENTNAME $SCRIPTS PATH/utils/sync−traces.sh −q −S −f stop$CLIENTNAME \ −c ‘date +%s‘ −t $LOCAL PATH −s $REPSERVER if [ $RUNNINGPROC == 1 ] then if [ $QUIET == 0 ]; then echo " I am the last client exiting and \ cleaning"; fi 370 $SCRIPTS PATH/utils/sync−traces.sh −q −U −f crunning −c $CLIENTNAME \ −t $LOCAL PATH −s $REPSERVER if [ $QUIET == 0 ]; then echo " Switching $LOCAL_PATH: running -> \ runned"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c running \ −t $LOCAL PATH −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c runned \ −t $LOCAL PATH −s $REPSERVER 380 MIN=‘date +%s‘ MAX=0 for i in ‘ssh −− $RSYNC USER@$REPSERVER \ ls /rep/$LOCAL PATH/.start*.lock‘ \ ‘ssh −− $RSYNC USER@$REPSERVER ls /rep/$LOCAL PATH/.stop*.lock‘ do CURR=‘ssh −− $RSYNC USER@$REPSERVER cat $i‘ if [[ $CURR < $MIN ]] then 390 MIN=$CURR fi if [[ $CURR > $MAX ]] then MAX=$CURR fi done let "TOTAL=MAX-MIN" 400 if [ $QUIET == 0 ]; then echo "--" ; echo " " ; echo "Overall duration: \ $TOTAL"; fi else if [ $QUIET == 0 ]; then echo " Other clients working exiting"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −U −f crunning −c $CLIENTNAME \ 111 −t $LOCAL PATH −s $REPSERVER fi # Unlock 410 $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME if [ $QUIET == 0 ]; then echo " Cleaning $LOCAL_PATH"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −r −t $LOCAL PATH break fi done else 420 if [ $QUIET == 0 ]; then echo " Starting local to server simulations:" ; fi for i in $LOCAL PATH/$RUNPREFIX* do if [ $QUIET == 0 ]; then echo " " ; echo "Entering simulate.sh:"; fi ./simulate.sh −t $i −s $REPSERVER −l −p −u if [ $QUIET == 0 ]; then echo "Exiting simulate.sh" ; echo " " ; fi done # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME 430 # Set the job ready for computation if [ $QUIET == 0 ]; then echo " Switching $LOCAL_PATH: running -> runned"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c running \ −t $LOCAL PATH −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c runned \ −t $LOCAL PATH −s $REPSERVER # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME 440 if [ $QUIET == 0 ]; then echo " Cleaning $LOCAL_PATH"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −r −t $LOCAL PATH fi else echo " Starting only local simulations:" for i in $LOCAL PATH/$RUNPREFIX* do if [ $QUIET == 0 ]; then echo " " ; echo "Entering simulate.sh:"; fi ./simulate.sh −t $i −l −k 450 if [ $QUIET == 0 ]; then echo "Exiting simulate.sh" ; echo " " ; fi done fi elif [ $RUNTYPE == "random" ] then echo "Starting random run:" 460 elif [ $RUNTYPE == "genetic" ] then 112 echo "Starting genetic run:" # Variables existence if [ $QUIET == 0 ]; then echo −n " Checking the existence of the GENOTYPE . ."; fi check variable $GENOTYPE if [ $RETURN != 0 ] then return 1 470 fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of the \ GENERATIONS variable . ."; fi check variable $GENERATIONS if [ $RETURN != 0 ] then return 1 fi 480 if [ $QUIET == 0 ]; then echo −n " Checking the existence of the \ POPULATION variable . ."; fi check variable $POPULATION if [ $RETURN != 0 ] then return 1 fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of the \ MUTATIONRATE variable . ."; fi 490 check variable $MUTATIONRATE if [ $RETURN != 0 ] then return 1 fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of the \ CROSSOVERRATE variable . ."; fi check variable $CROSSOVERRATE if [ $RETURN != 0 ] 500 then return 1 fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of the \ SHIFTRATE variable . ."; fi check variable $SHIFTRATE if [ $RETURN != 0 ] then return 1 510 fi # File check if [ $QUIET == 0 ]; then echo −n " Checking the existence of a config \ template file: $LOCAL_PATH/config-simul-template . ."; fi check prerequisite "$LOCAL_PATH/config-simul-template" if [ $RETURN != 0 ] then return 1 else 520 113 CONFIG SIMUL TEMPLATE="$LOCAL_PATH/config-simul-template" fi if [ $CHECKED == "remote" ] then # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME STATUS=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f status \ −t $LOCAL PATH −s $REPSERVER‘ 530 if [ "a$STATUS" == "aready" ] then if [ $QUIET == 0 ]; then echo "Run ready to be done"; fi # The job is ready for computation if [ $QUIET == 0 ]; then echo " Switching $LOCAL_PATH: ready -> running"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c ready \ −t $LOCAL PATH −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c running \ 540 −t $LOCAL PATH −s $REPSERVER STATUS="running" fi if [ "a$STATUS" == "arunning" ] then RUNNINGPROC=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f crunning \ −t $LOCAL PATH −s $REPSERVER | wc −l‘ RUNNEDPROC=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f crunned \ 550 −t $LOCAL PATH −s $REPSERVER | wc −l‘ if [ "a$RUNNINGPROC" == "a" ]; then RUNNINGPROC=0 ; fi if [ "a$RUNNEDPROC" == "a" ]; then RUNNEDPROC=0 ; fi let "TOTAL=RUNNINGPROC+RUNNEDPROC" if [ $MAXWORLDS == 0 ] then if [ $QUIET == 0 ]; then echo "Running forever" ; fi 560 else if [ $TOTAL −ge $MAXWORLDS ] then if [ $QUIET == 0 ]; then echo "Worlds limit reached" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME exit 0 fi fi 570 if [ $MAXPROC == 0 ] then if [ $QUIET == 0 ]; then echo "Join collaborative run (no client limit)" ; fi $SCRIPTS PATH/utils/sync−traces.sh −q −S −f crunning −c $CLIENTNAME \ −t $LOCAL PATH −s $REPSERVER 114 flush status $LOCAL PATH start$CLIENTNAME $SCRIPTS PATH/utils/sync−traces.sh −q −S −f start$CLIENTNAME \ 580 −c ‘date +%s‘ −t $LOCAL PATH −s $REPSERVER # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME else if [ $MAXPROC −gt $RUNNINGPROC ] then if [ $QUIET == 0 ]; then echo "Join collaborative run" ; fi 590 $SCRIPTS PATH/utils/sync−traces.sh −q −S −f crunning −c $CLIENTNAME \ −t $LOCAL PATH −s $REPSERVER flush status $LOCAL PATH start$CLIENTNAME $SCRIPTS PATH/utils/sync−traces.sh −q −S −f start$CLIENTNAME \ −c ‘date +%s‘ −t $LOCAL PATH −s $REPSERVER # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME 600 else if [ $QUIET == 0 ]; then echo "Collaborative run already full of clients" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME exit 0 fi fi else 610 if [ $QUIET == 0 ]; then echo "Run status: $STATUS. Nothing to do" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME exit 0 fi fi for j in ‘seq −w 1 $GENERATIONS‘ 620 do let "k=j-1" echo " Population analysis on generation: $k" ./scripts/genetic/analyze−generation.pl \ $CONFIG SIMUL TEMPLATE $RUN CONF $LOCAL PATH if [ −f avgfitness ] then 630 avgfit=‘cat avgfitness‘ if [ $CHECKED == "remote" ] 115 then # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME set status $LOCAL PATH gfit−$k−$CLIENTNAME $avgfit 640 # Lock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi echo $avgfit > $LOCAL PATH/gfit−$k rm −f avgfitness fi if [ −f numgenotypes ] 650 then popsize=‘cat numgenotypes‘ if [ $CHECKED == "remote" ] then # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME set status $LOCAL PATH pops−$k−$CLIENTNAME $popsize 660 # Lock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi echo $popsize > $LOCAL PATH/pops−$k rm −f numgenotypes fi 670 echo " New generation: $j" ./scripts/genetic/generate−generation.pl $CONFIG SIMUL TEMPLATE \ $RUN CONF $LOCAL PATH echo " Starting simulations:" echo " Generating run datadirs and moving config files:" for i in $LOCAL PATH/$RUNPREFIX* do 680 if [ $CHECKED == "remote" ] then # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME STATUS=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f status \ −t $i −s $REPSERVER‘ CLIID=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f cliid \ −t $i −s $REPSERVER‘ 690 if [[ "a$STATUS" == "asimulated" | | "a$STATUS" == "asimulating" ]] 116 then if [ $QUIET == 0 ]; then echo " " ; echo "Genotype already \ simulated/simulating, skip simulate"; fi if [[ "a$CLIID" == "a$CLIENTNAME" ]] then if [ $QUIET == 0 ]; then echo " " ; echo " Genotype is on \ my population"; fi else 700 if [ $QUIET == 0 ]; then echo " " ; echo " Genotype is not \ on my population, removing it"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −r −t $i fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME else if [ $QUIET == 0 ]; then echo " Switching $i: ready -> simulating"; fi flush status $i status 710 set status $i status simulating set status $i cliid $CLIENTNAME # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME if [ $QUIET == 0 ]; then echo " Copying resurces and workload to $i"; fi cp $LOCAL PATH/resources "$i" cp $LOCAL PATH/workload−real−unscaled "$i" 720 if [ $QUIET == 0 ]; then echo " " ; echo "Entering simulate.sh:"; fi ./simulate.sh −t $i −l −k −p −s $REPSERVER if [ $QUIET == 0 ]; then echo "Exiting simulate.sh" ; echo " " ; fi FITNESSVALUE=‘$LOCAL PATH/$FITNESS $i/workload−simul−unscaled‘ echo $FITNESSVALUE > "$i/fitness" # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME 730 if [ $QUIET == 0 ]; then echo " Switching $i: simulating -> simulated"; fi unset status $i status simulating set status $i status simulated set status $i fitness $FITNESSVALUE # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi else if [ $QUIET == 0 ]; then echo " Copying resurces and workload to $i"; fi cp $LOCAL PATH/resources "$i" cp $LOCAL PATH/workload−real−unscaled "$i" if [ $QUIET == 0 ]; then echo " " ; echo "Entering simulate.sh:"; fi ./simulate.sh −t $i −l −k if [ $QUIET == 0 ]; then echo "Exiting simulate.sh" ; echo " " ; fi echo " Evaluating fitness:" 117 740 FITNESSVALUE=‘$LOCAL PATH/$FITNESS $i/workload−simul−unscaled‘ 750 echo $FITNESSVALUE > "$i/fitness" fi done done if [ $CHECKED == "remote" ] then # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME 760 flush status $LOCAL PATH stop$CLIENTNAME $SCRIPTS PATH/utils/sync−traces.sh −q −S −f stop$CLIENTNAME \ −c ‘date +%s‘ −t $LOCAL PATH −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −U −f crunning \ −c $CLIENTNAME −t $LOCAL PATH −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −S −f crunned \ −c $CLIENTNAME −t $LOCAL PATH −s $REPSERVER 770 if [ $MAXWORLDS != 0 ] then RUNNEDPROC=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f crunned \ −t $LOCAL PATH −s $REPSERVER | wc −l‘ if [ "a$RUNNEDPROC" == "a" ]; then RUNNEDPROC=0 ; fi if [ $RUNNEDPROC −ge $MAXWORLDS ] then if [ $QUIET == 0 ]; then echo "Genetic simulation done" ; fi 780 if [ $QUIET == 0 ]; then echo " Switching $LOCAL_PATH: running -> runned"; fi $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c running \ −t $LOCAL PATH −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c runned \ −t $LOCAL PATH −s $REPSERVER fi fi # Unlock 790 $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME $SCRIPTS PATH/utils/sync−traces.sh −q −r −t $LOCAL PATH fi fi } # checkinput, check input variables, reporting “bad” or a code for each command checkinput() { 800 # echo “$TARGET VAR $LOCAL VAR $KEEP VAR $SERVER VAR $REMOTE VAR $PUT VAR” CHECKED="bad" if (( $TARGET VAR + $LOCAL VAR == 2 )) && \ (( $SERVER VAR + $REMOTE VAR == 0 )) then 118 # from local to local CHECKED="localr" fi if (( $TARGET VAR + $SERVER VAR + $REMOTE VAR == 3 )) && \ (( $LOCAL VAR == 0 )) 810 then # from server to local CHECKED="remote" fi } localr() { run RETURN=$? if [ $RETURN == 0 ] 820 then if [ $QUIET == 0 ]; then echo "Run successfully done"; fi else echo "Error: Run failed" fi } remote() { # 1) download from server the main run directory (sub excluded) if [ $QUIET == 0 ]; then echo "Downloading pre-run trace"; fi 830 $SYNC TRACES SCRIPT −q −n −g −t $LOCAL PATH −s $SERVER URL RETURN=$? # 2) run if [ $RETURN == 0 ] then if [ $QUIET == 0 ]; then echo " Pre-run trace successfully downloaded"; fi run RETURN=$? if [ $RETURN == 0 ] then 840 if [ $QUIET == 0 ]; then echo "Run successfully done"; fi else echo "Error: Run failed" fi else echo "Error: Downloading failed" fi } if [ $# -eq “$NO ARGS” ] # Script invoked with no command-line args. 850 then echo −e "$HELP" exit $ERROR CODE # Exit and explain usage, if no argument(s) given. fi while getopts "s:t:lr" Option # Command arguments: # -t target (local path) # -s server # -r, -l remote or local operations 860 do 119 case $Option in s ) SERVER VAR=1 SERVER URL=$OPTARG;; t ) TARGET VAR=1 LOCAL PATH=$OPTARG;; r ) REMOTE VAR=1;; l ) LOCAL VAR=1;; h ) echo −e $HELP exit 1;; * ) echo −e $HELP exit 1;; esac done 870 checkinput if [ $CHECKED == "bad" ] then echo "Error: bad parameters" echo −e $HELP exit 1 else 880 if [ $CHECKED == "localr" ] then localr if [[ $QUIET == 0 && $RETURN == 0 ]]; then echo "Local run ok"; fi exit $RETURN fi 890 if [ $CHECKED == "remote" ] then remote if [[ $QUIET == 0 && $RETURN == 0 ]]; then echo "Remote run ok"; fi exit $RETURN fi fi simulate.sh #!/bin/bash # NOTE: # # source "./functions.sh" # INITIALIZATION SYNC TRACES SCRIPT="./scripts/utils/sync-traces.sh" # path of sync-traces script SCRIPTS PATH="./scripts" # Path of the scripts ERROR CODE=39 # default error code HELP="Usage: $0 [-l|-r|-p|-k] [-u] -t trace [-s server] [-h]\n\n -t target pathname of target\n -s server remote traces server\n -r, -l download target from remote server or use local target\n 120 10 -p put result on remote server\n -k do not erase local target on exit\n\n -u check & set simulating/ed status\n\n -h this help.\n\n 20 Usage examples:\n simulate from local to local: $0 -l -k -t target\n simulate from remote server to local: $0 -r -k -t target -s server\n simulate from local to remote server: $0 -l -p -t target -s server\n simulate from local to remote server and keep a local copy: $0 -l -p -k \ -t target -s server\n simulate from remote server to remote server: $0 -r -p -t target -s server\n 30 simulate from remote server to remote server and keep a local copy: $0 -r -p -k \ -t target -s server\n" # Var initialization NO ARGS=0 SERVER VAR=0 TARGET VAR=0 REMOTE VAR=0 LOCAL VAR=0 40 PUT VAR=0 KEEP VAR=0 QUIET=0 VERBOSE=0 STATUS VAR=0 CLIENTNAME="randomize" if [ $CLIENTNAME == "randomize" ] then 50 TEMP=‘echo $USER $HOSTNAME $RANDOM | md5sum | cut −d" " −f1‘ CLIENTNAME=${TEMP:24} fi simulate() { if [ $STATUS VAR == 1 ] then # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME 60 CHECK=2 # check status return 0 if found, 1 if not found, 2 if an error occur check status $LOCAL PATH status simulating > /dev/null CHECK=$? if [ $CHECK == 0 ] then if [ $QUIET == 0 ]; then echo " Trace $LOCAL_PATH is already simulating" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME return 2 # trace in simulating state fi 121 70 CHECK=2 # check status return 0 if found, 1 if not found, 2 if an error occur check status $LOCAL PATH status simulated > /dev/null CHECK=$? if [ $CHECK == 0 ] then if [ $QUIET == 0 ]; then echo " Trace $LOCAL_PATH already simulated" ; fi 80 # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME return 2 # trace in simulating state fi CHECK=0 # Check for $LOCAL PATH existence $SYNC TRACES SCRIPT −q −e −t $LOCAL PATH −s $SERVER URL CHECK=$? 90 if [ $CHECK == 2 ] then if [ $QUIET == 0 ]; then echo " Trace $LOCAL_PATH does not exits: \ assuming ready" ; fi # trace not in simulating/ed state yet, locking now setting status=simulating set status $LOCAL PATH status simulating > /dev/null # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME 100 else CHECK=2 # check status return 0 if found, 1 if not found, 2 if an error occur check status $LOCAL PATH status ready > /dev/null CHECK=$? if [ $CHECK == 0 ] then if [ $QUIET == 0 ]; then echo " Trace $LOCAL_PATH: ready" ; fi 110 # trace not in simulating/ed state yet, locking now setting status=simulating set status $LOCAL PATH status simulating > /dev/null # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME else if [ $QUIET == 0 ]; then echo " Trace $LOCAL_PATH: unknown status" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME 120 return 1 # trace in error state fi fi fi # now proper simulate if [ $QUIET == 0 ]; then echo " Checking headers"; fi if [ $QUIET == 0 ]; then echo " Checking datadir: $LOCAL_PATH"; fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of a workload trace: \ 122 130 $LOCAL_PATH/workload-real-unscaled . ."; fi check prerequisite "$LOCAL_PATH/workload-real-unscaled" if [ $RETURN != 0 ] then return 1 else WORKLOAD REAL UNSCALED="$LOCAL_PATH/workload-real-unscaled" fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of a resources trace: \ 140 $LOCAL_PATH/resources . ."; fi check prerequisite "$LOCAL_PATH/resources" if [ $RETURN != 0 ] then return 1 else WORKLOAD RESOURCES="$LOCAL_PATH/resources" fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of a config file: \ 150 $LOCAL_PATH/config . ."; fi check prerequisite "$LOCAL_PATH/config" if [ $RETURN != 0 ] then return 1 else CONFIG RESOURCES="$LOCAL_PATH/config" fi if [ $QUIET == 0 ]; then echo −n " Checking the existence of a header config file: \ 160 $LOCAL_PATH/config-simul-header . ."; fi check prerequisite "$LOCAL_PATH/config-simul-header" if [ $RETURN != 0 ] then if [ $QUIET == 0 ]; then echo −n " Checking the existence of a header config file: \ traces/config-simul-header . ."; fi check prerequisite "traces/config-simul-header" if [ $RETURN != 0 ] then return 1 170 else CONFIG SIMUL HEADER="traces/config-simul-header" fi else CONFIG SIMUL HEADER="$LOCAL_PATH/config-simul-header" fi if [ $QUIET == 0 ]; then echo " Starting pre simulation tasks:"; fi if [ $QUIET == 0 ]; then echo " Cleaning old files"; fi rm −fv resources maui.cfg workload stats/simstat.out if [ $QUIET == 0 ]; then echo " Copying configuration"; fi cat $CONFIG SIMUL HEADER $CONFIG RESOURCES > maui.cfg if [ $QUIET == 0 ]; then echo " Copying resources trace file"; fi cat $WORKLOAD RESOURCES > resources FIRST REAL RUN=‘./scripts/utils/workload−first−submitted.pl \ $WORKLOAD REAL UNSCALED‘ 123 180 CURRENTTIME=‘date +%s‘; STARTTIME=$(($CURRENTTIME + 3)) OFFSET=$(($STARTTIME − $FIRST REAL RUN)) 190 WORKLOAD REAL SCALED=$LOCAL PATH/workload−real−scaled WORKLOAD SIMUL SCALED=$LOCAL PATH/workload−simul−scaled WORKLOAD SIMUL UNSCALED=$LOCAL PATH/workload−simul−unscaled if [ $QUIET == 0 ]; then echo " Shifting workload in the future"; fi $SCRIPTS PATH/utils/workload−time−updater.pl $WORKLOAD REAL UNSCALED \ $WORKLOAD REAL SCALED $OFFSET if [ $QUIET == 0 ]; then echo " Ordering workload"; fi cat $WORKLOAD REAL SCALED | sort −n > $LOCAL PATH/temp workload 200 mv −f $LOCAL PATH/temp workload $WORKLOAD REAL SCALED if [ $QUIET == 0 ]; then echo " Copying workload"; fi cat $WORKLOAD REAL SCALED > workload if [ $QUIET == 0 ]; then echo −n " Starting simulation at "; echo $STARTTIME ; fi $SCRIPTS PATH/utils/run−at.pl $STARTTIME ENDTIME=‘date +%s‘ 210 if [ $QUIET == 0 ]; then echo " Writing simulation duration:"; fi let "SIMTIME= $ENDTIME - $STARTTIME" echo $SIMTIME > $LOCAL PATH/duration if [[ $CHECKED == "s2s" | | $CHECKED == "l2s" ]] then # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME 220 flush status $LOCAL PATH duration > /dev/null set status $LOCAL PATH duration $SIMTIME > /dev/null # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi if [ $QUIET == 0 ]; then echo " Starting post simulation tasks:"; fi 230 if [ $QUIET == 0 ]; then echo " Moving results"; fi cat stats/simstat.out > $WORKLOAD SIMUL SCALED if [ $QUIET == 0 ]; then echo " Shifting simulation in the past"; fi $SCRIPTS PATH/utils/workload−time−updater.pl $WORKLOAD SIMUL SCALED \ $WORKLOAD SIMUL UNSCALED "-$OFFSET" if [ $QUIET == 0 ]; then echo " Creating Human readable formats:"; fi $SCRIPTS PATH/utils/workload−clean.pl $WORKLOAD SIMUL SCALED \ $LOCAL PATH/workload−simul−scaled−cleaned $SCRIPTS PATH/utils/workload−clean.pl $WORKLOAD REAL SCALED \ $LOCAL PATH/workload−real−scaled−cleaned $SCRIPTS PATH/utils/workload−clean.pl $WORKLOAD REAL UNSCALED \ $LOCAL PATH/workload−real−unscaled−cleaned 124 240 $SCRIPTS PATH/utils/workload−clean.pl $WORKLOAD SIMUL UNSCALED \ $LOCAL PATH/workload−simul−unscaled−cleaned if [ $QUIET == 0 ]; then echo " Cleaning files"; fi if [ $VERBOSE == 0 ] then 250 rm −f resources maui.cfg workload stats/simstat.out else rm −fv resources maui.cfg workload stats/simstat.out fi # Compressing results if [ $QUIET == 0 ]; then echo " Compressing files" ; fi if [ $VERBOSE == 0 ] then gzip −fq $LOCAL PATH/workload* 260 else gzip −f $LOCAL PATH/workload* fi if [ $STATUS VAR == 1 ] then # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME # setting status=simulated 270 set status $LOCAL PATH status simulated > /dev/null # now we can unset status simulating unset status $LOCAL PATH status simulating > /dev/null # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi } clean local() { 280 if [ $QUIET == 0 ]; then echo " Cleaning $LOCAL_PATH"; fi $SYNC TRACES SCRIPT −q −r −t $LOCAL PATH RETURN=$? if [[ $RETURN != 0 ]]; then echo " Error: cleaning $LOCAL_PATH error"; fi } # checkinput, check input variables, reporting “bad” or a code for each command checkinput() { # echo “$TARGET VAR $LOCAL VAR $KEEP VAR $SERVER VAR $REMOTE VAR $PUT VAR” CHECKED="bad" 290 if (( $TARGET VAR + $LOCAL VAR == 2 )) && \ (( $SERVER VAR + $REMOTE VAR + $PUT VAR == 0 )) then # from local to local CHECKED="l2l" fi if (( $TARGET VAR + $SERVER VAR + $REMOTE VAR == 3 )) && \ (( $LOCAL VAR + $PUT VAR == 0 )) then # from server to local 300 CHECKED="s2l" 125 fi if (( $TARGET VAR + $SERVER VAR + $LOCAL VAR + $PUT VAR == 4 )) && \ (( $REMOTE VAR == 0 )) then # from local to server (remove local target) CHECKED="l2s" fi if (( $TARGET VAR + $SERVER VAR + $REMOTE VAR + $PUT VAR == 4 )) && \ (( $LOCAL VAR == 0 )) 310 then # from server to server (remove local target) CHECKED="s2s" fi } l2l() { # local to local simulate if [ $QUIET == 0 ]; then echo "Local to local simulating $LOCAL_PATH"; fi simulate 320 RETURN=$? if [[ $RETURN != 0 ]]; then echo "Error: simulating $LOCAL_PATH error"; fi } s2l() { # server to local simulate if [ $QUIET == 0 ]; then echo "Server to local simulating $LOCAL_PATH from \ $SERVER_URL"; fi if [ $QUIET == 0 ]; then echo "Downloading $LOCAL_PATH from $SERVER_URL"; fi 330 $SYNC TRACES SCRIPT −g −t $LOCAL PATH −s $SERVER URL RETURN=$? # Simulate if [ $RETURN == 0 ] then simulate RETURN=$? if [[ $RETURN != 0 ]]; then echo "Error: simulating $LOCAL_PATH"; fi else echo "Error: Downloading $LOCAL_PATH from $SERVER_URL failed" 340 fi } l2s() { # local to server simulate if [ $QUIET == 0 ]; then echo "Local to server simulating $LOCAL_PATH to \ $SERVER_URL"; fi # simulate simulate RETURN=$? 350 # check simulate return (0 ok, 1 error, 2 simulating/ed) if [ $RETURN == 1 ] then echo "Error: simulating $LOCAL_PATH error" return $RETURN fi 126 if [ $RETURN == 2 ] then 360 if [ $QUIET == 0 ]; then echo " $LOCAL_PATH already simulating/ed"; fi UPLOAD="no" fi # Check for $LOCAL PATH existence $SYNC TRACES SCRIPT −q −e −t $LOCAL PATH −s $SERVER URL RETURN=$? # if trace exist do not perform a clean, print a warning if [ $RETURN == 1 ] 370 then echo "Error: cannot check trace on server, upload aborted, local simulated trace \ not deleted" return $RETURN fi # upload to server if [ "a$UPLOAD" != "ano" ] then if [ $QUIET == 0 ]; then echo "Uploading trace to server"; fi 380 $SYNC TRACES SCRIPT −q −p −t $LOCAL PATH −s $SERVER URL RETURN=$? if [ $RETURN == 0 ] then if [ $QUIET == 0 ]; then echo "Uploading ok"; fi # clean local if requested if [ $KEEP VAR == 0 ]; then clean local; fi else echo "Error: Uploading failed" 390 fi else if [ $QUIET == 0 ]; then echo "Skipping uploading trace to server"; fi RETURN=0 fi } s2s() { # server to server simulate 400 if [ $QUIET == 0 ]; then echo "Server to server simulating - local path=$LOCAL_PATH; \ server=$SERVER_URL"; fi # Control status to save time if [ $STATUS VAR == 1 ] then # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME CHECK=2 410 # check status return 0 if found, 1 if not found, 2 if an error occur check status $LOCAL PATH status simulating > /dev/null CHECK=$? if [ $CHECK == 0 ] then 127 if [ $QUIET == 0 ]; then echo " Trace is already simulating" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME return 0 # not an error but trace is in simulating state -> exit fi 420 CHECK=2 # check status return 0 if found, 1 if not found, 2 if an error occur check status $LOCAL PATH status simulated > /dev/null CHECK=$? if [ $CHECK == 0 ] then if [ $QUIET == 0 ]; then echo " Trace already simulated" ; fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME 430 return 0 # not an error but trace is in simulated state -> exit fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi # download from server $SYNC TRACES SCRIPT −g −t $LOCAL PATH −s $SERVER URL RETURN=$? 440 # simulate if [ $RETURN == 0 ] then simulate RETURN=$? else echo "Error: downloading error" fi # check simulate return (0 ok, 1 error, 2 simulating/ed) if [ $RETURN == 1 ] 450 then echo "Error: simulating $LOCAL_PATH error" return $RETURN fi if [ $RETURN == 2 ] then if [ $QUIET == 0 ]; then echo " $LOCAL_PATH already simulating/ed"; fi if [ $QUIET == 0 ]; then echo " Skipping uploading trace to server"; fi RETURN=0 460 fi if [ $RETURN == 0 ] then # upload to server $SYNC TRACES SCRIPT −p −t $LOCAL PATH −s $SERVER URL RETURN=$? # 4) clean local if [ $RETURN == 0 ] then 470 if [ $KEEP VAR == 0 ]; then clean local; fi RETURN=$? 128 else echo "Error: uploading error" fi if [[ $RETURN != 0 ]]; then echo "Error: local cleaning error"; fi fi } if [ $# -eq “$NO ARGS” ] # Script invoked with no command-line args. 480 then echo −e $HELP exit $ERROR CODE # Exit and explain usage, if no argument(s) given. fi while getopts "s:t:rlpkuhvq" Option # Command arguments: # -t target (local path) # -s server # -r, -l download target from remote server or use local path for target 490 # -p put result on remote server # -k do not erase local target on exit # -u check simulating/ed status do case $Option in s ) SERVER VAR=1 SERVER URL=$OPTARG;; t ) TARGET VAR=1 LOCAL PATH=$OPTARG 500 TARGET=‘basename $OPTARG‘;; r ) REMOTE VAR=1;; l ) LOCAL VAR=1;; p ) PUT VAR=1;; k ) KEEP VAR=1;; v ) VERBOSE=1;; q ) QUIET=1;; u ) STATUS VAR=1;; h ) echo −e $HELP exit 1;; 510 * ) echo −e $HELP exit 1;; esac done checkinput if [ $CHECKED == "bad" ] then echo "Error: bad parameters" echo −e $HELP 520 exit 1 else if [ $CHECKED == "l2l" ] then l2l if [[ $QUIET == 0 && $RETURN == 0 ]]; \ then echo "Local to Local simulation ok"; fi exit $RETURN 129 fi 530 if [ $CHECKED == "s2l" ] then s2l if [[ $QUIET == 0 && $RETURN == 0 ]]; \ then echo "Server to local simulation ok"; fi exit $RETURN fi if [ $CHECKED == "l2s" ] then l2s if [[ $QUIET == 0 && $RETURN == 0 ]]; \ then echo "Local to server simulation ok"; fi exit $RETURN fi if [ $CHECKED == "s2s" ] then s2s if [[ $QUIET == 0 && $RETURN == 0 ]]; \ then echo "Server to server simulation ok"; fi exit $RETURN fi 540 550 fi superexecute.sh #!/bin/bash source "./functions.sh" # INITIALIZATION SYNC TRACES SCRIPT="./scripts/utils/sync-traces.sh" # path of sync-traces script SCRIPTS PATH="./scripts" JOBQUEUE="traces/000000-jobqueue" REPSERVER="mauirep.fisica.unipg.it" CLIENTNAME="randomize" 10 if [ $CLIENTNAME == "randomize" ] then TEMP=‘echo $USER $HOSTNAME $RANDOM | md5sum | cut −d" " −f1‘ CLIENTNAME=${TEMP:24} fi while [ 1 ] do # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME 20 NEXTREADY=‘$SCRIPTS PATH/utils/ls−feat.sh −t $JOBQUEUE −f status \ −c ready −n 1‘ if [ "a$NEXTREADY" != "a" ] then 130 $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c ready \ −t $NEXTREADY −s $REPSERVER TYPE=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f type \ 30 −t $NEXTREADY −s $REPSERVER‘ if [ "a$TYPE" == "aparallel" ] then $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c running \ −t $NEXTREADY −s $REPSERVER else $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c taken \ −t $NEXTREADY −s $REPSERVER fi 40 $SCRIPTS PATH/utils/sync−traces.sh −q −g −t $NEXTREADY −s $REPSERVER fi # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME # Execute the given job if [ "a$NEXTREADY" != "a" ] then 50 echo echo "First execution of $NEXTREADY" $NEXTREADY/command.sh >> $NEXTREADY/$CLIENTNAME.o 2> \ $NEXTREADY/$CLIENTNAME.e echo "Execution of $NEXTREADY done" echo if [ "a$TYPE" == "aparallel" ] then 60 while [ 1 ] do # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c running \ −t $NEXTREADY −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c exitwait \ −t $NEXTREADY −s $REPSERVER 70 RUNNINGPROC=1 RUNNINGPROC=‘$SCRIPTS PATH/utils/sync−traces.sh −q −R −f crunning \ −t $NEXTREADY −s $REPSERVER | wc −l‘ if [ $RUNNINGPROC == 0 ] then break else # UnLock 80 $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi sleep 5 131 done $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c exitwait \ −t $NEXTREADY −s $REPSERVER else # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME 90 $SCRIPTS PATH/utils/sync−traces.sh −q −U −f status −c taken \ −t $NEXTREADY −s $REPSERVER fi $SCRIPTS PATH/utils/sync−traces.sh −q −S −f status −c ok \ −t $NEXTREADY −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −p −t $NEXTREADY −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −r −t $NEXTREADY 100 # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME fi # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME NEXTRUNNING=‘$SCRIPTS PATH/utils/ls−feat.sh −t $JOBQUEUE −f status \ −c running −n 1‘ 110 if [ "a$NEXTRUNNING" != "a" ] then $SCRIPTS PATH/utils/sync−traces.sh −q −S −f crunning −c $CLIENTNAME \ −t $NEXTRUNNING −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −g −t $NEXTRUNNING −s $REPSERVER fi # Unlock 120 $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME if [ "a$NEXTRUNNING" != "a" ] then echo echo "Collaborating with $NEXTRUNNING" $NEXTRUNNING/command.sh >> $NEXTRUNNING/$CLIENTNAME.o 2> \ $NEXTRUNNING/$CLIENTNAME.e echo "Stop collaborating with $NEXTRUNNING" echo 130 # Lock $SCRIPTS PATH/manager/client.py −l −c $CLIENTNAME $SCRIPTS PATH/utils/sync−traces.sh −q −U −f crunning −c $CLIENTNAME \ −t $NEXTRUNNING −s $REPSERVER # Unlock $SCRIPTS PATH/manager/client.py −u −c $CLIENTNAME 140 132 $SCRIPTS PATH/utils/sync−traces.sh −q −p −t $NEXTRUNNING −s $REPSERVER $SCRIPTS PATH/utils/sync−traces.sh −q −r −t $NEXTRUNNING fi # rpmpollintervall sleep 10 done sync-traces.sh #!/bin/bash # NOTE: version 1.3.1 # CHANGES: 1.3.1: better usage output # 1.3: -n option to NOT sync subdirectories # 1.2: -d option to delete on sync # 1.1: rsync with –exclude=“.*” # features files on server are now hidden files # 1.0a: added check on target existance before get, if target does not exist exit with 1 RSYNC USER="maui" # rsync remote user RSYNC DOMAIN="maui" # rsync remote domain RSYNC DIR="/rep/" # rsync remote directory in $RSYNC DOMAIN ERROR CODE=39 # Default error code HELP="Usage:\t$0 [-g|-p] [-d] [-n] [-q] -t trace -s server [-h]\n \t$0 -e [-q] -t trace -s server [-h]\n \t$0 -r [-q] -t trace [-h]\n \t$0 [-S|-U] [-q] -c char -f feat -t trace -s server [-h]\n \t$0 -R [-q] -f feat -t trace -s server [-h]\n \t$0 -A [-q] -t trace -s server [-h]\n\n 10 20 Perform various tasks relative to traces syncronization.\n\n -s server\t\tremote traces server.\n -t target\t\tname of the trace.\n\n -g\t\t\tget, download remote trace from server.\n -p\t\t\tput, upload trace to the remote server.\n\n -d\t\t\tdelete on sync, files not present on source will be removed from destination.\n -n\t\t\tdo NOT sync subdirectories.\n\n 30 -r\t\t\tremove, delete a local trace.\n\n -e\t\t\texists, check if target exists on remote server, -s, -t required.\n\n -q\t\t\tquiet, output only error messages.\n\n -f feature\t\tfeature to set, unset or read.\n -c characteristic\tvalue of the feature to set, unset.\n -S\t\t\tset, set a characteristic of a feature, -f, -c, -s, -t required.\n -U\t\t\tunset, remove a characteristic of a feature, -f, -c, -s, -t required.\n -R\t\t\tread, report the characteristics of a feature, -f, -c, -s, -t required.\n\n -A\t\t\tread all, report all the characteristics of all the features, -s, -t required.\n\n 133 40 -h\t\t\tthis help.\n\n Usage examples:\n to get target from server: $0 -g -t target -s server\n 50 to put target in server: $0 -p -t target -s server\n to remove a local target: $0 -r -t target\n to check existance of a target on server: $0 -e -t target -s server\n to set a characteristc of a feature: $0 -S -c char -f feat -t target -s server\n to unset a characteristc of a feature: $0 -U -c char -f feat -t target -s server\n to read the characteristcs of a feature: $0 -R -f feat -t target -s server\n to read the characteristcs of all the features: $0 -A -t target -s server\n" NO ARGS=0 # Var initialization GET VAR=0 60 PUT VAR=0 CLEAN=0 EXISTS=0 QUIET=0 SERVER VAR=0 TARGET VAR=0 FEATURE POP=0 70 FEATURE PUSH=0 FEATURE READ=0 FEATURE READALL=0 CHAR VAR=0 FEAT VAR=0 DELETING=0 FULLTREE=1 RETURN=0 FEATURE=0 80 STATUS=0 # checkinput, check input var, reporting “bad” or a code for each command checkinput() { CHECKED="bad" if (( $GET VAR + $SERVER VAR + $TARGET VAR == 3 )) && \ (( $PUT VAR + $CLEAN + $EXISTS + $FEATURE PUSH + $FEATURE POP + \ $FEATURE READ + $FEATURE READALL + $CHAR VAR + $FEAT VAR == 0 )) then # program will make a “get target from server” 90 CHECKED="get" fi if (( $PUT VAR + $SERVER VAR + $TARGET VAR == 3 )) && \ (( $GET VAR + $CLEAN + $EXISTS + $FEATURE PUSH + $FEATURE POP + \ $FEATURE READ + $FEATURE READALL + $CHAR VAR + $FEAT VAR == 0 )) then # program will make a “put target on server” CHECKED="put" fi if (( $CLEAN + $TARGET VAR == 2 )) && (( $PUT VAR + $GET VAR + \ $SERVER VAR + $EXISTS + $FEATURE PUSH + $FEATURE POP + \ $FEATURE READ + $FEATURE READALL + $CHAR VAR + $FEAT VAR == 0 )) then 134 100 # program will make a “clean local target” CHECKED="clr" fi if (( $EXISTS + $SERVER VAR + $TARGET VAR == 3 )) && \ (( $PUT VAR + $GET VAR + $CLEAN + $FEATURE PUSH + $FEATURE POP + \ $FEATURE READ + $FEATURE READALL + $CHAR VAR + $FEAT VAR == 0 )) then 110 # program will make a “exists remote target?” CHECKED="exist" fi if (( $FEATURE PUSH + $SERVER VAR + $TARGET VAR + $CHAR VAR + \ $FEAT VAR == 5 )) && (( $PUT VAR + $GET VAR + $CLEAN + $EXISTS + \ $FEATURE POP + $FEATURE READ + $FEATURE READALL == 0 )) then # program will make a “set feature” CHECKED="set" fi 120 if (( $FEATURE POP + $SERVER VAR + $TARGET VAR + $CHAR VAR + \ $FEAT VAR == 5 )) && (( $PUT VAR + $GET VAR + $CLEAN + $EXISTS + \ $FEATURE PUSH + $FEATURE READ + $FEATURE READALL == 0 )) then # program will make a “unset feature” CHECKED="unset" fi if (( $FEATURE READ + $SERVER VAR + $TARGET VAR + \ $FEAT VAR == 4 )) && (( $PUT VAR + $GET VAR + $CLEAN + $EXISTS + \ $FEATURE PUSH + $FEATURE POP + $FEATURE READALL + $CHAR VAR == 0 )) 130 then # program will make a “read feature” CHECKED="read" fi if (( $FEATURE READALL + $SERVER VAR + $TARGET VAR == 3 )) && \ (( $PUT VAR + $GET VAR + $CLEAN + $EXISTS + $FEATURE PUSH + \ $FEATURE POP + $FEATURE READ + $CHAR VAR + $FEAT VAR == 0 )) then # program will make a “read all features” CHECKED="readall" 140 fi } # check, return if a file/dir exists on remote server check() { if [ −z "$1" ] # parameter #1 of zero length or no parameter passed. then ISTHERE="error" else ISTHERE=0 150 ssh −− $RSYNC USER@$SERVER URL ls −− $RSYNC DIR/$1&>/dev/null RETURN=$? if [ $RETURN == 0 ] then ISTHERE="yes" else if [ $RETURN == 2 ] then ISTHERE="no" else 160 135 ISTHERE="error" fi fi fi } read() { READ RETURN="" if [ −z "$1" ] # parameter #1 of zero length or no parameter passed. then 170 READ RETURN="" else FEATURE=$1 check $TARGET if [ $ISTHERE == "yes" ] # $TARGET exists on $SERVER URL then READ RETURN=‘ssh −− $RSYNC USER@$SERVER URL cat −− \ $RSYNC DIR/$TARGET/.$FEATURE−status.lock 2> /dev/null‘ else READ RETURN="" 180 fi fi } read all() { READ RETURN="" check $TARGET if [ $ISTHERE == "yes" ] # $TARGET exists on $SERVER URL then # return content of all .lock files on $TARGET 190 READ RETURN=$(ssh −− $RSYNC USER@$SERVER URL "for i in \ \‘ls $RSYNC_DIR/$TARGET/.*.lock\‘; do echo \$i\‘cat \$i\‘ | \ sed -e ’s/-status\.lock/: /’ | sed -e ’s/.*$TARGET.//’ | \ sed -e ’s/[.]//g’; done") else READ RETURN="" fi } 200 # push, set $FEATURE as $STATUS in $TARGET on $SERVER URL push() { PUSH RETURN=0 if [ −z "$1" ] | | [ −z "$2" ] # parameter #1 or #2 of zero length or no parameter passed. then PUSH RETURN=1 else FEATURE=$1 STATUS=$2 check $TARGET 210 if [ $ISTHERE == "yes" ] # $TARGET exists on $SERVER URL then ssh −− $RSYNC USER@$SERVER URL "grep -- \ $STATUS $RSYNC_DIR/$TARGET/.$FEATURE-status.lock &> /dev/null" RETURN=$? if [ $RETURN == 0 ] # $FEATURE is already on $STATUS then 136 PUSH RETURN=0 else if [ $RETURN == 1 ] | | [ $RETURN == 2 ] # $STATUS missing (1) \ 220 or $FEATURE−status.lock missing (2) then ssh −− $RSYNC USER@$SERVER URL "echo -e $STATUS >> \ $RSYNC_DIR/$TARGET/.$FEATURE-status.lock 2> /dev/null" RETURN=$? if [ $RETURN == 0 ] then PUSH RETURN=0 else PUSH RETURN=1 230 fi else # grep or comunication error PUSH RETURN=1 fi fi else # connection error OR $TARGET missing on $SERVER URL if [ $ISTHERE == "no" ] then 240 # there is no $TARGET on $SERVER URL. PUSH RETURN=1 else # a connection error maybe? PUSH RETURN=1 fi fi fi } 250 # pop, remove a $STATUS of a $FEATURE in $TARGET on $SERVER URL pop() { POP RETURN=0 if [ −z "$1" ] | | [ −z "$2" ] # parameter #1 or #2 of zero length or no parameter passed. then POP RETURN=1 else FEATURE=$1 STATUS=$2 check $TARGET 260 if [ $ISTHERE == "yes" ] # $TARGET exists on $SERVER URL then check $TARGET/.$FEATURE−status.lock if [ $ISTHERE == "yes" ] # $TARGET/.$FEATURE-status.lock exists \ on $SERVER URL then read $FEATURE $STATUS if [ READ RETURN == "" ] then POP RETURN=0 # no action to perform else ISEMPTY=‘ssh −− $RSYNC USER@$SERVER URL wc −l \ $RSYNC DIR/$TARGET/.$FEATURE−status.lock |cut −d " " −f 1‘ if [ $ISEMPTY == "1" ] 137 270 then # delete $FEATURE-status.lock ssh −− $RSYNC USER@$SERVER URL rm −f −− \ $RSYNC DIR/$TARGET/.$FEATURE−status.lock RETURN=$? if [ $RETURN == 0 ] then 280 POP RETURN=0 else POP RETURN=1 fi else # create .tmp ssh −− $RSYNC USER@$SERVER URL "egrep -v -- \ "^$STATUS$" $RSYNC_DIR/$TARGET/.$FEATURE-status.lock > \ $RSYNC_DIR/$TARGET/.$FEATURE-status.tmp" RETURN=$? if [ $RETURN == 0 ] 290 then # rm .lock ssh −− $RSYNC USER@$SERVER URL rm −f −− \ $RSYNC DIR/$TARGET/.$FEATURE−status.lock RETURN=$? if [ $RETURN == 0 ] then # move .tmp in .lock ssh −− $RSYNC USER@$SERVER URL "mv -- \ $RSYNC_DIR/$TARGET/.$FEATURE-status.tmp \ $RSYNC_DIR/$TARGET/.$FEATURE-status.lock" RETURN=$? 300 if [ $RETURN == 0 ] then POP RETURN=0 else POP RETURN=1 fi else POP RETURN=1 fi else 310 POP RETURN=1 fi fi fi else if [ $ISTHERE == "no" ] # no action to perform then POP RETURN=0 else POP RETURN=1 320 fi fi else if [ $ISTHERE == "no" ] # no action to perform then POP RETURN=0 else POP RETURN=1 fi fi 330 fi 138 } if [ $# -eq “$NO ARGS” ] # Script invoked with no command-line args. then echo −e $HELP exit $ERROR CODE # Exit and explain usage, if no argument(s) given. fi while getopts "s:t:gpluvrqef:c:SRAUdhn" Option 340 # Command arguments: # -s server # -t target # -g, -p, get, put ## -l lock ## -u unlock ## -v verify lock # -r remove # -q quiet # -e exists 350 # -R read feature # -A read all feature # -S set feature # -U unset feature # -d delete on sync # -n DO NOT sync subdirectory do case $Option in s ) SERVER VAR=1 SERVER URL=$OPTARG;; 360 t ) TARGET VAR=1 LOCAL PATH=$OPTARG TARGET=$OPTARG;; g ) GET VAR=1;; p ) PUT VAR=1;; r ) CLEAN=1;; q ) QUIET=1;; e ) EXISTS=1;; f ) FEAT VAR=1 FEATURE=$OPTARG;; 370 c ) CHAR VAR=1 CHAR=$OPTARG;; S ) FEATURE PUSH=1;; R ) FEATURE READ=1;; A ) FEATURE READALL=1;; U ) FEATURE POP=1;; d ) DELETING=1;; n ) FULLTREE=0;; h ) echo −e $HELP exit 1;; 380 * ) echo −e $HELP exit 1;; esac done checkinput if [ $CHECKED == "bad" ] then 139 echo "Error: bad parameters" echo −e $HELP 390 exit 1 else if [ $CHECKED == "get" ] then # get $TARGET trace from the remote rsync server $SERVER URL. # does $TARGET exists? check $TARGET if [ $ISTHERE == "yes" ] then 400 # no local dir $TARGET? we create it. if [ ! −d $LOCAL PATH ] then mkdir −p −− $LOCAL PATH/ RETURN=$? fi if [ $RETURN == 0 ] then if [ $DELETING == 1 ] then 410 if [ $FULLTREE == 1 ] then # download $TARGET with –del, -r options rsync −rlpt −−exclude=".*" −−del −q −e ssh −− \ $RSYNC USER@$SERVER URL::$RSYNC DOMAIN/$LOCAL PATH/ \ $LOCAL PATH/ RETURN=$? else # download $TARGET with –del option, without -r rsync −lpt −−exclude=".*" −−del −q −e ssh −− \ 420 $RSYNC USER@$SERVER URL::$RSYNC DOMAIN/$LOCAL PATH/* \ $LOCAL PATH/ RETURN=$? fi else if [ $FULLTREE == 1 ] then # download $TARGET with -r option rsync −rlpt −−exclude=".*" −q −e ssh −− \ $RSYNC USER@$SERVER URL::$RSYNC DOMAIN/$LOCAL PATH/ \ 430 $LOCAL PATH/ RETURN=$? else # download $TARGET without -r option rsync −lpt −−exclude=".*" −q −e ssh −− \ $RSYNC USER@$SERVER URL::$RSYNC DOMAIN/$LOCAL PATH/* \ $LOCAL PATH/ RETURN=$? fi fi 440 if [ $RETURN != 0 ] then ERROR CODE=$RETURN echo "rsync error: $RETURN" exit $RETURN 140 fi if [ $QUIET == 0 ]; then echo "all seems ok"; fi ERROR CODE=$RETURN exit $ERROR CODE else 450 # something wrong with mkdir echo "error making local directory: $RETURN" exit $RETURN fi else if [ $ISTHERE == "no" ] then if [ $QUIET == 0 ]; then echo "$TARGET does not exists on $SERVER_URL"; fi exit 1 else 460 echo "ssh error?" exit 1 fi fi fi if [ $CHECKED == "put" ] then # put $TARGET trace on the remote rsync server $SERVER URL. # first we test if there is $TARGET on $SERVER URL. 470 check $LOCAL PATH if [ $ISTHERE != "yes" ] then # a connection error OR $TARGET is missing on $SERVER URL if [ $ISTHERE == "no" ] then # if there is no $TARGET on $SERVER URL we make it. ssh −− $RSYNC USER@$SERVER URL mkdir −p −− \ $RSYNC DIR/$LOCAL PATH RETURN=$? 480 if [ $RETURN != 0 ] then # or not :) echo "error making remote dir $TARGET: $RETURN" exit $RETURN fi else # probably there is a connection error. echo "ssh error: $RETURN" exit 1 490 fi fi # finally we rsync $TARGET. if [ $DELETING == 1 ] then # rsync $TARGET with –del option rsync −rlpt −−exclude=".*" −−del −q −e ssh \ $LOCAL PATH/ $RSYNC USER@$SERVER URL::$RSYNC DOMAIN/\ $LOCAL PATH/ RETURN=$? 500 else # rsync $TARGET $TARGET 141 rsync −rlpt −−exclude=".*" −q −e ssh \ $LOCAL PATH/ $RSYNC USER@$SERVER URL::$RSYNC DOMAIN/\ $LOCAL PATH/ RETURN=$? fi if [ $DELETING == 1 ] then 510 if [ $FULLTREE == 1 ] then # rsync $TARGET with –del, -r options rsync −rlpt −−exclude=".*" −−del −q −e ssh \ $LOCAL PATH/ $RSYNC USER@$SERVER URL::$RSYNC DOMAIN/\ $LOCAL PATH/ RETURN=$? else # rsync $TARGET with –del option, without -r rsync −lpt −−exclude=".*" −−del −q −e ssh \ 520 $LOCAL PATH/* $RSYNC USER@$SERVER URL::$RSYNC DOMAIN/\ $LOCAL PATH/ RETURN=$? fi else if [ $FULLTREE == 1 ] then # rsync $TARGET with -r option rsync −rlpt −−exclude=".*" −q −e ssh \ $LOCAL PATH/ $RSYNC USER@$SERVER URL::$RSYNC DOMAIN/\ 530 $LOCAL PATH/ RETURN=$? else # rsync $TARGET without -r option rsync −lpt −−exclude=".*" −q −e ssh \ $LOCAL PATH/* $RSYNC USER@$SERVER URL::$RSYNC DOMAIN/\ $LOCAL PATH/ RETURN=$? fi fi 540 if [ $RETURN != 0 ] then echo "rsync error: $RETURN" exit $RETURN fi if [ $QUIET == 0 ]; then echo "all seems ok"; fi ERROR CODE=$RETURN exit $ERROR CODE fi 550 if [ $CHECKED == "clr" ] then # clean a local $TARGET trace. rm −rf −− $LOCAL PATH/ RETURN=$? if [ $RETURN != 0 ] then echo "rm error: $RETURN" 142 exit $RETURN 560 else if [ $QUIET == 0 ]; then echo "all seems ok"; fi ERROR CODE=$RETURN exit $ERROR CODE fi fi if [ $CHECKED == "exist" ] then check $TARGET 570 if [ $ISTHERE == "yes" ] then if [ $QUIET == 0 ]; then echo "$TARGET exists on $SERVER_URL"; fi exit 0 else if [ $ISTHERE == "no" ] then if [ $QUIET == 0 ]; then echo "$TARGET does not exists on $SERVER_URL"; fi exit 2 else 580 echo "ssh error?" exit 1 fi fi fi if [ $CHECKED == "set" ] then push $FEATURE $CHAR if [ $PUSH RETURN == 0 ] 590 then if [ $QUIET == 0 ]; then echo "all seems ok"; fi exit 0 else echo "Set feature error" exit 1 fi fi if [ $CHECKED == "unset" ] 600 then pop $FEATURE $CHAR if [ $POP RETURN == 0 ] then if [ $QUIET == 0 ]; then echo "all seems ok"; fi exit 0 else echo "Unset feature errorr" exit 1 fi 610 fi if [ $CHECKED == "read" ] then read $FEATURE if [ "$READ_RETURN" != "" ]; then echo "$READ_RETURN"; fi 143 exit 0 fi if [ $CHECKED == "readall" ] then read all $FEATURE if [ "$READ_RETURN" != "" ]; then echo "$READ_RETURN"; fi exit 0 fi 620 fi visualize.sh #!/bin/bash # NOTE: version 0.2 # CHANGES: 0.2: bugfix release # plug-ins timeliner="./branch/trace-timeliner.pl --analyzpath ./scripts"\ # –analyzpath ./scripts = path of workload-analyzer # initialization DOWNLOADER="./scripts/utils/sync-traces.sh" SERVER URL="" TARGET="" KEEP VAR="0" PLUGIN="timeliner" PAR="" # plugin parameters 10 # defaults ERROR CODE=39 # default error code HELP="Usage:\t$0 [-k] [-p plug-in] [-s server] -t trace [-h] [plug-in options]\n Trace visualizer.\n\n 20 -k keep trace after download and visualization.\n\n -p visualizer plug-in. [default timeliner].\n\n -s server\t\tremote traces server.\n -t trace\t\tname of the trace.\n\n 30 -h this help.\n\n Other options will be passed to the plug-in.\n\n Usage examples:\n to visualize a local trace: $0 -t trace [-p timeliner] [plug-in options]\n to visualize a remote trace: $0 -s server -t trace [-p timeliner] [plug-in options]\n to visualize and keep a local copy of a remote trace: $0 -k -s server \ -t trace [-p timeliner] [plug-in options]\n " 40 if [ $# -eq 0 ] # script invoked with no command-line args then 144 echo −e $HELP exit $ERROR CODE # exit and explain usage, if no argument(s) given fi while getopts ":s:t:p:kh" Option # Command arguments: # -s server # -t target 50 # -k keep target, do not erase it from disk # -p visualizer plug-in to use do case $Option in s ) SERVER URL=$OPTARG;; t ) TARGET=$OPTARG;; #TARGET=‘basename $OPTARG‘;; k ) KEEP VAR=1;; f ) PLUGIN=$OPTARG;; h ) echo −e $HELP 60 exit 1;; \?) ;; esac done # copy plugin parameter in $PAR (everything except visualize.sh arguments) STATUS="ok" for i in $* do if [[ "$STATUS" == "ok" ]] 70 then if [[ "$i" == "-s" ]] then STATUS="miss" # to bypass next argument also elif [[ "$i" == "-t" ]] then : # nothing to do to, next argument will be passed to the plugin as is \ (hopefully a directory) elif [[ "$i" == "-p" ]] then 80 STATUS="miss" # to bypass next argument also elif [[ "$i" == "-k" ]] then : # nothing to do else PAR="$PAR $i" fi else # current argument bypassed STATUS="ok" fi 90 done RETURN=0 eval COMMAND=\$$PLUGIN if [[ "$SERVER_URL" == "" ]] && [[ "$TARGET" != "" ]] && \ [[ "$KEEP_VAR" == "0" ]] # local trace then # unzip 145 for j in ‘tree −if $TARGET|egrep "unscaled.gz$"‘ 100 do gzip −d $j done RETURN=$? if [[ $RETURN != 0 ]] then echo "$TARGET DECOMPRESSING ERROR: $RETURN" exit $RETURN else echo "unzip fase ok" 110 # visualize with plug-in $COMMAND $PAR RETURN=$? if [[ $RETURN != 0 ]] then echo "$COMMAND ERROR: $RETURN" exit $RETURN fi # all seems ok echo "plug-in launched" 120 fi elif [[ "$SERVER_URL" != "" ]] && [[ "$TARGET" != "" ]] && \ [[ "$KEEP_VAR" == "0" ]] # remote trace: download, visualize, erase then # download $DOWNLOADER −g −t $TARGET −s $SERVER URL RETURN=$? if [[ $RETURN != 0 ]] then echo "$TARGET DOWNLOADING ERROR: $RETURN" 130 exit $RETURN else # unzip for j in ‘tree −if $TARGET|egrep "unscaled.gz$"‘ do gzip −d $j done RETURN=$? if [[ $RETURN != 0 ]] then 140 echo "$TARGET DECOMPRESSING ERROR: $RETURN" exit $RETURN else echo "unzip fase ok" # visualize with plug-in $COMMAND $PAR RETURN=$? if [[ $RETURN != 0 ]] then echo "$COMMAND ERROR: $RETURN" 150 echo "$TARGET state may be incorrect, please check and remove" exit $RETURN else # all seems ok echo "plug-in launched" # remove trace 146 rm −rI −− $TARGET RETURN=$? if [[ $RETURN != 0 ]] then 160 echo "DELETING $TARGET ERROR: $RETURN" echo "$TARGET state may be incorrect, please check and remove" exit $RETURN else echo "$TARGET deleted as requested" fi fi fi fi elif [[ "$SERVER_URL" != "" ]] && [[ "$TARGET" != "" ]] && \ 170 [[ "$KEEP_VAR" == "1" ]] # remote trace: download, visualize then # download $DOWNLOADER −g −t $TARGET −s $SERVER URL RETURN=$? if [[ $RETURN != 0 ]] then echo "$TARGET DOWNLOADING ERROR: $RETURN" exit $RETURN else 180 # unzip for j in ‘tree −if $TARGET|egrep "unscaled.gz$"‘ do gzip −d $j done RETURN=$? if [[ $RETURN != 0 ]] then echo "$TARGET DECOMPRESSING ERROR: $RETURN" exit $RETURN 190 else echo "unzip fase ok" # visualize with plug-in $COMMAND $PAR RETURN=$? if [[ $RETURN != 0 ]] then echo "$COMMAND ERROR: $RETURN" exit $RETURN fi 200 # all seems ok echo "plug-in launched" fi fi else echo −e "ERROR: bad arguments\n" echo −e $HELP fi 210 147 148 Bibliografia [1] Maui Administrator’s Guide, maui 3.2, may 16 edizione, Maggio 2008. [2] I. Bird, K. Bos, N. Brook, D. Duellmann, C. Eck, I. Fisk, D. Foster, B. Gibbard, M. Girone, C. Grandi, e altri. Lhc computing grid technical design report. [3] F. Cantini, L. Servoli, e M. Mariotti. Studio delle prestazioni di un batch system basato su torque/maui, 2007. [4] J. Dean e S. Ghemawat. Mapreduce: Simplified data processing on large clusters. COMMUNICATIONS OF THE ACM, 51(1):107, 2008. [5] G. Dozsa, M. Farreras, P. Luk, e T. Spelce. xlupc/bluegene class ii submission to the hpc challenge award competition. 2006. [6] H. W. Meuer. The top500 project: Looking back over 15 years of supercomputing experience. Informatik Spektrum, 31:203–222, 2008. [7] E. Moore Gordon. Cramming more components onto integrated circuits. Electronics, 38(8):114–117, 1965. [8] R. Poli, W. B. Langdon, e N. F. McPhee. A Field Guide to Genetic Programming. Lulu Press, 2008. 149