Add recovery preflight and ACC restart runbook
This commit is contained in:
+3
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
## Projekt und Sicherheitsziel
|
||||
|
||||
Das Repository enthält ein .NET-Framework-4.6.1-WinForms-Tool für kontrollierte BizTalk-2020-Wartungsoperationen. Änderungen müssen Dry-run, explizite Freigabe realer Aktionen, sichere Reihenfolgen und wiederherstellbare Installergrenzen erhalten. Die aktuelle Produktversion ist 2.2.0.
|
||||
Das Repository enthält ein .NET-Framework-4.6.1-WinForms-Tool für kontrollierte BizTalk-2020-Wartungsoperationen. Änderungen müssen Dry-run, explizite Freigabe realer Aktionen, sichere Reihenfolgen und wiederherstellbare Installergrenzen erhalten. Die aktuelle Produktversion ist 2.2.1.
|
||||
|
||||
## Installerinvarianten
|
||||
|
||||
@@ -28,6 +28,8 @@ Die zentrale Implementierung liegt in `src/BizTalkPlatformManagementTool.Setup/I
|
||||
- Restore-Kategorien gelten global über alle Anwendungen: Host Instances, Send Ports, Orchestrations, Receive Locations.
|
||||
- Emergency Restore überschreibt niemals die Eingabe-`before.json`, erzeugt eine timestamp-basierte Kopie und stellt `ENTSSO` vor Host Instances sicher.
|
||||
- Emergency Restore muss mit genau einer validen `before.json` funktionieren; Dateien eines vorherigen fehlgeschlagenen Laufs dürfen keine Voraussetzung sein.
|
||||
- Ein älterer kompatibler Snapshot, insbesondere aus 2.1.3, darf nicht allein anhand seines `ToolVersion`-Werts abgelehnt werden.
|
||||
- Der echte Emergency Restore erzeugt nach Möglichkeit automatisch einen timestamp-basierten Soll/Ist-Diff aus Recovery-Quelle und Nachher-Snapshot.
|
||||
- Die zentrale best-effort Orchestrierung liegt in `OperationPlanExecutor`; die WMI-/Service-Zustandsprüfung bleibt im produktiven Runtime-Adapter.
|
||||
|
||||
## Versionierung
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
|
||||
# Changelog
|
||||
|
||||
## [2.2.1] - 2026-08-19
|
||||
### Added
|
||||
- WMI-free **Validate State** preflight with snapshot metadata, server matching and artifact counts.
|
||||
- Direct **State...** file selection for a preserved recovery snapshot.
|
||||
- Automatic timestamped JSON/CSV/HTML target/actual diff after a real Emergency Restore.
|
||||
- Regression coverage proving that a `before.json` created by version 2.1.3 remains a valid Emergency Restore source.
|
||||
|
||||
### Changed
|
||||
- Emergency-recovery documentation now provides a dedicated operator runbook for the ACC restart and explicitly distinguishes dry-run evidence from real execution evidence.
|
||||
|
||||
## [2.2.0] - 2026-08-19
|
||||
### Added
|
||||
- State-aware **Emergency Restore** from an existing `before.json` without overwriting the recovery source.
|
||||
|
||||
+2
-1
@@ -56,7 +56,7 @@ Selbsterklärende Zuweisungen und reine UI-Konstruktion werden nicht zeilenweise
|
||||
9. Nach der Wartung mit **Restore** aus `before.json` wiederherstellen.
|
||||
10. Mit **Snapshot After** und **Compare** die Umgebung validieren.
|
||||
|
||||
Wenn ein echter Shutdown oder Restore nur teilweise ausgeführt wurde und lediglich die ursprüngliche `before.json` verfügbar ist, wird **Emergency Restore** verwendet. Der Modus kopiert die Quelle unter einem timestamp-basierten Namen, überschreibt `before.json` nicht, stellt zuerst `ENTSSO` sicher und gleicht danach den gespeicherten Sollzustand zustandsbewusst ab. Der erste Lauf muss als Dry-run erfolgen.
|
||||
Wenn ein echter Shutdown oder Restore nur teilweise ausgeführt wurde und lediglich die ursprüngliche `before.json` verfügbar ist, wird **Emergency Restore** verwendet. Die Quelle kann über **State...** ausgewählt und vorab über **Validate State** ohne WMI-Zugriff geprüft werden. Der Modus kopiert die Quelle unter einem timestamp-basierten Namen, überschreibt `before.json` nicht, stellt zuerst `ENTSSO` sicher und gleicht danach den gespeicherten Sollzustand zustandsbewusst ab. Der erste Lauf muss als Dry-run erfolgen.
|
||||
|
||||
Die Statusanzeige rechts im Kopfbereich bewertet die Host-Instance-Zustaende des letzten Snapshots und zeigt `Started`, `Stopped`, `Partial` oder `Unknown`. **Clear** leert die sichtbaren Ergebnis- und Log-Grids, loescht aber keine Dateien. **Close** beendet die Anwendung.
|
||||
|
||||
@@ -109,6 +109,7 @@ Der Emergency Restore benötigt nur eine valide `before.json` und denselben Ziel
|
||||
5. Bereits korrekte Zustände als `AlreadySatisfied` überspringen.
|
||||
6. Isolierte WMI-/Adapterfehler als `Failed` erfassen und mit dem nächsten unabhängigen Schritt fortfahren.
|
||||
7. Nachher-Snapshot unabhängig versuchen und vollständigen Ergebnisreport schreiben.
|
||||
8. Bei erfolgreichem Nachher-Snapshot automatisch einen timestamp-basierten Soll/Ist-Diff als JSON, CSV und HTML erzeugen.
|
||||
|
||||
Der Best-effort-Ansatz bedeutet nicht, dass Fehler ignoriert werden: Jeder Teilfehler erzeugt einen roten/fehlgeschlagenen Abschluss und erfordert die Prüfung des Ergebnisreports. Er verhindert lediglich, dass beispielsweise eine einzelne nicht validierbare Receive Location alle späteren Host-, Port- oder Receive-Location-Schritte blockiert.
|
||||
|
||||
|
||||
+7
-6
@@ -89,18 +89,19 @@ Ein erfolgreicher Installer-Self-Test bestätigt Paket, Programmstart und lokale
|
||||
|
||||
## Emergency Restore nach einem Teilabbruch
|
||||
|
||||
Version 2.2.0 kann einen Wiederanlauf allein aus einer erhaltenen `before.json` vorbereiten und ausführen. Zusätzliche Plan- oder Nachher-Dateien des fehlgeschlagenen Laufs sind nicht erforderlich.
|
||||
Version 2.2.1 kann einen Wiederanlauf allein aus einer erhaltenen `before.json` vorbereiten und ausführen. Eine mit Version 2.1.3 erzeugte Datei ist kompatibel; zusätzliche Plan- oder Nachher-Dateien des fehlgeschlagenen Laufs sind nicht erforderlich.
|
||||
|
||||
1. Die erhaltene `before.json` außerhalb des Arbeitsverzeichnisses zusätzlich sichern.
|
||||
2. Anwendung als Administrator starten und denselben Zielserver wählen, der im Snapshot gespeichert ist.
|
||||
3. Im Feld **State** die erhaltene Datei auswählen oder ihren vollständigen Pfad eintragen.
|
||||
4. **Dry run** aktiviert lassen und **Emergency Restore** wählen.
|
||||
5. Den timestamp-basierten `emergency-restore-plan-*` prüfen. Die bestehende `before.json` wird dabei nicht überschrieben.
|
||||
6. Dry run deaktivieren, **Emergency Restore** erneut wählen und den expliziten Dialog bestätigen.
|
||||
3. Über **State...** die erhaltene Datei auswählen oder im Feld **State** ihren vollständigen Pfad eintragen.
|
||||
4. **Validate State** ausführen. Diese Prüfung öffnet keine WMI-Verbindung und verändert keinen Laufzeitzustand.
|
||||
5. **Dry run** aktiviert lassen und **Emergency Restore** wählen.
|
||||
6. Den timestamp-basierten `emergency-restore-plan-*` prüfen. Die bestehende `before.json` wird dabei nicht überschrieben.
|
||||
7. Dry run deaktivieren, **Emergency Restore** erneut wählen und den expliziten Dialog bestätigen.
|
||||
|
||||
Der Emergency Restore stellt zuerst sicher, dass der Windows-Dienst `ENTSSO` läuft. Danach folgen Host Instances, Send Ports, Orchestrations und zuletzt Receive Locations. Vor jeder Mutation wird der aktuelle Zustand geprüft; bereits korrekte Zustände werden als `AlreadySatisfied` protokolliert. Ein isolierter Fehler wird als `Failed` festgehalten, während alle späteren unabhängigen Schritte weiter versucht werden.
|
||||
|
||||
Jeder Lauf schreibt eine unveränderte Snapshot-Kopie sowie timestamp-basierte Plan-, Ergebnis- und Nachher-Dateien. Ein Ergebnis mit mindestens einem fehlgeschlagenen Schritt bleibt im GUI ausdrücklich `Failed` und verlangt Operator-Review, auch wenn alle anderen Schritte erfolgreich waren.
|
||||
Jeder Lauf schreibt eine unveränderte Snapshot-Kopie sowie timestamp-basierte Plan- und Ergebnisdateien. Der echte Lauf versucht zusätzlich Nachher-Snapshot und `emergency-restore-diff-*` als JSON, CSV und HTML. Ein Ergebnis mit mindestens einem fehlgeschlagenen Schritt bleibt im GUI ausdrücklich `Failed` und verlangt Operator-Review, auch wenn alle anderen Schritte erfolgreich waren.
|
||||
|
||||
## Deinstallation
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ WinForms tool for controlled Microsoft BizTalk Server 2020 platform operations d
|
||||
- Controlled shutdown from the current runtime state
|
||||
- Controlled restore from `before.json`
|
||||
- State-aware emergency restore from a preserved `before.json`, including Enterprise SSO startup
|
||||
- WMI-free validation and file selection for preserved recovery snapshots, including 2.1.3 snapshots
|
||||
- Host instance handling for the selected BizTalk server
|
||||
- Dry-run mode enabled by default
|
||||
- WMI access through `root\MicrosoftBizTalkServer`
|
||||
@@ -45,7 +46,7 @@ WinForms tool for controlled Microsoft BizTalk Server 2020 platform operations d
|
||||
7. After maintenance, click **Restore** using the saved `before.json`.
|
||||
8. Click **Snapshot After** and **Compare**.
|
||||
|
||||
If a shutdown was interrupted and only the original `before.json` remains, select that file, keep **Dry run** enabled and click **Emergency Restore**. The recovery plan never overwrites the source snapshot, ensures the `ENTSSO` service is running first, skips already-correct runtime states and continues after isolated step failures. Disable Dry run only after reviewing the timestamped emergency plan.
|
||||
If a shutdown was interrupted and only the original `before.json` remains, select that file with **State...**, run **Validate State**, keep **Dry run** enabled and click **Emergency Restore**. The recovery plan never overwrites the source snapshot, ensures the `ENTSSO` service is running first, skips already-correct runtime states and continues after isolated step failures. Disable Dry run only after reviewing the timestamped emergency plan. A real run automatically writes a timestamped target/actual diff when the post-operation snapshot succeeds.
|
||||
|
||||
The environment indicator shows `Started`, `Stopped`, `Partial` or `Unknown` from the most recent snapshot. `Clear` removes the visible status and operation log grids; it does not delete files.
|
||||
|
||||
@@ -75,7 +76,7 @@ Orchestrations that were `Bound` are deliberately left unchanged during restore
|
||||
- `shutdown-plan.json`, `restore-plan.json`
|
||||
- `shutdown-after.json`, `restore-after.json`
|
||||
- `shutdown-result.json`, `restore-result.json`
|
||||
- Timestamped `emergency-source-before-*`, `emergency-restore-plan-*`, `emergency-restore-result-*` and `emergency-restore-after-*` files
|
||||
- Timestamped `emergency-source-before-*`, `emergency-restore-plan-*`, `emergency-restore-result-*`, `emergency-restore-after-*` and `emergency-restore-diff-*` files
|
||||
- `diff.json`, `diff.csv`, `diff.html`
|
||||
- Snapshot sidecars: `*.csv`, `*.hosts.csv`, `*.html`
|
||||
- Runtime logs under `%ProgramData%\BizTalkPlatformManagementTool\Logs`
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# ACC Emergency Restore aus `before.json`
|
||||
|
||||
Stand: 19.08.2026
|
||||
Ausführung: Donnerstag, 20.08.2026, oder Montag, 24.08.2026
|
||||
Zielserver: `AV23AGPWBI01`
|
||||
Benötigte Toolversion: `2.2.1`
|
||||
|
||||
## Kurzantwort
|
||||
|
||||
Ja. Die vorhandene, mit Version 2.1.3 erzeugte `before.json` kann von Version 2.2.1 direkt als Recovery-Quelle verwendet werden. Zusätzliche Dateien des abgebrochenen Shutdowns sind nicht erforderlich. Die Kompatibilität wird durch einen Regressionstest abgedeckt; die echte BizTalk-/ACC-Ausführung muss noch vor Ort bestätigt werden.
|
||||
|
||||
Für diesen Teilzustand ausschließlich **Emergency Restore** verwenden. Dieser Modus startet zuerst `ENTSSO`, arbeitet danach den gespeicherten Sollzustand idempotent ab, setzt nach Einzelfehlern fort und überschreibt die Quell-`before.json` nicht.
|
||||
|
||||
## 1. Dateien vorbereiten
|
||||
|
||||
- [ ] Originale `before.json` an einem zweiten Ort unverändert sichern.
|
||||
- [ ] Optional den Hash der Originaldatei protokollieren:
|
||||
|
||||
```bat
|
||||
certutil -hashfile before.json SHA256
|
||||
```
|
||||
|
||||
- [ ] Die Release-Datei `BizTalkPlatformManagementTool-Setup.zip.b64.txt` und die zugehörige SHA-256-Datei auf ACC übertragen.
|
||||
- [ ] Die TXT-Datei rekonstruieren und das ZIP prüfen:
|
||||
|
||||
```bat
|
||||
certutil -decode BizTalkPlatformManagementTool-Setup.zip.b64.txt BizTalkPlatformManagementTool-Setup.zip
|
||||
certutil -hashfile BizTalkPlatformManagementTool-Setup.zip SHA256
|
||||
type BizTalkPlatformManagementTool-Setup.zip.sha256.txt
|
||||
```
|
||||
|
||||
- [ ] Nur fortfahren, wenn beide ZIP-Hashes exakt gleich sind.
|
||||
- [ ] ZIP in einen neuen Ordner entpacken, die laufende Toolinstanz schließen und `Setup.exe` als Administrator starten.
|
||||
- [ ] Installation/Update auf Version `2.2.1` vollständig abschließen.
|
||||
- [ ] Ein neues, leeres Ausgabeverzeichnis anlegen, zum Beispiel `C:\BizTalk-Recovery\2026-08-20` oder `C:\BizTalk-Recovery\2026-08-24`.
|
||||
|
||||
Wichtig: Die Recovery-Quelle außerhalb dieses Ausgabeverzeichnisses lassen. Während der Recovery nicht **Snapshot Before** anklicken, damit kein neuer Teilzustand als vermeintlicher Sollzustand gespeichert wird.
|
||||
|
||||
## 2. `before.json` ohne WMI prüfen
|
||||
|
||||
- [ ] Anwendung als Administrator starten.
|
||||
- [ ] Im Feld **Server** `AV23AGPWBI01` eintragen. Kurzname und FQDN desselben Hosts werden akzeptiert.
|
||||
- [ ] Unter **Output** das neue Recovery-Ausgabeverzeichnis auswählen.
|
||||
- [ ] Über **State...** die gesicherte `before.json` auswählen.
|
||||
- [ ] **Dry run** aktiviert lassen.
|
||||
- [ ] **Validate State** anklicken.
|
||||
- [ ] Im Erfolgsdialog prüfen:
|
||||
- Snapshotversion ist erwartungsgemäß `2.1.3-net461` oder `2.1.3`.
|
||||
- Snapshotserver ist `AV23AGPWBI01` beziehungsweise dessen FQDN.
|
||||
- Erstellzeit und Anzahl von Applications, Host Instances, Receive Locations, Send Ports und Orchestrations wirken plausibel.
|
||||
|
||||
**Validate State** öffnet keine WMI-Verbindung und verändert weder BizTalk noch Windows-Dienste. Bei einer roten Fehlermeldung nicht real ausführen, sondern zuerst Pfad, JSON-Datei und Serverauswahl korrigieren.
|
||||
|
||||
## 3. Emergency-Dry-run prüfen
|
||||
|
||||
- [ ] **Dry run** bleibt aktiviert.
|
||||
- [ ] **Emergency Restore** anklicken.
|
||||
- [ ] Den erzeugten `emergency-restore-plan-<Zeitstempel>.json` öffnen.
|
||||
- [ ] Prüfen, dass der Plan zu `AV23AGPWBI01` gehört.
|
||||
- [ ] Reihenfolge prüfen:
|
||||
1. Windows-Dienst `ENTSSO`,
|
||||
2. alle Host Instances,
|
||||
3. alle Send Ports,
|
||||
4. alle Orchestrations,
|
||||
5. alle Receive Locations.
|
||||
- [ ] Prüfen, dass der Dry-run-Report `emergency-restore-result-<Zeitstempel>.json` geschrieben wurde und keine Initialisierungsfehler enthält.
|
||||
- [ ] Die timestamp-basierte Datei `emergency-source-before-<Zeitstempel>.json` als zusätzliche Recovery-Evidenz sichern.
|
||||
|
||||
Der Dry-run führt keine Zustandsänderung aus. Er bestätigt aber, dass genau die erhaltene Datei gelesen, validiert und in einen ausführbaren Emergency-Plan übersetzt werden kann.
|
||||
|
||||
## 4. Echten Wiederanlauf ausführen
|
||||
|
||||
- [ ] Sicherstellen, dass SQL Server, Netzwerk und die übrigen BizTalk-Abhängigkeiten erreichbar sind.
|
||||
- [ ] Noch einmal kontrollieren: Server, Output-Pfad und State-Datei sind korrekt.
|
||||
- [ ] **Dry run** deaktivieren.
|
||||
- [ ] **Emergency Restore** anklicken.
|
||||
- [ ] Im Bestätigungsdialog Quellpfad, Zielserver, Plandatei und Schrittzahl lesen.
|
||||
- [ ] Erst danach mit **Yes** bestätigen.
|
||||
- [ ] Anwendung bis zum Abschluss geöffnet lassen.
|
||||
|
||||
Die reale Reihenfolge ist:
|
||||
|
||||
1. `ENTSSO` auf `Running` bringen.
|
||||
2. Host Instances gemäß `before.json` abgleichen.
|
||||
3. Send Ports abgleichen.
|
||||
4. Orchestrations abgleichen; im Snapshot nur `Bound` gebliebene Orchestrations werden bewusst zur manuellen Kontrolle markiert.
|
||||
5. Receive Locations zuletzt abgleichen.
|
||||
|
||||
Vor jeder Mutation liest Version 2.2.1 den Istzustand. Ein bereits korrekter Zustand wird als `AlreadySatisfied` protokolliert und nicht erneut per WMI geändert. Ein einzelner Fehler wird als `Failed` gespeichert; alle späteren unabhängigen Schritte werden trotzdem versucht.
|
||||
|
||||
## 5. Ergebnis abnehmen
|
||||
|
||||
- [ ] `emergency-restore-result-<Zeitstempel>.json` prüfen.
|
||||
- [ ] Idealfall: `FailedCount` ist `0`, `InitializationError` und `PostSnapshotError` sind leer.
|
||||
- [ ] `AlreadySatisfied` ist normal und bedeutet, dass keine unnötige Mutation ausgeführt wurde.
|
||||
- [ ] `emergency-restore-after-<Zeitstempel>.json` muss nach einem echten Lauf vorhanden sein, sofern der Nachher-Snapshot gelang.
|
||||
- [ ] `emergency-restore-diff-<Zeitstempel>.json`, `.csv` und `.html` prüfen. Im vollständigen Sollzustand sind keine fachlichen Unterschiede enthalten.
|
||||
- [ ] Danach **Diagnose** ausführen und den BizTalk-Zustand zusätzlich in der BizTalk Administration Console kontrollieren.
|
||||
- [ ] Empfang, Verarbeitung und Versand mit dem für ACC vorgesehenen fachlichen Smoke-Test prüfen; Suspended Instances kontrollieren.
|
||||
- [ ] Recovery-Quelle, Plan, Ergebnis, Nachher-Snapshot, Diff und Laufzeitlog gemeinsam sichern.
|
||||
|
||||
## Sonderfall `RV_PMP_Trigger_Schedule`
|
||||
|
||||
Wenn die Receive Location noch aktiviert ist und `before.json` ebenfalls `Enabled=true` vorgibt, wird sie als `AlreadySatisfied` übersprungen. Damit wird der fehlerhafte Scheduler-WMI-Aufruf nicht erneut ausgelöst.
|
||||
|
||||
Wenn sie deaktiviert ist, obwohl `before.json` sie aktiviert erwartet, versucht das Tool die Aktivierung. Tritt der bekannte Fehler zu `Microsoft.BizTalk.Scheduler, Version=3.13.0.0` erneut auf, wird genau dieser Schritt als `Failed` protokolliert; die übrigen Receive Locations werden weiterbearbeitet. Dann:
|
||||
|
||||
- [ ] Ergebnisreport und vollständige Exception sichern.
|
||||
- [ ] Aktuellen Zustand der Receive Location in der BizTalk Administration Console prüfen.
|
||||
- [ ] Scheduler-Assembly/Abhängigkeiten auf dem Administrations-/BizTalk-Host reparieren oder den Schritt nach fachlicher Freigabe manuell ausführen.
|
||||
- [ ] PROD erst freigeben, wenn der verbleibende Einzelfehler behoben oder ausdrücklich akzeptiert und getestet ist.
|
||||
|
||||
## Wenn der Lauf erneut unterbrochen wird
|
||||
|
||||
Die ursprüngliche `before.json` bleibt unverändert. Version 2.2.1 kann mit derselben Recovery-Quelle erneut gestartet werden, weil bereits erreichte Zustände als `AlreadySatisfied` übersprungen werden. Vor dem Wiederholen immer den jüngsten Ergebnisreport sichern und prüfen, welcher Schritt tatsächlich fehlgeschlagen ist.
|
||||
|
||||
## Go/No-Go für PROD
|
||||
|
||||
ACC ist erst ein belastbarer PROD-Nachweis, wenn der echte Emergency Restore abgeschlossen wurde, der Ergebnisreport vollständig ist, der automatische Diff geprüft wurde und jeder verbliebene `Failed`-Schritt fachlich behandelt wurde. Die lokalen 24 Regressionstests und der Self-Test ersetzen diesen realen BizTalk-/Windows-Nachweis nicht.
|
||||
@@ -48,6 +48,13 @@ Der neue Button **Emergency Restore** benötigt außer der erhaltenen `before.js
|
||||
6. arbeitet nach isolierten Fehlern weiter,
|
||||
7. versucht einen Nachher-Snapshot und schreibt immer den vollständigen Ergebnisreport, soweit das Ausgabeverzeichnis verfügbar ist.
|
||||
|
||||
## Recovery-Ergänzungen in Version 2.2.1
|
||||
|
||||
- **State...** wählt die erhaltene `before.json` direkt aus.
|
||||
- **Validate State** prüft JSON-Struktur und Zielserver ohne WMI-Verbindung oder Laufzeitänderung und zeigt Snapshotversion, Erstellzeit sowie Artefaktzahlen an.
|
||||
- Ein 2.1.3-Snapshot wird ausdrücklich als kompatible Recovery-Quelle regressionsgetestet; `ToolVersion` ist keine Versionssperre.
|
||||
- Nach einem echten Emergency Restore wird bei erfolgreichem Nachher-Snapshot automatisch `emergency-restore-diff-*` als JSON, CSV und HTML geschrieben.
|
||||
|
||||
### Globale Reihenfolge
|
||||
|
||||
Shutdown:
|
||||
@@ -79,11 +86,11 @@ Die portable Regressionstestsuite enthält ab Version 2.2.0 unter anderem folgen
|
||||
- globale Shutdown-/Restore-Reihenfolge mit mehreren Anwendungen,
|
||||
- Kurzname/FQDN-Gleichheit für Host-Instance-Schritte.
|
||||
|
||||
Die lokale Suite umfasst 23 bestandene Tests. Die abschließende Freigabe für PROD benötigt weiterhin einen Windows-/BizTalk-Test auf ACC: echter Scheduler-Fehlerpfad, vollständige Fortsetzung, Ergebnisreport, Emergency-Dry-run und realer Wiederanlauf aus einer Kopie der vorhandenen `before.json`.
|
||||
Die lokale Suite umfasst ab Version 2.2.1 insgesamt 24 Tests einschließlich der expliziten 2.1.3-Snapshot-Kompatibilität. Die abschließende Freigabe für PROD benötigt weiterhin einen Windows-/BizTalk-Test auf ACC: echter Scheduler-Fehlerpfad, vollständige Fortsetzung, Ergebnisreport, Emergency-Dry-run und realer Wiederanlauf aus einer Kopie der vorhandenen `before.json`.
|
||||
|
||||
## PROD-Freigabekriterien
|
||||
|
||||
- Version 2.2.0 installieren und Installer-Self-Test bestätigen.
|
||||
- Version 2.2.1 installieren und Installer-Self-Test bestätigen.
|
||||
- `Diagnose` und einen vollständigen Shutdown-Dry-run ausführen.
|
||||
- Kontrolliert nachweisen, dass eine isolierte Scheduler-Exception spätere Schritte nicht blockiert.
|
||||
- `shutdown-result.json` und Nachher-Snapshot prüfen.
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace BizTalkPlatformManagementTool.Setup
|
||||
private const string ProductName = "BizTalk Platform Management Tool";
|
||||
|
||||
/// <summary>Aktuelle Produktversion des Installers und Uninstall-Eintrags.</summary>
|
||||
private const string ProductVersion = "2.2.0";
|
||||
private const string ProductVersion = "2.2.1";
|
||||
|
||||
/// <summary>
|
||||
/// Wartezeiten zwischen Wiederholungen atomarer Verzeichnisverschiebungen.
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace BizTalkPlatformManagementTool.Setup
|
||||
{
|
||||
AutoSize = true,
|
||||
Font = new Font(Font.FontFamily, 14, FontStyle.Bold),
|
||||
Text = "BizTalk Platform Management Tool 2.2.0"
|
||||
Text = "BizTalk Platform Management Tool 2.2.1"
|
||||
});
|
||||
root.Controls.Add(new Label
|
||||
{
|
||||
|
||||
@@ -8,6 +8,6 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyProduct("BizTalk Platform Management Tool")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("675b68a9-bd80-46a5-b8c5-3b11b0b374e2")]
|
||||
[assembly: AssemblyVersion("2.2.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.2.0.0")]
|
||||
[assembly: AssemblyVersion("2.2.1.0")]
|
||||
[assembly: AssemblyFileVersion("2.2.1.0")]
|
||||
[assembly: InternalsVisibleTo("BizTalkPlatformManagementTool.Tests")]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="2.2.0.0" name="BizTalkPlatformManagementTool.Setup" />
|
||||
<assemblyIdentity version="2.2.1.0" name="BizTalkPlatformManagementTool.Setup" />
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security><requestedPrivileges><requestedExecutionLevel level="requireAdministrator" uiAccess="false" /></requestedPrivileges></security>
|
||||
</trustInfo>
|
||||
|
||||
@@ -9,6 +9,6 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyCopyright("Copyright © 2026")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("2c5b2c0a-f407-46c2-9e3b-1fa09fa8445a")]
|
||||
[assembly: AssemblyVersion("2.2.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.2.0.0")]
|
||||
[assembly: AssemblyVersion("2.2.1.0")]
|
||||
[assembly: AssemblyFileVersion("2.2.1.0")]
|
||||
[assembly: InternalsVisibleTo("BizTalkPlatformManagementTool.Tests")]
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace BizTalkPlatformManagementTool.Services
|
||||
/// <summary>
|
||||
/// Current tool version written into generated snapshots.
|
||||
/// </summary>
|
||||
public const string Version = "2.2.0-net461";
|
||||
public const string Version = "2.2.1-net461";
|
||||
|
||||
/// <summary>
|
||||
/// Fallback application name used when WMI does not expose an application property.
|
||||
|
||||
@@ -110,6 +110,11 @@ namespace BizTalkPlatformManagementTool.Ui
|
||||
/// </summary>
|
||||
private Button _restoreButton;
|
||||
|
||||
/// <summary>
|
||||
/// Button that validates the selected recovery snapshot without contacting WMI.
|
||||
/// </summary>
|
||||
private Button _validateStateButton;
|
||||
|
||||
/// <summary>
|
||||
/// Button that performs a state-aware best-effort recovery from an existing snapshot.
|
||||
/// </summary>
|
||||
@@ -220,6 +225,8 @@ namespace BizTalkPlatformManagementTool.Ui
|
||||
|
||||
var browseButton = new Button { Text = "Browse...", Dock = DockStyle.Fill, Margin = new Padding(8, 5, 8, 5) };
|
||||
browseButton.Click += BrowseButtonClick;
|
||||
var stateBrowseButton = new Button { Text = "State...", Dock = DockStyle.Fill, Margin = new Padding(8, 5, 8, 5) };
|
||||
stateBrowseButton.Click += StateBrowseButtonClick;
|
||||
|
||||
_environmentStatusLabel.Dock = DockStyle.Fill;
|
||||
_environmentStatusLabel.Margin = new Padding(12, 5, 0, 5);
|
||||
@@ -239,6 +246,7 @@ namespace BizTalkPlatformManagementTool.Ui
|
||||
panel.Controls.Add(_stateFileTextBox, 1, 1);
|
||||
panel.SetColumnSpan(_stateFileTextBox, 2);
|
||||
panel.Controls.Add(_dryRunCheckBox, 3, 1);
|
||||
panel.Controls.Add(stateBrowseButton, 4, 1);
|
||||
panel.Controls.Add(Label("Poll sec."), 7, 1);
|
||||
panel.Controls.Add(_pollInput, 8, 1);
|
||||
panel.Controls.Add(_environmentStatusLabel, 9, 0);
|
||||
@@ -260,6 +268,7 @@ namespace BizTalkPlatformManagementTool.Ui
|
||||
_compareButton = ActionButton("Compare", CompareClick);
|
||||
_shutdownButton = ActionButton("Shutdown", ShutdownClick);
|
||||
_restoreButton = ActionButton("Restore", RestoreClick);
|
||||
_validateStateButton = ActionButton("Validate State", ValidateStateClick);
|
||||
_emergencyRestoreButton = ActionButton("Emergency Restore", EmergencyRestoreClick);
|
||||
_emergencyRestoreButton.Width = 142;
|
||||
_clearButton = ActionButton("Clear", ClearClick);
|
||||
@@ -271,6 +280,7 @@ namespace BizTalkPlatformManagementTool.Ui
|
||||
panel.Controls.Add(_compareButton);
|
||||
panel.Controls.Add(_shutdownButton);
|
||||
panel.Controls.Add(_restoreButton);
|
||||
panel.Controls.Add(_validateStateButton);
|
||||
panel.Controls.Add(_emergencyRestoreButton);
|
||||
panel.Controls.Add(_clearButton);
|
||||
panel.Controls.Add(_closeButton);
|
||||
@@ -425,6 +435,39 @@ namespace BizTalkPlatformManagementTool.Ui
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the selected state file and target server without opening a WMI connection
|
||||
/// or changing any runtime state.
|
||||
/// </summary>
|
||||
/// <param name="sender">The control that raised the event.</param>
|
||||
/// <param name="e">The event arguments.</param>
|
||||
private void ValidateStateClick(object sender, EventArgs e)
|
||||
{
|
||||
var options = GetOptions();
|
||||
RunAsync("Validating recovery state file...", () =>
|
||||
{
|
||||
var sourcePath = ResolveStateFile(options);
|
||||
var snapshot = JsonFileStore.Load<BizTalkSnapshot>(sourcePath);
|
||||
SnapshotValidator.EnsureServerMatches(snapshot, options.Server);
|
||||
ShowSnapshot(snapshot);
|
||||
|
||||
var receiveLocationCount = snapshot.Applications.Sum(x => x.ReceiveLocations.Count);
|
||||
var sendPortCount = snapshot.Applications.Sum(x => x.SendPorts.Count);
|
||||
var orchestrationCount = snapshot.Applications.Sum(x => x.Orchestrations.Count);
|
||||
var summary = "Recovery state is valid. Source=" + sourcePath
|
||||
+ "; SnapshotVersion=" + (snapshot.ToolVersion ?? "(not recorded)")
|
||||
+ "; CreatedAt=" + (snapshot.CreatedAt ?? "(not recorded)")
|
||||
+ "; Server=" + snapshot.Server
|
||||
+ "; Applications=" + snapshot.Applications.Count
|
||||
+ "; HostInstances=" + snapshot.HostInstances.Count
|
||||
+ "; ReceiveLocations=" + receiveLocationCount
|
||||
+ "; SendPorts=" + sendPortCount
|
||||
+ "; Orchestrations=" + orchestrationCount + ".";
|
||||
_logger.Success(summary);
|
||||
ShowStateValidationSummary(summary);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles emergency recovery from the preserved state file without creating
|
||||
/// or overwriting <c>before.json</c>.
|
||||
@@ -454,7 +497,24 @@ namespace BizTalkPlatformManagementTool.Ui
|
||||
}
|
||||
|
||||
var report = _service.ExecutePlan(plan, options);
|
||||
CapturePostOperationSnapshot(options, report, "emergency-restore-after-" + stamp + ".json");
|
||||
var after = CapturePostOperationSnapshot(options, report, "emergency-restore-after-" + stamp + ".json");
|
||||
if (after != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var diff = SnapshotComparer.Compare(snapshot, after);
|
||||
var diffPath = _service.SaveDiff(options.OutputDirectory, "emergency-restore-diff-" + stamp + ".json", diff);
|
||||
_logger.Success("Saved emergency recovery target/actual comparison: " + diffPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var error = "Emergency recovery comparison failed: " + FormatException(ex);
|
||||
report.PostSnapshotError = string.IsNullOrWhiteSpace(report.PostSnapshotError)
|
||||
? error
|
||||
: report.PostSnapshotError + " | " + error;
|
||||
_logger.Error(error + " The execution report will still be saved.");
|
||||
}
|
||||
}
|
||||
var reportPath = _service.SaveExecutionReport(options.OutputDirectory, "emergency-restore-result-" + stamp + ".json", report);
|
||||
ShowExecutionReport(report);
|
||||
ThrowWhenOperatorReviewIsRequired(report, reportPath);
|
||||
@@ -467,22 +527,24 @@ namespace BizTalkPlatformManagementTool.Ui
|
||||
/// <param name="options">The operation options.</param>
|
||||
/// <param name="report">The report that receives a snapshot error.</param>
|
||||
/// <param name="fileName">The snapshot file name.</param>
|
||||
private void CapturePostOperationSnapshot(OperationOptions options, OperationExecutionReport report, string fileName)
|
||||
private BizTalkSnapshot CapturePostOperationSnapshot(OperationOptions options, OperationExecutionReport report, string fileName)
|
||||
{
|
||||
if (options.DryRun)
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
var after = _service.CreateSnapshot(options.Server);
|
||||
_service.SaveSnapshot(options.OutputDirectory, fileName, after);
|
||||
ShowSnapshot(after);
|
||||
return after;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
report.PostSnapshotError = FormatException(ex);
|
||||
_logger.Error("Post-operation snapshot failed, but the execution report will still be saved. Error: " + report.PostSnapshotError);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -788,6 +850,7 @@ namespace BizTalkPlatformManagementTool.Ui
|
||||
_compareButton.Enabled = !busy;
|
||||
_shutdownButton.Enabled = !busy;
|
||||
_restoreButton.Enabled = !busy;
|
||||
_validateStateButton.Enabled = !busy;
|
||||
_emergencyRestoreButton.Enabled = !busy;
|
||||
_clearButton.Enabled = !busy;
|
||||
_closeButton.Enabled = !busy;
|
||||
@@ -812,6 +875,54 @@ namespace BizTalkPlatformManagementTool.Ui
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles selection of an existing JSON recovery snapshot.
|
||||
/// </summary>
|
||||
/// <param name="sender">The control that raised the event.</param>
|
||||
/// <param name="e">The event arguments.</param>
|
||||
private void StateBrowseButtonClick(object sender, EventArgs e)
|
||||
{
|
||||
using (var dialog = new OpenFileDialog())
|
||||
{
|
||||
dialog.Title = "Select preserved BizTalk state snapshot";
|
||||
dialog.Filter = "BizTalk snapshot (*.json)|*.json|All files (*.*)|*.*";
|
||||
dialog.CheckFileExists = true;
|
||||
dialog.Multiselect = false;
|
||||
var currentPath = ResolveStateFile(GetOptions());
|
||||
if (File.Exists(currentPath))
|
||||
{
|
||||
dialog.InitialDirectory = Path.GetDirectoryName(currentPath);
|
||||
dialog.FileName = Path.GetFileName(currentPath);
|
||||
}
|
||||
if (dialog.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
_stateFileTextBox.Text = dialog.FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the successful WMI-free state-file validation result to the operator.
|
||||
/// </summary>
|
||||
/// <param name="summary">The complete validation summary.</param>
|
||||
private void ShowStateValidationSummary(string summary)
|
||||
{
|
||||
Action show = () => MessageBox.Show(
|
||||
this,
|
||||
summary + "\n\nNo runtime state was changed and no WMI connection was opened.",
|
||||
"Recovery State Valid",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information);
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(show);
|
||||
}
|
||||
else
|
||||
{
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a UI update on the UI thread when required.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="2.2.0.0" name="BizTalkPlatformManagementTool" />
|
||||
<assemblyIdentity version="2.2.1.0" name="BizTalkPlatformManagementTool" />
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
Run("JsonRoundTripIsBomTolerantAndAtomic", JsonRoundTripIsBomTolerantAndAtomic);
|
||||
Run("DiffUsesApplicationAndNameIdentity", DiffUsesApplicationAndNameIdentity);
|
||||
Run("RestoreRejectsDifferentServer", RestoreRejectsDifferentServer);
|
||||
Run("Legacy213SnapshotIsAcceptedForRecovery", Legacy213SnapshotIsAcceptedForRecovery);
|
||||
Run("RestorePlanUsesSafeOrder", RestorePlanUsesSafeOrder);
|
||||
Run("ShutdownPlanUsesGlobalSafeOrder", ShutdownPlanUsesGlobalSafeOrder);
|
||||
Run("EmergencyRestorePlanStartsSsoFirst", EmergencyRestorePlanStartsSsoFirst);
|
||||
@@ -98,6 +99,25 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
Expect<InvalidOperationException>(() => SnapshotValidator.EnsureServerMatches(snapshot, "BIZTALK-B"));
|
||||
}
|
||||
|
||||
/// <summary>Prüft, dass ein erhaltener 2.1.3-Snapshot ohne Versionssperre als Recovery-Quelle dient.</summary>
|
||||
private static void Legacy213SnapshotIsAcceptedForRecovery()
|
||||
{
|
||||
InTemp(directory =>
|
||||
{
|
||||
var original = Snapshot("APP", "PORT", ArtifactStates.SendPortStarted);
|
||||
original.ToolVersion = "2.1.3-net461";
|
||||
original.Server = "BIZTALK-A.example.local";
|
||||
var path = Path.Combine(directory, "before.json");
|
||||
JsonFileStore.Save(path, original);
|
||||
|
||||
var loaded = JsonFileStore.Load<BizTalkSnapshot>(path);
|
||||
SnapshotValidator.EnsureServerMatches(loaded, "BIZTALK-A");
|
||||
var plan = new BizTalkOperationService(null).CreateEmergencyRestorePlan(loaded, "BIZTALK-A");
|
||||
Assert(plan.Steps.First().Kind == "WindowsService", "legacy snapshot did not create an emergency plan");
|
||||
Assert(loaded.ToolVersion == "2.1.3-net461", "legacy tool version was not preserved");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Prüft die sichere Restore-Reihenfolge und den Schutz gebundener Orchestrierungen.</summary>
|
||||
private static void RestorePlanUsesSafeOrder()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user