# Makefile

## Voraussetzungen

> Server-Admin kontaktieren

- Server: Public SSH-Key
- Client: Private SSH-Key
- Client: SSH-Config konfiguriert

<details>
<summary>Details anzeigen</summary>

Dieses Projekt sieht eine Kommunikation zwischen Client und Server mittels _SSH_ vor.
Aufgrund der höheren Sicherheit und einfacheren Bedienbarkeit sind _SSH-Keys_ für den Authentifizierungsprozess zu verwenden.

Das Schlüsselpaar aus _Private-Key_ und _Public-Key_ muss zuvor gemeinsam mit dem Server-Admin eingerichtet werden.
Je nach Server-Konfiguration kann es erforderlich sein, die _SSH-Config_ anzupassen.

</details>

## Empfehlung

Die Verwendung des Terminal-Multiplexers _[Byobu](https://www.byobu.org/)_ wird empfohlen.

```bash
# Debian / Ubuntu
sudo apt install byobu
```

Download & Installation: [https://www.byobu.org/downloads](https://www.byobu.org/downloads)

<details>
<summary>Details anzeigen</summary>

Vorteile von _Byobu_:

- **Sitzungen beibehalten**: Byobu ermöglicht das Starten von Sitzungen, die im Hintergrund weiterlaufen, selbst wenn das Terminal geschlossen oder die Verbindung getrennt wird.
- **Einfache Wiederherstellung**: Es ist jederzeit möglich, zu einer bestehenden Sitzung zurückzukehren, um den Status von Prozessen zu überprüfen oder die Arbeit fortzusetzen.

</details>

## Installation

1. Makefile [herunterladen](./basic.mk)
2. Makefile in Projektordner speichern
   - Projektstruktur ([Beispiel](#projektstruktur))
3. Makefile in `Makefile` oder `makefile` umbenennen (ohne `.mk`)
4. Makefile [konfigurieren](#konfiguration)
5. `make install`

<details>
<summary>Details anzeigen</summary>

Die _Makefile_ dient in diesem Projekt zur Vereinfachung von Prozessen, die sonst in vielen Einzelschritten manuell über die bash-Konsole durchgeführt werden müssten.

Im Abschnitt [Projektstruktur](#projektstruktur) ist ein beispielhafter Aufbau eines Projekts skizziert.

Hilfen zur Konfiguration der _Makefile_ und Bedeutung der verwendeten Variablen sind im Abschnitt [Konfiguration](#konfiguration) zu finden.

</details>

## Bedienung

> siehe [Befehle](#befehle)

1. `make upload` Projektdateien hochladen
2. `make run` Python-Script ausführen
3. `make log` Fortschritt überprüfen
4. `make download` Output-Dateien herunterladen

<details>
<summary>Details anzeigen</summary>

Die Makefile ist so konfiguriert, dass sie mit Erhalt einfacher Befehlen komplexe oder aufwendige Prozesse ausführt.

Im Abschnitt [Befehle](#befehle) sind diese sogenannten _Targets_ und deren Aufgabe näher beschrieben.

</details>

## Projektstruktur

> Beispiel

```txt
/home/max/akis/my_project/
├── data
│   ├── one.pkl
│   ├── three.pkl
│   └── two.pkl
├── main.py
├── makefile
├── output
│   └── empty
└── scripts
    ├── script-1.py
    ├── script-2.py
    └── script-3.py
```

<details>
<summary>Details anzeigen</summary>

Diese Projektstruktur ist nur ein Beispiel.
Sie soll jedoch verdeutlichen, dass auch verschachtelte Projekte mit der _Makefile_ bedienbar sind.

Wichtig ist jedoch, dass in der _Makefile_ das `MAIN_SCRIPT` konfiguriert ist.
Dieses Python-Script wird durch die _Makefile_ ausgeführt; hier: `main.py`.

Wie das `MAIN_SCRIPT` aufgebaut ist, ist dabei nicht relevant.
Es könnte ein alleinstehendes Script sein, welches ohne andere Python-Scripts auskommt.
Oder es führt andere Python-Scripts aus; wie im obigen Beispiel `scripts/script-1.py`, `scripts/script-2.py` und `scripts/script-3.py`.

Importierte Datasets liegen oft nicht innerhalb des Projektordners, sondern werden von extern importiert.
Es ist darauf zu achten, dass auch der Server Zugriff auf den Pfad der Datasets hat.
Symbolische Links können hier hilfreich sein.

Output-Dateien, die nach Abschluss des Scripts wieder auf den Client heruntergeladen werden sollen, können mit `REMOTE_OUTPUT_FILES` definiert werden.
Sie werden durch den Aufruf von `make download` im zuvor definierten Ordner `LOCAL_OUTPUT_DIR` abgespeichert.

</details>

## Konfiguration

### Server

| Variable      | Verwendung       | Beispiel           |
| :------------ | :--------------- | :----------------- |
| `SERVER_USER` | Server: Username | `mmustermann`      |
| `SERVER_HOST` | Server: Hostname | `hyrican-1-extern` |

<details>
<summary>Details anzeigen</summary>

#### `SERVER_USER`

> Wird vom Server-Admin vergeben.

#### `SERVER_HOSTNAME`

> Kann in verschiedenen Formaten vorliegen:

| Format               | Beschreibung                 | Beispiel             |
| :------------------- | :--------------------------- | :------------------- |
| IP-Address (private) | numerisch; lokales Netz      | `192.168.1.100`      |
| IP-Address (public)  | numerisch; öffentliches Netz | `203.0.113.1`        |
| FQDN                 | vollständiger Domänenname    | `server.example.com` |
| Hostname             | einfacher Name ohne Domäne   | `hyrican-1`          |
| Alias                | alternativer Name; Verweis   | `hyrican-1-extern`   |

In der Regel übermittelt der Server-Admin dem neu freigeschalteten User eine SSH-Config.
Die Verwendung der dort definierten Aliases wird empfohlen.

</details>

### Python Environment

| Variable            | Verwendung                       | Beispiel                           |
| :------------------ | :------------------------------- | :--------------------------------- |
| `SERVER_CONDA_PATH` | Server: Conda-Installations-Pfad | `/opt/conda`                       |
| `SERVER_CONDA_ENV`  | Server: Conda-Environment-Pfad   | `~/envs/ml`                        |
| `PYTHON_VERSION`    | Python: Gewünschte Version       | `3.10`                             |
| `PYTHON_PACKAGES`   | Python: Benötigte Pakete         | `tensorflow-cpu matplotlib optuna` |

<details>
<summary>Details anzeigen</summary>

#### `SERVER_CONDA_PATH`

> Definiert dem Pfad der _Conda_-Installation auf dem Server.

Auf den Servern `server-*`, `hyrican-*` und `nbb-*` ist _Conda_ unter `/opt/conda` installiert.

#### `SERVER_CONDA_ENV`

> Definiert den Pfad des _Conda-Environments_.

Die Erstellung neuer _Conda Environments_ ist ohne Admin-Rechte global nicht möglich.
Deshalb müssen sie lokal erstellt und installiert werden.
Die vorkonfigurierte _Makefile_ schlägt den Ordner `~/envs` im Home-Verzeichnis vor.

**WICHTIG:** Möchte man sich manuell auf einem der Server anmelden und bspw. Pakete installieren, muss das _Conda Environment_ aktiviert werden:

```bash
conda activate ~/envs/ml
```

#### `PYTHON_VERSION`

> Nach Bedarf konfigurierbar

#### `PYTHON_PACKAGES`

> Nach Bedarf konfigurierbar.

Vorkonfiguriert ist das `ml`-Environment mit seinen Packages, wie es in _[install-python-tensorflow-gym](https://gitlab.cvh-server.de/ckaufmann/install-python-tensorflow-gym)_ vorschlägt.
Zusätzlich benötigte Pakete müssen – mit Leerzeichen getrennt – an `PYTHON_PACKAGES` angehängt werden.

</details>

### Project Paths

| Variable             | Verwendung                 | Beispiel            |
| :------------------- | :------------------------- | :------------------ |
| `LOCAL_PROJECT_DIR`  | Client: Projektordner [^1] | `.`                 |
| `REMOTE_PROJECT_DIR` | Server: Projektordner [^2] | `~/akis/my_project` |

<details>
<summary>Details anzeigen</summary>

#### `LOCAL_PROJECT_DIR`

> Definiert den Pfad des Projektordners auf dem **lokalen Rechner**.

Er wird relativ zur _Makefile_ angegeben.
Liegt die _Makefile_ also direkt im Projektordner, ist `LOCAL_PROJECT_DIR := .`.

#### `REMOTE_PROJECT_DIR`

> Definiert den Pfad des Projektordners auf dem **Server**.

Er wird absolut oder relativ zum Home-Verzeichnis angegeben.
Also bspw. `~/akis/my_project` oder `/home/mmustermann/akis/my_project`.

</details>

### Project Files

| Variable      | Verwendung                        | Beispiel       |
| :------------ | :-------------------------------- | :------------- |
| `MAIN_SCRIPT` | Auszuführendes Python-Script [^1] | `main.py`      |
| `LOG_FILE`    | Log-Datei (optional) [^1]         | `progress.log` |

<details>
<summary>Details anzeigen</summary>

#### `MAIN_SCRIPT`

> Definiert das _Python_-Script, welches später durch die _Makefile_ auf dem Server ausgeführt wird.

Sollte es notwendig sein, dass mehrere Scripts bspw. nacheinander ausgeführt werden, ist es empfehlenswert eine `main.py` zu erstellen.
Dieses `main.py` kann dann alle anderen Scripts ausführen.

#### `LOG_FILE`

> Definiert die Datei, in der das _Python_-Script einen Log schreib.

Logs beinhalten meist Statusmeldungen über den Programmablauf und geben Aufschluss über Fortschritt, Fehler und Erfolg.
Die Verwendung einer Log-File ist optional.
Ist sie vorhanden, kann sie mit `make log` angesehen werden; neue Einträge werden "live" aktualisiert und angezeigt.

</details>

### File Transfer

| Variable                | Verwendung                    | Beispiel                     |
| :---------------------- | :---------------------------- | :--------------------------- |
| `LOCAL_UPLOAD_FILES`    | Client: Upload-Dateien [^1]   | `main.py scripts data/*.pkl` |
| `LOCAL_DOWNLOAD_DIR`    | Client: Download-Ordner [^1]  | `output`                     |
| `REMOTE_DOWNLOAD_FILES` | Server: Download-Dateien [^1] | `output/*.svg output/*.pkl`  |

<details>
<summary>Details anzeigen</summary>

#### `LOCAL_UPLOAD_FILES`

> Definiert – relativ zum Projektordner –, welche Dateien auf den Server hochgeladen werden sollen.

Dazu ein paar Beispiele:

| `LOCAL_UPLOAD_FILES`   | Bedeutung                                       |
| :--------------------- | :---------------------------------------------- |
| `.`                    | gesamter Projektordner                          |
| `main.py scripts data` | Main-Script, Scripts-Ordner, Data-Ordner        |
| `main.py data/*.pkl`   | Main-Script; alle Pickle-Dateien im Data-Ordner |

#### `LOCAL_DOWNLOAD_DIR`

> Definiert – relativ zum Projektordner – den Pfad, in dem vom Server heruntergeladene Output-Dateien gespeichert werden sollen.

Dieser kann dem selben Ordner entsprechen, wie er auf dem Server gespiegelt ist.
Er kann jedoch auch ein anderer sein und bspw. `downloads` heißen.

#### `REMOTE_DOWNLOAD_FILES`

> Definiert – relativ zum Projektordner –, welche Output-Dateien vom Server heruntergeladen werden sollen.

Es können sowohl ganze Ordner, als auch einzelne Dateien ausgewählt werden.
Dazu ein paar Beispiele:

| `REMOTE_DOWNLOAD_FILES`                     | Bedeutung                                                                       |
| :------------------------------------------ | :------------------------------------------------------------------------------ |
| `outputs`                                   | alle Dateien im Output-Ordner                                                   |
| `outputs/*.csv outputs/*.pkl outputs/*.svg` | alle CSV-, PKL- und SVG-Dateien des Output-Orderns                              |
| `outputs img/*.png progress.log             | alle Dateien im Output-Ordner; alle PNG-Dateien im Image-Ordner; eine Log-Datei |

</details>

### Misc

| Variable              | Verwendung                         | Beispiel |
| :-------------------- | :--------------------------------- | :------- |
| `COPY_LINKS_AS_FILES` | Server: Kopiert Symlinks als Datei | `false`  |

<details>
<summary>Details anzeigen</summary>

#### `COPY_LINKS_AS_FILES`

> Kopiert symbolische Links als Datei

Wenn in den Projektordnern symbolische Links ("symlinks", "symbolic links") liegen, werden diese von der _Makefile_ **nicht wie Dateien** behandelt.
Sie werden unverändert als _Symlinks_ auf den Server kopiert.

In manchen Anwendungsfällen ist es jedoch gewollt, dass die Verlinkung aufgelöst und die Links als Dateien kopiert werden.
Für diesen Fall muss `COPY_LINKS_AS_FILES` auf `true` gesetzt werden.

</details>

[^1]: Relativer Pfad zum Projektordner
[^2]: Absoluter Pfad

## Befehle

> `make <target>`

| Target     | Aktion                                    |       Ort       |
| :--------- | :---------------------------------------- | :-------------: |
| `install`  | Installiert Python Environment und Pakete |     Server      |
| `upload`   | Läd veränderte Projektdateien hoch        | Client → Server |
| `run`      | Führt Python Script aus                   |     Server      |
| `kill`     | Bricht Python Script ab                   |     Server      |
| `log`      | Zeigt Log-Datei (optional) an             |     Client      |
| `download` | Läd Output-Dateien herunter               | Server → Client |
| `delete`   | Löscht Projektdateien                     |     Server      |

---

## TODOs

- [x] Bedienung über _Byobu_
- [ ] Datasets über NFS
- [ ] Benachrichtigung bei Abbruch/Erfolg
  - [ ] Slack
  - [ ] E-Mail
  - [ ] Matrix
- [ ] parallele Ausführung mehrerer Scripts
- [ ] docs: get local python packages; append to `PYTHON_PACKAGES`
- [ ] ggf. mehrere Log-Files

---

## Fragen & Anregungen

Fragen, Unklarheiten, Anregungen und Wünsche können gerne über folgende Kommunikationswege an mich herangetragen werden:

- [Slack](https://cvhai.slack.com/team/U084BA4GQ8M)
- [E-Mail](maximilian.melchert@stud.hs-bochum.de)

Es ist durchaus im Sinne des Projekts, auch individualisierte _Makefiles_ für einzelne Projekte anzupassen und umzuschreiben.
Die hier beschriebene _Makefile_ ist nur die Basis, auf der zukünftige Maßanfertigungen aufbauen können.