
      ==========================================================
			Virtuelles Unix Labor
			     - Design -

		   	    Hubert Feyrer
      ==========================================================

0. Konventionen

I:X:Y:Z:	Interaktion X zu Y, laufende Nummer Z
DB:X:Y:		Datenbank, Tabelle X Spalte Y


1. Komponenten


                        User

                          |
                          |

v|                User Management
 |                /       |      \
u|               /        |       \
 |              /         |        \
l|   Kurs Engine --- Datenbank ---- Scheduler
 |        |        /           \        |
a|        |       /             \       |
 |        |      /               \      |
b|      Firewall                   Deployment


 1. User 
 2. User Management    (UM)
 3. Kursengine         (KE) 
 4. Scheduler          (SC)
 5. Datenbank          (DB)
 6. Firewall           (FW)
 7. Deployment         (DE)


2. Aufgaben der Komponenten

2.1 User

   Student, der mittels Web-Browser, telnet/ssh und ftp übt.

   Neben Scheduler einzige und gleichzeitig wichtigste aktive
   "Komponente", liefert Steuerinformation, interagiert mit
   User-Management und Kursengine sowie natuerlich den
   Uebungsrechnern.  


2.2 User Management & Buchungssystem

   Web-Frontend und Zugang zum Übungssystem, realisiert durch
   dynamische PHP-Scripte sowie statische HTML-Seiten.  

   Benutzerverwaltung, Anmeldung fuer Kurse, Zugang zur Kursengine,
   Buchen von Uebungen, Deployment-Auftrag an Scheduler, Firewall
   freischalten vor Uebung

   Aufgaben:

    - Neuanmeldung:
       * Daten uebernehmen: Name, EMail-Adresse, Passwort
       * Daten in Datenbank ablegen I:UM:DB:1
         [DB:benutzer:user_id, DB:benutzer:vorname, DB:benutzer:nachname,
          DB:benutzer:email, DB:benutzer:passwort,
          DB:benutzer:freischalt_secret]
       * Bestaetigung per Mail versenden, mit best. Link zum
         Bestaetigen; enthaelt eindeutiges Passwort zur Authentifizierung
         der Bestaetigung.
	 [DB:benutzer:freischalt_secret]
       * User ist sofort eingeloggt, aber nicht freigeschaltet
       * Wenn User auf Link klickt(...) ist der Account offiziell
         freigeschaltet I:UM:DB:2
         [DB:benutzer:freischalt_secret="bestaetigt"]
    - Login:
       * Mit Login & Passwort anmelden
         [DB:benutzer:email, DB:benutzer:passwort]
       * Wenn Account noch nicht bestaetigt, Meldung ausgeben und zum
         freischalten auffordern I:UM:DB:2 [XXX DB:benutzer:Freigeschaltet=ja]
       * Pruefen ob "normaler" User, Lehrer/Professor oder Admin ist
         [DB:benutzer:typ]
       * Auswahl, was tun:
          . Logout
          . Übung buchen (User/Admin only)
          . Übung einstellen (Prof/Admin only)
          . Üben (User only)
          . Auswertungen (User/Admin only)
          . Administration der Benutzerdaten (User/Admin)
    - Aufgabe buchen:
       * Aufgabe aussuchen I:UM:DB:3
         Gesamtdauer := Vorlauf (Deployment) + übungsdauer
                      + Nachlauf (Checks)
         [DB:uebung:uebung_id, DB:uebung:vorlauf,
          DB:uebung:dauer, DB:uebung:nachlauf]
       * Datum & Uhrzeit auswaehlen (Reihenfolge?!)
       * Abschicken
       * Ueberpruefen ob Labor zum Zeitpunkt frei
         [DB:buchungen:uebung_id, DB:buchungen:startzeit,
          DB:uebung:vorlauf, DB:uebung:dauer,
          DB:uebung:nachlauf] 
       * Wenn nein, zurueck.
       * Uebung in DB buchen I:UM:DB:4
         [DB:buchungen:freigegeben(user_id,uebung_id,startzeit) = "nein"]
       * at(1)-Job aufsetzen, der Labor vorbereitet (-> Deployment)
         "at gewuenschteZeit-Vorlauf setup 
          DB:buchungen:buchungs_id" I:UM:SC:1 
    - Übung einstellen:
      (TODO)
    - Üben
       * Daten aus DB: User, gebuchte Kurse, wann+von wem gebucht
         I:UM:DB:5
         [DB:buchungen:user_id==DB:benutzer:user_id,
          DB:buchungen:uebung_id, DB:buchungen:startzeit]
       * Wen Übung nicht gebucht: irgendwas ausgeben :)
       * Wenn Übung nicht vorbereitet:
         Anzeigen von gebuchten Termin(en)
	 [DB:buchungen:freigegeben]
       * Uebergeben an Kursengine I:UM:KE:1
         [DB:uebung:uebung_id, DB:benutzer:user_id, DB:buchungen:startzeit]
    - Auswertungen
       * Daten aus Datenbank auslesen I:UM:DB:6
         [DB:buchungen:user_id==DB:benutzer:user_id]
       * Auflistung der Gesamt-Ergebnisse aller absolvierten Übungen
       * Details zu jeder Übung - Score pro Check
         [DB:ergebnis_checks:erfolg, ...]
       * Diverse statistische Auswertungen (Durschnitt, Zeit/Dauer,
         Verteilung pro Teilaufgabe, ...)
         [DB:buchungen:startzeit, DB:buchungen:endzeit, ...]
   

