forked from kay/RouterOS
Geräte-Tab: "Feste Zuweisung entfernen" (Rückweg zu dynamisch)
RouterOS kennt kein make-dynamic als Umkehrung von make-static (per Recherche-Agent gegen die offizielle DHCP-Doku bestätigt: nur check-status/make-static/send-reconfigure existieren) — der offizielle Weg ist, die statische Lease zu entfernen; das Gerät bekommt beim naechsten Verbindungsaufbau automatisch wieder eine dynamische Adresse, moeglicherweise eine andere IP als zuvor. Neuer Kontextmenü-Eintrag bei fest zugewiesenen Geräten im Geräte-Tab, mit Bestätigungsdialog (erklärt den Ablauf) und Nachkontrolle, dass der Lease-Eintrag wirklich entfernt wurde, bevor Erfolg gemeldet wird - gleiche Vorsicht wie beim bestehenden "Feste IP zuweisen". Vom Nutzer live bestätigt (fest zuweisen -> entfernen -> Kabel/WLAN neu verbinden -> wieder dynamisch), kein neuer Bug diesmal. HANDOFF.md/CHATLOG.md aktualisiert: "Zurück auf dynamisch" aus den offenen Punkten entfernt, M12-Beschreibung ergänzt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CTgRxJTzaQwaRkngbaE1GJ
This commit is contained in:
+34
-4
@@ -523,7 +523,37 @@ falsche Negative. Alle vier zusammen ergaben eine ungewöhnlich lange,
|
||||
mehrstufige Fehlersuche mit wiederholten Rohdaten-Abgleichen und direkten
|
||||
Router-Gegenchecks (SSH-CLI + Winbox) durch den Nutzer. Commit für M11
|
||||
separat (`10f30a8`), M12 + HANDOFF/CHATLOG-Update in diesem Commit.
|
||||
Offen: "Zurück auf dynamisch"-Aktion fehlt noch im Geräte-Tab; ob dieselbe
|
||||
`.id`-Überlagerung auch im Experte-Tab bei anderen Menüs falsch zuordnen
|
||||
kann, ist ungeprüft; alle bereits vorher offenen Punkte unverändert
|
||||
(siehe HANDOFF.md Nächste Schritte).
|
||||
Offen (Stand direkt nach M12): "Zurück auf dynamisch"-Aktion fehlt noch
|
||||
im Geräte-Tab; ob dieselbe `.id`-Überlagerung auch im Experte-Tab bei
|
||||
anderen Menüs falsch zuordnen kann, ist ungeprüft; alle bereits vorher
|
||||
offenen Punkte unverändert (siehe HANDOFF.md Nächste Schritte).
|
||||
|
||||
## Nachtrag: "Feste Zuweisung entfernen"
|
||||
|
||||
- "jetzt Statisch IP zurück in dynamische IP. das scheint nicht so
|
||||
einfach zu funktionieren. Meine Recherchen ergeben: man muss erst die
|
||||
statische IP löschen, dann den client disconnecten und dann wieder
|
||||
connecten. ... kannst du das bestätigen? ... gib mir ein
|
||||
Lösungsvorschlag" → per Recherche-Agent gegen die offizielle
|
||||
RouterOS-DHCP-Doku geprüft: bestätigt, kein `make-dynamic`-Befehl
|
||||
existiert (nur `check-status`/`make-static`/`send-reconfigure`), der
|
||||
offizielle Weg ist `remove` der Lease, Client muss selbst neu anfragen
|
||||
(Kabel/WLAN trennen+verbinden, Neustart, oder eigener Renewal-Timer),
|
||||
keine Garantie auf dieselbe IP danach. Lösungsvorschlag vorgelegt: neuer
|
||||
Kontextmenü-Eintrag "Feste Zuweisung entfernen" bei festen Geräten
|
||||
(`.remove` über MAC-Adresse, Bestätigungsdialog mit diesen Hinweisen,
|
||||
Nachkontrolle dass der Eintrag wirklich weg ist).
|
||||
- "ja, bau das so" → `DevicesViewModel.pendingStaticRemoval`/
|
||||
`pendingRemovalCommand`/`confirmStaticRemoval`/`cancelStaticRemoval`,
|
||||
zweiter `.confirmationDialog` in `DevicesView` (gleiches
|
||||
Entkopplungs-Muster wie beim Zuweisen-Dialog, siehe Bug 16). Build+Tests
|
||||
grün, Release neu gebaut+deployt.
|
||||
- "das hat sofort funktioniert, habe die feste ip gelöscht und den client
|
||||
danach disconnectet und wieder connected, alles ok. ip ist wieder
|
||||
dynamisch. lassen wir so" — kein neuer Bug, erster Versuch erfolgreich,
|
||||
kompletter Kreislauf (fest zuweisen → entfernen → neu verbinden →
|
||||
dynamisch) vom Nutzer selbst bestätigt.
|
||||
- "ja, committen und beides aktualisieren" → dieser Eintrag, HANDOFF.md
|
||||
(M12-Beschreibung/Nächste-Schritte/Bekannte-Einschränkungen aktualisiert,
|
||||
"Zurück auf dynamisch" als erledigt markiert) aktualisiert, Commit
|
||||
erstellt.
|
||||
|
||||
+27
-27
@@ -21,13 +21,16 @@ farblich nach Verbindungsart getrennt, Hover/Klick hebt Zusammenhänge
|
||||
hervor). **M12 "Geräte"** ist ein LAN-Scanner (DHCP-Leases + ARP +
|
||||
Bridge-Host-Tabelle, gruppiert als eine Tabelle pro physischem Port) mit
|
||||
Rechtsklick-Aktion "Feste IP zuweisen" (RouterOS' "Make Static", per
|
||||
`/ip dhcp-server lease make-static`). Beide Tabs live gegen den
|
||||
hEX-Testrouter verifiziert — M12 dabei besonders gründlich, weil die
|
||||
Fest/Dynamisch-Erkennung und die Zuweisung selbst sich als überraschend
|
||||
zäh erwiesen: **vier** echte Bugs gefunden und gefixt (Bug 14–17 unten),
|
||||
am Ende vom Nutzer selbst bestätigt ("das funktioniert jetzt super").
|
||||
Release-Build unter `/Applications/RouterOS Assistant.app` ist auf
|
||||
aktuellem Stand.
|
||||
`/ip dhcp-server lease make-static`) sowie ihr Gegenstück "Feste
|
||||
Zuweisung entfernen" (RouterOS kennt kein `make-dynamic`, nur `remove` —
|
||||
vorher per Recherche-Agent gegen die offizielle Doku geprüft statt
|
||||
geraten). Beide Tabs live gegen den hEX-Testrouter verifiziert — M12
|
||||
dabei besonders gründlich, weil die Fest/Dynamisch-Erkennung und die
|
||||
Zuweisung selbst sich als überraschend zäh erwiesen: **vier** echte Bugs
|
||||
gefunden und gefixt (Bug 14–17 unten), am Ende vom Nutzer selbst
|
||||
bestätigt ("das funktioniert jetzt super", und für den Rückweg "das hat
|
||||
sofort funktioniert"). Release-Build unter
|
||||
`/Applications/RouterOS Assistant.app` ist auf aktuellem Stand.
|
||||
|
||||
## Ziel
|
||||
|
||||
@@ -103,7 +106,7 @@ RouterOSAssistant/
|
||||
Wizard/Steps/Setup/ — Einrichten-Tab: Mode → Wan → Lan → (Vlan nur Experte) → Wifi → Firewall → Review/Apply
|
||||
Expert/ — Experte-Tab (M10): ExpertView (Kategorie-/Menüliste + "eigener Pfad"), ExpertViewModel, ExpertMenuDetailView (Item-Liste + Add/Edit-Sheet)
|
||||
Overview/ — Übersicht-Tab (M11): OverviewView (Diagramm+Legende+Detailpanel), OverviewViewModel (buildGraph, rein), OverviewLayout (Spalten/Zeilen-Geometrie)
|
||||
Devices/ — Geräte-Tab (M12): DevicesView (Port-Tabellen+Rechtsklick+Rohdaten-Sheet), DevicesViewModel (buildDevices/buildPortGroups, rein; confirmStaticAssignment)
|
||||
Devices/ — Geräte-Tab (M12): DevicesView (Port-Tabellen+Rechtsklick+Rohdaten-Sheet), DevicesViewModel (buildDevices/buildPortGroups, rein; confirmStaticAssignment/confirmStaticRemoval)
|
||||
Backup/ — Sicherungen-Tab (Ordner wählen, Gefahrenzone: Werkseinstellungen wiederherstellen)
|
||||
Resources/Assets.xcassets/AppIcon.appiconset/ — App-Icon "Signal Router" (16px–1024px)
|
||||
RouterOSAssistantTests/ — reine Unit-Tests (Command-Builder, CLI-Parser, Fallback-Logik via Mock-Transport)
|
||||
@@ -377,9 +380,6 @@ wiederholen.
|
||||
(M12/Bug 14) ist zusätzlich auf einer nirgends offiziell dokumentierten
|
||||
Annahme aufgebaut (Query-String-Filter `?feld=wert` auf einem GET) —
|
||||
komplett unverifiziert.
|
||||
- **"Zurück auf dynamisch" fehlt im Geräte-Tab (M12)** — es gibt nur
|
||||
"Feste IP zuweisen" (Winbox' "Make Static"), keine Umkehrung. Kein Bug,
|
||||
einfach noch nicht gebaut; Nutzer hat danach gefragt.
|
||||
- **`fetchMenuItems`s `.id`-Positions-Überlagerung: Verlässlichkeit für
|
||||
andere Menüs ungeprüft** (siehe Bug 15) — bei `/ip dhcp-server lease`
|
||||
live als falsch bestätigt (`.id` landete auf der falschen Zeile), für
|
||||
@@ -600,13 +600,18 @@ beim Ändern die RouterOS-Suffix-Form zurück) — angewendet auf DHCP-Server
|
||||
System/Werkzeuge/Mangle/Raw (siehe `OverviewGraph.unmappedAreas`),
|
||||
weiterhin nur über den Experte-Tab erreichbar.
|
||||
- ✅ M12: Geräte-Tab (LAN-Scanner: DHCP-Leases+ARP+Bridge-Host, eine
|
||||
Tabelle pro physischem Port, "Feste IP zuweisen" für dynamische Leases)
|
||||
— **live gegen Hardware verifiziert und vom Nutzer bestätigt
|
||||
funktionierend** ("das funktioniert jetzt super"), nach vier gefundenen
|
||||
und gefixten Bugs (Bug 14–17, siehe oben) — die aufwendigste
|
||||
Tabelle pro physischem Port, "Feste IP zuweisen" für dynamische Leases,
|
||||
"Feste Zuweisung entfernen" für den Rückweg) — **live gegen Hardware
|
||||
verifiziert und vom Nutzer bestätigt funktionierend** ("das funktioniert
|
||||
jetzt super" / "das hat sofort funktioniert"), nach vier gefundenen und
|
||||
gefixten Bugs beim Hinweg (Bug 14–17, siehe oben) — die aufwendigste
|
||||
Live-Verifikation aller Milestones bisher, mehrere Diagnose-Runden mit
|
||||
Rohdaten-Vergleichen und direkten Router-Gegenchecks (SSH-CLI + Winbox).
|
||||
Fehlt noch: "Zurück auf dynamisch" (siehe Bekannte Einschränkungen).
|
||||
Rückweg ("Feste Zuweisung entfernen") lief dagegen beim ersten Versuch
|
||||
fehlerfrei — RouterOS-Doku vorher per Recherche-Agent geprüft (kein
|
||||
`make-dynamic`, nur `remove` + Client muss neu verbinden), nicht
|
||||
geraten. Nutzer bestätigte den vollen Kreislauf (fest zuweisen →
|
||||
entfernen → Kabel/WLAN trennen+verbinden → wieder dynamisch).
|
||||
|
||||
## Nächste Schritte
|
||||
|
||||
@@ -649,25 +654,20 @@ beim Ändern die RouterOS-Suffix-Form zurück) — angewendet auf DHCP-Server
|
||||
"Wiederherstellen"-Button pro Eintrag im Sicherungen-Tab
|
||||
(`BackupListView`) mit Bestätigungsdialog (ähnlich der bestehenden
|
||||
Werksreset-Gefahrenzone).
|
||||
11. **Geräte-Tab: "Zurück auf dynamisch"-Aktion bauen** — Gegenstück zu
|
||||
"Feste IP zuweisen". RouterOS-Weg dafür noch nicht recherchiert/live
|
||||
geprüft (vermutlich `remove` der Lease und Neuanlage, oder ein
|
||||
eigenes Menü-Kommando wie bei `make-static` — vor dem Bauen klären,
|
||||
nicht raten, siehe Bug 17 als Warnung).
|
||||
12. **`.id`-Positions-Überlagerung (`fetchMenuItems`) auf weitere Menüs
|
||||
11. **`.id`-Positions-Überlagerung (`fetchMenuItems`) auf weitere Menüs
|
||||
prüfen** — live als falsch bestätigt für `/ip dhcp-server lease`
|
||||
(Bug 15). Betrifft potenziell jedes `.set`/`.remove` im Experte-Tab.
|
||||
Am ehesten zu prüfen: bei einem Menü mit mehreren gleichzeitig
|
||||
vorhandenen Einträgen `:put [<Pfad> find]` und `<Pfad> print terse`
|
||||
unabhängig ausführen und die Reihenfolgen von Hand vergleichen.
|
||||
13. Optional: den neuen Dauer-Editor auch für den Geräte-Tab nutzen, falls
|
||||
12. Optional: den neuen Dauer-Editor auch für den Geräte-Tab nutzen, falls
|
||||
dort künftig Zeitfelder (z.B. eine Lease-Zeit-Anzeige) dazukommen.
|
||||
14. `Kay-Uwes-iMac` (ether2, `defconf`) und der Test-Laptop `DEDELLB2M6GK3`
|
||||
13. `Kay-Uwes-iMac` (ether2, `defconf`) und der Test-Laptop `DEDELLB2M6GK3`
|
||||
(ether3, `dhcp5`/`defconf` je nach Testrunde) sind **echte Geräte des
|
||||
Nutzers**, mit denen M12 live durchgetestet wurde — keine
|
||||
Test-Artefakte, nicht aufräumen. Der Laptop hatte am Ende der Session
|
||||
testweise eine feste IP zugewiesen bekommen; ob das der gewünschte
|
||||
Dauerzustand ist oder nur zum Testen war, mit dem Nutzer klären.
|
||||
Test-Artefakte, nicht aufräumen. Beide Geräte am Ende der Session
|
||||
wieder dynamisch (kompletter Kreislauf fest→entfernt→dynamisch vom
|
||||
Nutzer selbst am Laptop bestätigt).
|
||||
|
||||
Kein Gitea-Remote vorhanden — falls der Nutzer später eine Gitea-Instanz
|
||||
aufsetzt (z.B. selbst gehostet auf der vorhandenen OMV-NAS, siehe
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import SwiftUI
|
||||
|
||||
/// "Geräte" tab: LAN scanner — one table per physical Ethernet/WLAN port, each listing the
|
||||
/// devices resolved onto it (name/IP/MAC/status), plus a right-click action to give a device a
|
||||
/// permanent static IP the same way Winbox's "Make Static" does.
|
||||
/// devices resolved onto it (name/IP/MAC/status), plus right-click actions to give a device a
|
||||
/// permanent static IP (Winbox's "Make Static") or remove that reservation again.
|
||||
struct DevicesView: View {
|
||||
@ObservedObject var connectionService: ConnectionService
|
||||
@StateObject private var viewModel: DevicesViewModel
|
||||
@@ -16,6 +16,8 @@ struct DevicesView: View {
|
||||
/// dialogs elsewhere (BackupListView's factory-reset dialog): the dialog's own visibility and
|
||||
/// its payload are two separate pieces of state.
|
||||
@State private var showStaticConfirmation = false
|
||||
/// Same reasoning as `showStaticConfirmation` — independent of `pendingStaticRemoval`.
|
||||
@State private var showRemovalConfirmation = false
|
||||
|
||||
init(connectionService: ConnectionService) {
|
||||
self.connectionService = connectionService
|
||||
@@ -105,6 +107,28 @@ struct DevicesView: View {
|
||||
)
|
||||
}
|
||||
}
|
||||
.confirmationDialog(
|
||||
"Feste Zuweisung entfernen?",
|
||||
isPresented: $showRemovalConfirmation,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Entfernen", role: .destructive) {
|
||||
Task { await viewModel.confirmStaticRemoval() }
|
||||
}
|
||||
Button("Abbrechen", role: .cancel) { viewModel.cancelStaticRemoval() }
|
||||
} message: {
|
||||
if let device = viewModel.pendingStaticRemoval {
|
||||
Text(
|
||||
"Die feste Zuweisung für \(device.macAddress)"
|
||||
+ (device.hostName.map { " (\($0))" } ?? "")
|
||||
+ " wird entfernt. RouterOS kennt keine direkte Umkehrung von \"Make Static\" —"
|
||||
+ " das Gerät bekommt erst beim nächsten Verbindungsaufbau (Kabel/WLAN neu"
|
||||
+ " verbinden, Neustart) automatisch wieder eine Adresse per DHCP, eventuell"
|
||||
+ " eine andere IP als bisher."
|
||||
+ "\n\nBefehl: \(viewModel.pendingRemovalCommand?.cliLine ?? "")"
|
||||
)
|
||||
}
|
||||
}
|
||||
.alert(
|
||||
"Fehler",
|
||||
isPresented: Binding(
|
||||
@@ -127,7 +151,12 @@ struct DevicesView: View {
|
||||
private func deviceMenu(for device: LanDevice) -> some View {
|
||||
if device.hasLease {
|
||||
if device.isStatic {
|
||||
Text("Bereits fest zugewiesen")
|
||||
Button {
|
||||
viewModel.pendingStaticRemoval = device
|
||||
showRemovalConfirmation = true
|
||||
} label: {
|
||||
Label("Feste Zuweisung entfernen", systemImage: "pin.slash")
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
viewModel.pendingStaticAssignment = device
|
||||
|
||||
@@ -2,8 +2,9 @@ import Foundation
|
||||
|
||||
/// Builds the "Geräte" tab's LAN device list from a live router — DHCP leases enriched with the
|
||||
/// physical port each device was learned on (ARP + bridge host table) — grouped into one table
|
||||
/// per physical port, and drives the one write action this tab offers: converting a dynamic
|
||||
/// lease to a permanent static one.
|
||||
/// per physical port, and drives the two write actions this tab offers: converting a dynamic
|
||||
/// lease to a permanent static one, and removing a static lease again (RouterOS has no reverse
|
||||
/// of "make-static", so "back to dynamic" is just removal — see `pendingStaticRemoval`).
|
||||
@MainActor
|
||||
final class DevicesViewModel: ObservableObject {
|
||||
@Published private(set) var devices: [LanDevice] = []
|
||||
@@ -12,6 +13,12 @@ final class DevicesViewModel: ObservableObject {
|
||||
@Published private(set) var loadError: String?
|
||||
|
||||
@Published var pendingStaticAssignment: LanDevice?
|
||||
/// The device whose static reservation the user is about to remove — RouterOS has no
|
||||
/// "make-dynamic" (confirmed against the official DHCP docs: only check-status, make-static,
|
||||
/// send-reconfigure exist for this menu), so "back to dynamic" is genuinely just removing the
|
||||
/// static lease; the device gets a fresh dynamic lease on its own next DHCP request, not
|
||||
/// immediately or necessarily on the same IP.
|
||||
@Published var pendingStaticRemoval: LanDevice?
|
||||
@Published private(set) var isApplying = false
|
||||
@Published private(set) var applyError: String?
|
||||
|
||||
@@ -76,6 +83,17 @@ final class DevicesViewModel: ObservableObject {
|
||||
)
|
||||
}
|
||||
|
||||
/// The command "Feste Zuweisung entfernen" would run — matched by MAC, same reasoning as
|
||||
/// `pendingCommand`.
|
||||
var pendingRemovalCommand: RouterOSCommand? {
|
||||
guard let device = pendingStaticRemoval, device.hasLease else { return nil }
|
||||
return .remove(
|
||||
menuPath: "/ip dhcp-server lease", restPath: "ip/dhcp-server/lease",
|
||||
matchField: "mac-address", matchValue: device.macAddress,
|
||||
summary: "Feste Zuweisung entfernen für \(device.macAddress)"
|
||||
)
|
||||
}
|
||||
|
||||
/// Backs up once per connection before this tab's first write — same shared flag the Expert
|
||||
/// tool uses (`ConnectionService.hasExpertToolBackedUpThisSession`), so a session that already
|
||||
/// backed up via one "power tool" doesn't back up again via the other.
|
||||
@@ -125,6 +143,38 @@ final class DevicesViewModel: ObservableObject {
|
||||
pendingStaticAssignment = nil
|
||||
}
|
||||
|
||||
func confirmStaticRemoval() async {
|
||||
guard let command = pendingRemovalCommand, let device = pendingStaticRemoval else { return }
|
||||
isApplying = true
|
||||
applyError = nil
|
||||
do {
|
||||
try await ensureSessionBackup()
|
||||
try await connectionService.apply(command)
|
||||
// Same lesson as `confirmStaticAssignment` (Bug 10/17 in HANDOFF.md): empty output
|
||||
// isn't proof it worked. Verify the lease for this MAC is actually gone — a lingering
|
||||
// "no error" success is worth less than an honest check.
|
||||
let remainingLeases = try await connectionService.fetchMenuItems(
|
||||
menuPath: "/ip dhcp-server lease", restPath: "ip/dhcp-server/lease"
|
||||
)
|
||||
let stillPresent = remainingLeases.contains { $0.fields["mac-address"]?.lowercased() == device.macAddress.lowercased() }
|
||||
guard !stillPresent else {
|
||||
throw RouterOSError.invalidResponse(
|
||||
"Befehl lief ohne Fehlermeldung, aber der Lease-Eintrag ist weiterhin vorhanden. Bitte manuell mit \"/ip dhcp-server lease print\" kontrollieren."
|
||||
)
|
||||
}
|
||||
pendingStaticRemoval = nil
|
||||
await load()
|
||||
} catch {
|
||||
pendingStaticRemoval = nil
|
||||
applyError = error.localizedDescription
|
||||
}
|
||||
isApplying = false
|
||||
}
|
||||
|
||||
func cancelStaticRemoval() {
|
||||
pendingStaticRemoval = nil
|
||||
}
|
||||
|
||||
func dismissApplyError() {
|
||||
applyError = nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user