From 1cdcd0581ca4df80e0c9cdc64b9645db34c79449 Mon Sep 17 00:00:00 2001 From: Daniel Nagel Date: Tue, 8 Sep 2026 19:40:48 +0200 Subject: [PATCH] init --- README.md | 26 ++++++++++++++++++ src/host.rs | 16 +++++++++++ src/main.rs | 41 ++++++++++------------------ system_compress.txt | 65 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 27 deletions(-) create mode 100644 README.md create mode 100644 system_compress.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..18c1aae --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Homelab Watchdog + +Überwacht Server, Container und Netzwerkgeräte im Homelab. +Eine Liste von Hosts wird zyklisch geprüft, nicht erreichbare +dienste werden rot markiert und in MariaDB geloggt. + +## Architektur: Hybrid Python + Rust + +Nach Evaluation wurde eine hybride Architektur gewählt, um +die steile Lernkurve von Rust (async, TUI, DB-Handling) zu +vermeiden und trotzdem Rust zu lernen. + +| Layer | Sprache | Aufgabe | +|-------|---------|---------| +| **Manager** | Python | TUI (`textual`/`rich`), Timer, Config, MariaDB, ruft Worker auf | +| **Worker** | Rust | Kleine, synchron CLI-Binaries (z.B. `ping_worker`). IP als Arg, JSON auf stdout. | +| **Datenbank** | MariaDB | Zentrale Wahrheit für Status, Fehler, Zeitstempel | + +### Datenfluss +1. Python Manager liest Host-Liste (JSON/DB). +2. Alle X Minuten: `subprocess.run(["./ping_worker", ""])` pro Host. +3. Rust Worker pingt synchron, gibt `{"address":"...","reachable":true,"rtt_ms":12}` aus. +4. Python parst JSON, schreibt in MariaDB, aktualisiert TUI. + +## Projektstruktur (geplant) + diff --git a/src/host.rs b/src/host.rs index 2d46c15..2480263 100644 --- a/src/host.rs +++ b/src/host.rs @@ -11,4 +11,20 @@ pub(crate) struct Hosts { pub(crate) address: String, pub(crate) last_checked: Option, pub(crate) state_since: tokio::time::Instant, +} + +pub(crate) fn generate_gen_hosts() { + let google = Hosts { + name:String::from("Google"), + state: HostState::Offline, + address:String :: from("8.8.8.8"), + last_checked: None, + state_since: tokio::time::Instant::now(), + }; + let router = Hosts { + name:String::from("Unify Router"), + state: HostState::Offline, + address:String :: from("192.168.0.1"), + last_checked: None, + state_since: tokio::time::Instant::now(),}; } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f608e3c..b8faf62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,38 +1,25 @@ mod host; use tokio::time::{sleep, Duration}; -use host::{Hosts, HostState}; +use host::{Hosts, HostState, generate_gen_hosts}; +use tokio::process::Command; -async fn example() { - let time =5; - println!("Time .. On"); - sleep(Duration::from_secs(time)).await; +async fn ping_once(host: &Hosts) -> String { + let output = Command::new("ping") + .args(["-c", "1", &host.address]) + .output() + .await; + + // Transform bytes from output to string + let text_output = String::from_utf8_lossy(&output.output()).to_string(); + + return text_output } #[tokio::main] async fn main() { - let google = Hosts { - name:String::from("Google"), - state: HostState::Offline, - address:String :: from("8.8.8.8"), - last_checked: None, - state_since: tokio::time::Instant::now(), - }; - let router = Hosts { - name:String::from("Unify Router"), - state: HostState::Offline, - address:String :: from("192.168.0.1"), - last_checked: None, - state_since: tokio::time::Instant::now(), - }; - let pve1 = Hosts { - name:String::from("pve Server 1"), - state: HostState::Offline, - address:String :: from("192.168.0.231"), - last_checked: None, - state_since: tokio::time::Instant::now(), - }; - let list = vec![google, router, pve1]; + generate_gen_hosts(); + let list = vec![host.google, host.router]; println!("{:?}", list); example().await; diff --git a/system_compress.txt b/system_compress.txt new file mode 100644 index 0000000..46bbd5e --- /dev/null +++ b/system_compress.txt @@ -0,0 +1,65 @@ +=== KONTEXT KOMPENDIUM === +Nutzer: Daniel, SPS/Industrie-Hintergrund, KOP/SCL, Python, C#, JS, Rust-Anfänger. +Stil: Step-by-Step, max 1-2 Schritte pro Antwort. Erklärungen mit SPS-Analogien. +Kein fertiger Code als Lösung, nur Mini-Hinweise/Puzzleteile. +Nutzer darf selbst puzzeln. Bei Frage "Lösung bitte" -> Lösung geben. + +=== ARCHITEKTUR (Hybrid) === +Manager: Python (TUI, Timer, MariaDB, Config). +Worker: Rust (sync CLI, single-purpose, JSON stdout). +Kommunikation: subprocess.run(["./binary", ""], capture_output=True). +KEIN async/TUI in Rust. KEIN PyO3/maturin. KEINE Rust-DLLs für Python. + +=== AKTUELLES PROJEKT: ping_worker === +Separates Cargo-Projekt. +Aufgabe: IP aus args -> ping -> JSON stdout. +Status: Projekt neu, Cargo.toml steht, src/main.rs leer/Grundgerüst. +Nächster Schritt: Station 1: env::args() parsen, IP ausgeben, Usage bei fehlendem Arg. + +=== TECH STACK (ping_worker) === +[package] edition="2021" oder "2024" (Nutzer hat 2024, stable seit 1.85) +[dependencies] +serde = { version="1", features=["derive"] } +serde_json = "1" +KEIN tokio. KEIN anyhow. KEIN ratatui. KEIN crossterm. + +=== WISSENSSTAND NUTZER (Rust) === +- Cargo.toml: dependencies, features (hat tokio full gemeistert). +- struct, enum, Vec, #[derive(Debug)], Option. +- std::process::Command (Output, .status.success(), .stdout als Vec). +- String::from_utf8_lossy -> Cow -> .to_string(). +- match / if let / ? Operator (Theorie, teilweise Praxis). +- tokio::time::Instant (gelöscht aus aktuellem Scope). +- env::args() (neu, aktuelle Station). +- sync vs async Unterschied bekannt (blockierend vs Scheduler). + +=== OFFENE PUNKTE / LEHRPLAN === +1. ping_worker: args parsen. +2. ping_worker: Command::new("ping").args([...]).output() (sync!). +3. ping_worker: Struct mit Serialize, JSON stdout. +4. Python Manager: pymysql, textual/rich, Timer-Loop. +5. MariaDB Schema: host_status (id, name, address, reachable, checked_at, error_msg). +6. Integration Python <-> Rust. + +=== DOs & DONTs === +DO: Kleine Schritte. DO: SPS-Analogien (DB=Vec/Struct, OB1=main loop, Baugruppe=Crate). +DO: Einfache Vokabeln erklären (Ownership, Borrow, Lifetime, Result). +DO: Bei Fehlermeldungen: Fehler 1,2,3 klar trennen, nicht alles auf einmal. +DONT: Keine 5 Schritte auf einmal generieren. +DONT: Kein komplexes async/await/TUI für diesen Worker. +DONT: Kein "das ist einfach" sagen. Ehrlich über Rust-Steilheit. +DONT: Kein globaler State in Rust (static/Mutex) für diesen Worker. + +=== DATEISTRUKTUR (aktuell) === +homelab-watchdog/ +├── HomelabWatchdog/ (altes Projekt, mit tokio/ratatui, ruht) +└── ping_worker/ (aktiv) + ├── Cargo.toml + ├── src/main.rs + ├── ai_chat_help.txt + └── README.md + +=== VERSIONS-HINWEISSE === +Rust Edition 2024 ist stable (Rust >= 1.85). +tokio features = ["full"] nur wenn tokio verwendet wird (nicht hier). +serde features = ["derive"] zwingend für #[derive(Serialize)].