2.3 Kursengine

   Web-Frontend das die Übung steuert. Auf dem Übungs-Server des
   Virtuellen Unix Labors liegen HTML-Dateien, PHP-Scripten und
   IP-Filter, auf den einzelnen Uebungsrechnern sorgen diverse
   Daemonen (sshd, telnetd, ftpd) für den Ablauf der Übungen. 

   Speichern von Übungen (Aufgabentext + Image), Präsentation von
   Übungsaufgaben, ggf. Unterstützung/Erklaerung,
   ggf. weiterfuehrender Text & Hintergrund, Anstossen der Auswertung,
   abspeichern der Ergebnisse in Datenbank

   Aufgaben:

    - Firewall aufmachen (vor Uebung)
       * IP-Nummer aus CGI uebernehmen (-> I:UM:KE:1)
       * Formular zum Anpassen der freizuschaltenden IP anbieten:
         IP-Nummer (Default: $HTTP_X_FORWARDED_FOR wenn gesetzt,
         ansonsten $REMOTE_ADDR, ggf. Anzahl Hostbits a'la CIDR zum
         Freischalten von Netzen (Default: /32 == 1 Host)
       * Firewall-Modul mit IP/hostbits aufrufen I:KE:FW:1
       * Daten zum entfernen in Datenbank hinterlegen I:KE:DB:1
         [DB:laufendeUebung:ip(Username, Startzeit)]
    - Firewall schliessen (filtern nach Uebungsende anschalten)
       * Firewall-Daten der jeweiligen Uebung aus der Datenbank
         auslesen(?) I:KE:DB:2  (-> I:KE:DB_1)
         [DB:laufendeUebung:ip(Username, Startzeit)]
       * Firewall-Regeln entsprechend entfernen I:KE:FW:2
       * Ggf. Sanity Check: wenn keine Uebungs mehr laeuft sollten nun
         keine FW-Regeln mehr aktiv sein
    - Aufgabentext praesentieren
       * URL/Pfad aus DB auslesen, abhaengig von d. gebuchten Uebung
         I:KE:DB:3
         [DB:uebung:text(uebung_id)]
       * Uebungstext anzeigen
    - Ggf. Weiterfuehrende Informationen bereitstellen
       * Entweder in Kurs-DB oder im Aufgabentext verlinkt  I:KE:DB:4
         [DB:uebung:mehr_info(uebung_id)]
    - Uebungsende ueberwachen (per User oder Timeout)
       * HTML-Formular mit
          + Refresh Meta-Tag (Redirect auf "Fertig"-Seite; Problem:
            Schutz gegen Timer-Restart durch Reload?) und
            Refresh := DB:buchungen:startzeit + DB:uebung:dauer - now()
          + "Fertig"-Button anbieten (auf Aufgabentext-Seite?)
       * Verweis auf freiwilliges Ende oder Timeout mit entsprechendem
         Text quittieren
       * Benoetigte Uebungszeit hinterlegen (Statistik!): Script
         "uebung_ende"  I:KE:DB:5
         [DB:buchungen:endzeit(user_id, uebung_id, startzeit) = now()]
       * Auswertung starten: Script "uebung_ende"
    - Auswertung (via "uebung_ende")
       * Uebung als "wird ausgewertet" markieren
         [DB:buchungen:freigegeben(user_id,uebung_id,startzeit):=
          auswertung-laeuft]  
       * Pfade der Check-Scripten & Rechner fuer
         Check-Scripten aus DB auslesen  I:KE:DB:6
         [DB:uebung_checks:*]
       * Interpreter aus 1. Zeile des Check-Scripts ermitteln
       * Absetzen der Check-Scripten:
         ssh $rechner $interpreter <$check_script
         (Optimierung wenn rechner == localhost, f. Tests "von
         aussen", z.B. vom Supervisor-Rechner aus, ob Dienst laeuft)
       * Rueckgabewert (OK/nicht OK) in Datenbank eintragen I:KE:DB:7
         [DB:ergebnis_checks(Username,uebung_id,Startzeit,Script,
          Rechner) := ok/nicht ok]      
       * Uebung als "niehr mehr freigegeben" markieren
         [DB:buchungen:freigegeben(user_id,uebung_id,startzeit):=
          nicht-mehr]  
    - Zurueck zur Übersicht im User Management I:KE:UM:1

2.4 Scheduler

   Aktive Komponente, mit at(1) als Backend; User Management stoesst
   Jobs an (Uebung vorbereiten, nicht bestaetigte Anmeldungen
   loeschen, ...); 

   Aufgaben:
    - Deployment starten 
       * Eintrag "setup" at(1) Job durch User-Management beim Buchen
         der Kurse (-> I:UM:SC:1)  I:SC:DE:1
    - Loeschen unbestaetigter Anmeldungen
       * Per cron jede Nacht (Stunde, ...) gucken welche Accounts
         nicht bestaetigt sind, und loeschen (-> I:UM:DB:1)
         I:SC:DB:1, I:SC:SC_1
         [DB:benutzer:freischalt_secret, DB:benutzer:anmeldedatum]
    - Loeschen von Firewall_Regeln fuer (abgebrochene/nicht
      angetretene?) übungen mit Script "uebung_ende" (-> I:DE:SC:1)
      I:SC:FW:1 


2.5 Datenbank

   Speichert Daten ueber Benutzer, Uebungen, gebuchte Uebungen;
   Vorbereitete Übungen werden als solches gekennzeichnet (fuer 
   naechstes Login des users: "Sie koennen jetzt ueben")

   Aufgaben:
    - Daten speichern (naja) (-> I:*:DB:*)

   Siehe Datenbank-Struktur unten fuer weitere Erklaerungen


2.6 Firewall

   Wird beim Login freigeschaltet, damit nur 1 User (Rechner?
   Subnetz?) Zugang zu den Uebungsrechnern hat. Implementierung
   über IPfilter / Port redirection. Daten werden von der
   Kursengine beim Start des Kurses abgefragt und scharf geschaltet.
   Nach Kurs-Ende werden die Daten wieder entfernt.

   Aufgaben:
    - Freizuschaltende IP-Nummer/Netzbits uebernehmen (-> I:KE:FW:1)
    - Firewall aufmachen, Interaktion mit IP Filter
       * Port-Redirection f. Uebungsrechner X, Ports 2[123]
         (incoming) => 2X02[123] - IPFilter: RDR ...
       * Admin Issues:
          - Wie Rules dynamisch in IPFilter laden?
          - Port-Freischaltung am RZ/FH-Firewall 
    - Firewall zumachen, Interaktion mit IP Filter (-> I:KE:FW:2,
      I:SC:FW:1) 


2.7 Deployment

   Scheduler stoesst Deployment an; Rechner werden automatisch ("ssh
   machine reboot"? kein Plan... :/) gebootet, per Default Netboot
   mit Bootloader fuer wahlweise Netboot (-> Deployment/Image restore)
   oder boot from local disk (-> Uebung). Nach erfolgreichem
   Deployment hinterlegen der Information ("Uebung aufgesetzt") in
   Datenbank - von Kursengine/User Management ausgewertet.

   Aufgaben:
    - Script "deploy DB:uebung:uebung_id" (-> I:SC:DE:1,
      vgl. I:UM:SC:1) 
    - Uebung als "in Vorbereitung" markieren
      [DB:buchungen:freigegeben(user_id,uebung_id,startzeit):=
       deployment-in-progress]  
    - Daten aus DB auslesen: welches Image auf welchen Rechner (fuer
      den jeweiligen Kurs)  I:DE:DB:1 (XXX Scheduling welches Image auf
      welche Maschine fuer welche Aufgabe? Ggf. noch eigene
      Vermittlungsschicht dafuer, vorerst aber ausklammern)

      "deploy1 DB:uebung_setup:rechner DB:uebung_setup:image"
      (Mehr Details zum Deployment siehe "deployment" Text)

      [DB:uebung_setup:uebung_id==DB:uebung:uebung_id,
       DB:uebung_setup:rechner, DB:uebung_setup:image] 
    - Eintrag "Deployment fertig" in DB hinterlegen  I:DE:DB:2
      [DB:buchungen:freigegeben(user_id,uebung_id,startzeit)=ja]
    - at-Job "uebung_ende" zum entfernen der Regeln bei abgebrochenen
      Übungen I:DE:SC:1
    - Ggf. User via Mail benachrichtigen


3. Datenbank-Struktur:

  Tabelle "benutzer":
    *user_id				serial
     login                              char(80)
     vorname				char(50)
     nachname				char(50)
     matrikel_nr			char(50)
     email 				char(80)
     passwort 				char(15)
     freischalt_secret / "bestaetigt"	char(80)
     anmeldedatum 			datum_t
     typ (admin, lehrer, prof)		char(80)

  Tabelle "uebungen":
    *uebung_id				char(40)
     bezeichnung			char(150)
     nur_fuer				char(40)
     vorlauf				time
     dauer				time
     nachlauf				time
     wiederholbar?			bool
     text (URL)				char(150)
     mehr_info (URL)			char(150)

  Tabelle "uebung_checks":
    *check_id				serial
     uebung_id				like DB:uebung:uebung_id
     script (Pfad?)			char(150)
     parameter				char(300)
     rechner (localhost, vulab1, ...)	like DB:uebung_setup:rechner
     bezeichnung (f. Auswertung)	char(150)

  Tabelle "buchungen":
    *buchungs_id			serial
    *user_id				like DB:benutzer:user_id
    *uebung_id				like DB:uebung:uebung_id
    *startzeit				time
     datum				date (startdatum)
     freigegeben?			nein/deployment-in-progress/ja/auswertung-laeuft/nicht-mehr
     endzeit				time
     ip ("a.b.c.d/x")			char(20)
     at_id				int (deployment job)
     at_id_end				int (end-of-uebung job)

  Tabelle "ergebnis_checks":
    *buchungs_id			like DB:buchungen:buchungs_id
    *check_id				like DB:uebung_checks:check_id
     erfolg				bool

  Tabelle "uebung_setup":
    *uebung_id				like DB:uebung:uebung_id
    *rechner				like DB:uebungs_checks:rechner
    *image				char(150)


4. Schnittstellendefinition:

4.1 User

http, ssh, ftp


4.2 User Management

   Ruft auf (via):
    * Kursengine: 	CGI
    * Scheduler:	at(1), cron(8)
    * Datenbank:	PHP/pgsql

   Wird aufgerufen von (via):
    * Kursengine:
       - I:KE:UM:1:	Rueckkehr nach Uebung (PHP/HTML)

4.3 Kursengine

   Ruft auf (via):
    * Datenbank:	PHP/pgsql
    * Firewall:		CGI

   Wird aufgerufen von (via):
    * User-Management:
       - I:UM:KE:1:	Uebungsbeginn (PHP/HTML)

4.4 Scheduler

   Ruft auf (via):
    * Datenbank:	sh/perl/PHP/pgsql
    * Deployment:	sh/perl

   Wird aufgerufen von (via):
    * User-Management:
       - I:UM:SC:1:	Übung buchen (at(1))
    * Scheduler:
       - I:SC:SC:1:	Periodischer Account-Purge

4.5 Datenbank

   Ruft auf (via):
   (nichts, keine aktive oder aktivierte Komponente, rein passiver
    Datenspeicher)

   Wird aufgerufen von (via):
    * User-Management:
       - I:UM:DB:1:	User neu anmelden (PHP/pgsql)
       - I:UM:DB:2: 	Anmeldung bestätigen (2x) (PHP/pgsql)
       - I:UM:DB:3:	Auslesen verfügbarer Kurse+Übungszeiten
                        (PHP/pgsql)
       - I:UM:DB:4:	Übung buchen (PHP/pgsql)
       - I:UM:DB:5:	Abfrage ob Übung gebucht (PHP/pgsql)
       - I:UM:DB:6:	Auswertung der von Checks eingegebenen Daten
                        (PHP/pgsql)
    * Kursengine:
       - I:KE:DB:1:	Firewall-Daten merken (PHP/pgsql)
       - I:KE:DB:2:	Firewall-Daten loeschen (PHP/pgsql)
       - I:KE:DB:3:	Übungstext anzeigen (PHP/pgsql)
       - I:KE:DB:4:	Hintergrund-Infos anzeigen (PHP/pgsql)
       - I:KE:DB:5:	Übungsdauer speichern (PHP/pgsql)
       - I:KE:DB:6:	Daten zu Checks auslesen (perl/CGI/sh)
       - I:KE:DB:7:	Check-Ergebnisse speichern (sh/perl)
    * Scheduler:
       - I:SC:DB:1:	Unbestätigte Accounts löschen (sh/perl)
    * Deployment:
       - I:DE:DB:1:	Infos ueber vorzubereitendes Deployment (sh)
       - I:DE:DB:2:	Deployment fertig, Übung kann beginnen (sh)

4.6 Firewall

   Ruft auf (via):
    * Datenbank:	perl/PHP/sh/pgsql

   Wird aufgerufen von (via):
    * Kursengine:
       - I:KE:FW:1:	Öffnen vor Übung (CGI/perl/sh)
       - I:KE:FW:2:	Schließen nach Übung (sh/perl)

4.7 Deployment

   Ruft auf (via):
    * Datenbank:	perl/sh/pgsql

   Wird aufgerufen von (via):
    * Scheduler:
       - I:SC:DE:1:	Übung vorbereiten (sh)


5. Übungen - Komponenten & Definition

Aufbau und Erstellung von Übungen ist im Dokument
"uebungserstellung.txt" beschrieben.


6. Diverses

6.1 Erweitern des Design mit Tutorieller Komponente fuer Benutzer (TK_B)

                                User
        
                                  |
                                  |
                                             
        v|                User Management
         |                /       |      \ 
        u|               /        |       \
         |              /         |        \
        l|   Kurs Engine --- Datenbank ---- Scheduler
         |        |      \ /      |    \        |
        a|        |       /\      |     \       |
         |        |      /   Tutorielle  \      |
        b|      Firewall     Komponente    Deployment
        
        
         1. User 
         2. User Management       (UM)
         3. Kursengine            (KE) 
         4. Scheduler             (SC)
         5. Datenbank             (DB)
         6. Firewall              (FW)
         7. Deployment            (DE)
         8. Tutorielle Komponente
            fuer Benutzer         (TK_B)


6.2 Admin-Uebung zum Image-Erzeugen/Editieren

Idee: Definieren einer Uebung, deren Check-Scripten ein oder mehrere
      Images neu erzeugen.

Anforderungen:
 - Darf nicht von Normaluser gebucht werden (kann Admin Ubung
   buchen?)
 - Zu aenderndes Image wird fuer die Uebung vorselektiert
 - Benennung des neuen Image-Dateinamens via Parameter
 - Check sorgen fuer Image-Erzeugung:
	admin-check-makeimage RECHNER=xx IMGNAME=$neuesimage 
 - Beachtung genuegend grosser Nachlaufzeit!

Checks: admin-check-clearharddisk admin-check-makeimage


6.3 Umorganisieren für bessere Datenstrukturen

Die urspruenglich Entworfene Struktur mit Speichern der Übung in der
Datenbank (Tabellen DB:uebungs_checks, DB:ergebnis_checks) hat das
Problem, dass die Überprüfung relativ weit von der Aufgabenstellung
entfernt ist.

Alternative Varianten:
1. Übungstext -----> DB (check_nr) -----> Check-Script
2. Übungstext --------------------------> Check-Script
3. Übungstext 

zu 1.: Stand SS04 (7. Febl. 2004).

   Grund für Design: ursprünglich war keine (enge) Vermischung
   zwischen Aufgabentext und Überprüfung geplant:

	Übungstext		|	DB->Scripts(-ergebnis)
	(Anzeige waehrend	| 	(Anzeige nach
	 Übung)			| 	 Übung)

   Die Vermischung (2.) geschah, um bei der Auswertung (Anzeige der
   Übung) klarer zu machen, welche TeilÜbungen genau geprüft wird, und
   mit welchem Erfolg. Nachteil ist, dass Uebungstext und DB getrennt
   gewartet werden muessen.


   Abhilfe: Script 'uebung2db' liest Kommentare aus Uebungstext, und
   fuegt sie in die DB ein, so daß die manuelle Pflege entfällt, ohne
   die DB-Struktur ändern zu muessen. So koennen die Ergebnis-Checks
   an der selben Stelle wie der Aufgabentext gespeichert werden,
   zusaetzlich aber darueberhinaus mit Hilfe des DB-Frontends
   manipuliert werden, falls Bedarf besteht. 

zu 2.: Script-Aufruf, Parameter, Rechner & Bezeichnung f. Auswertung
       direkt bei Aufgabenstellung speichern

   Aufwand zur Realisierung:
    * Tabbel DB:uebungs_checks (+Erstell-Masken) loeschen
    * Tabelle DB:ergebnis_checks erhaelt als Ersatz für die Spalte
      check_id entweder das Tupel (rechner, script, parameter), oder
      die Bezeichnung (sollte innerhalb der Übung einmalig sein) oder
      eine Aufgabenweite laufende Nummer.
    * 3. Seite (3/3) bei Übungserstellung entfällt

zu 3.: Check-Code direkt in Aufgabentext eingebettet

   Benötigt ID zum Mapping der Teilübungen -> Ergebnis in
   DB:ergebnis_checks (vgl. 3er-Tupel oder Bezeichnung etc. oben)

   Vorteil dieses Ansatzes: Einfachere Initialisierung der
   Datenstrukturen (In-Place, nicht in DB), vgl. [Spinellis:2001,
   "4.7. Data stucture representation", S. 96]

6.4 Komponenten-Graph generieren

Der unter #1 aufgelistete Graph mit den Komponenten untereinander kann
mit den Angaben aus Kapitel 4 (Schnittstellendefinition) automatisch
erzeugt werden:

( echo "digraph vulab {"
  grep '.- I:..:..' design
  echo "}"
) | sed 's/.*I:\(..\):\(..\):.*/\1 -> \2/' > design_interactions.dot
dot -Tgif design_interactions.dot >design_interactions0.gif

Wenn man in der erzeugten Datei design_interactions.dot die erste
Zeile als letztes nimmt und so einfach den Graph etwas umstrukturiert
erhält man einen Graph der dem in Kapitel 1 schon recht nahe kommt,
siehe design_interactions0.gif.

--
$Id: design,v 1.48 2012-12-21 23:32:17 feyrer Exp $
