Harden BizTalk WMI monitoring
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31624.102
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizTalkCheckmkPulse", "src\BizTalkCheckmkPulse\BizTalkCheckmkPulse.csproj", "{A4D4D050-9EA7-4A71-B510-7D9D699B9F38}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizTalkCheckmkPulse.Tests", "tests\BizTalkCheckmkPulse.Tests\BizTalkCheckmkPulse.Tests.csproj", "{E219C45A-CC95-4F45-9B6F-8244CBAEE35A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -14,9 +16,12 @@ Global
|
||||
{A4D4D050-9EA7-4A71-B510-7D9D699B9F38}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A4D4D050-9EA7-4A71-B510-7D9D699B9F38}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A4D4D050-9EA7-4A71-B510-7D9D699B9F38}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E219C45A-CC95-4F45-9B6F-8244CBAEE35A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E219C45A-CC95-4F45-9B6F-8244CBAEE35A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E219C45A-CC95-4F45-9B6F-8244CBAEE35A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E219C45A-CC95-4F45-9B6F-8244CBAEE35A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
|
||||
+16
-9
@@ -100,7 +100,7 @@ BizTalkCheckmkPulse.exe
|
||||
Checkmk Local Check Zeilen nach STDOUT
|
||||
```
|
||||
|
||||
Der `.cmd`-Wrapper liefert auch dann eine gueltige `UNKNOWN`-Zeile mit Massnahme, wenn die EXE fehlt oder bereits der Prozessstart mit einem Exitcode fehlschlaegt. Die EXE selbst faengt Laufzeitfehler ab und schreibt fuer alle stabilen Services `UNKNOWN`, damit unvollstaendige Deployments oder WMI-Probleme in Checkmk sichtbar bleiben.
|
||||
Der `.cmd`-Wrapper liefert auch dann fuer alle sechs stabilen Services gueltige `UNKNOWN`-Zeilen mit Massnahme, wenn die EXE fehlt oder bereits der Prozessstart mit einem Exitcode fehlschlaegt. Die EXE selbst faengt Laufzeitfehler ab und schreibt ebenfalls fuer alle stabilen Services `UNKNOWN`, damit unvollstaendige Deployments oder WMI-Probleme in Checkmk sichtbar bleiben.
|
||||
|
||||
## Datenquellen
|
||||
|
||||
@@ -116,14 +116,15 @@ Genutzte Klassen:
|
||||
|
||||
| Klasse | Zweck |
|
||||
| --- | --- |
|
||||
| `MSBTS_GroupSetting` | BizTalk-Gruppe und Management-DB-Hinweise. |
|
||||
| `MSBTS_MessageBoxSetting` | MessageBox-DB-Hinweise. |
|
||||
| `MSBTS_HostInstance` | Host-Instance-Zustand. |
|
||||
| `MSBTS_GroupSetting` | BizTalk-Gruppe, Management-DB und Master-MessageBox (`SubscriptionDB*`). |
|
||||
| `MSBTS_HostInstance` | Host-Instance-Zustand; Ergebnis wird clientseitig auf den ueberwachten Server begrenzt. |
|
||||
| `MSBTS_ServiceInstance` | Suspended service instances. |
|
||||
| `MSBTS_ReceiveLocation` | Receive-Location-Zustand. |
|
||||
| `MSBTS_SendPort` | Send-Port-Zustand. |
|
||||
| `MSBTS_Orchestration` | Orchestration-Zustand. |
|
||||
| `MSBTS_ReceivePort` | Application-Mapping fuer Details. |
|
||||
| `MSBTS_ReceivePort` | Optionales Best-Effort-Application-Mapping, nur wenn per-Application-Services aktiviert sind. |
|
||||
|
||||
Die Plattformabfrage verwendet absichtlich keine vermeintliche Klasse `MSBTS_MessageBoxSetting`: Sie ist nicht Bestandteil des dokumentierten BizTalk-WMI-Schemas. Auch die vorhandene Klasse `MSBTS_MsgBoxSetting` ist fuer die Zielermittlung nicht erforderlich. `MSBTS_GroupSetting` liefert mit `SubscriptionDBServerName` und `SubscriptionDBName` bereits das Ziel der Master-MessageBox. Dadurch entfallen eine Providerabfrage und eine unnoetige Berechtigungs-/Schemaschnittstelle.
|
||||
|
||||
### Windows Application Event Log
|
||||
|
||||
@@ -139,7 +140,7 @@ Die Liste ist ueber `EventLogSources` konfigurierbar.
|
||||
|
||||
### SQL-Zugriffsprobe
|
||||
|
||||
Nach erfolgreicher oder teilweise erfolgreicher Plattformabfrage uebernimmt der SQL-Probe die per WMI ermittelten Management- und MessageBox-Ziele. Fuer jedes eindeutige Ziel wird mit `System.Data.SqlClient` eine Verbindung mit integrierter Windows-Authentifizierung geoeffnet und `SELECT 1` ausgefuehrt. Die Verbindung wird unmittelbar danach geschlossen; es werden keine BizTalk-Tabellen gelesen oder veraendert.
|
||||
Nach erfolgreicher oder teilweise erfolgreicher Plattformabfrage uebernimmt der SQL-Probe die per `MSBTS_GroupSetting` ermittelten Management- und Master-MessageBox-Ziele. Fuer jedes eindeutige Ziel wird mit `System.Data.SqlClient` eine Verbindung mit integrierter Windows-Authentifizierung geoeffnet und `SELECT 1` ausgefuehrt. Die Verbindung wird unmittelbar danach geschlossen; es werden keine BizTalk-Tabellen gelesen oder veraendert.
|
||||
|
||||
Der Test laeuft unter derselben Identitaet wie der Checkmk Local Check. Damit wird sichtbar, ob `LocalSystem` beziehungsweise das Maschinenkonto des BizTalk-Servers das SQL-Ziel tatsaechlich erreichen und die Datenbank oeffnen kann. Die Probe ist mit `ProbeSqlConnectivity=false` deaktivierbar und verwendet `SqlConnectionTimeoutSeconds` mit dem Default 5 Sekunden je Ziel als Timeout.
|
||||
|
||||
@@ -200,13 +201,14 @@ WMI-Verbindungsaufbau und jede erforderliche WMI-Klasse werden separat bewertet.
|
||||
| `Wmi/Permission` | Namespace oder BizTalk-WMI-Klasse verweigert den Zugriff. | Namespace-ACL bei Verbindungsfehlern beziehungsweise BizTalk-Operator-Gruppe bei Klassenfehlern pruefen. |
|
||||
| `Wmi/Connectivity` | WMI-/RPC-Ziel nicht erreichbar. | WMI-Dienst, Provider und bei Remote-WMI zusaetzlich DNS/RPC/Firewall pruefen. |
|
||||
| `Wmi/Timeout` | WMI-Abfrage ueberschreitet `QueryTimeoutSeconds`. | WMI-, BizTalk- und SQL-Auslastung untersuchen, bevor der Timeout erhoeht wird. |
|
||||
| `Wmi/Configuration` | Namespace oder Klasse fehlt. | BizTalk-WMI-Provider und BizTalk-Installation pruefen beziehungsweise reparieren. |
|
||||
| `Wmi/Configuration` | BizTalk-WMI-Namespace fehlt oder Plattformdaten sind unvollstaendig. | Provider, Namespace und BizTalk-Konfiguration pruefen. |
|
||||
| `Wmi/Schema` | `InvalidClass` oder `InvalidQuery`; Klasse beziehungsweise WQL passt nicht zum installierten Provider. | Klasse/Properties gegen das BizTalk-WMI-Schema pruefen; keine Rechteerhoehung vornehmen. |
|
||||
| `Sql/Permission` | Login oder Datenbankzugriff wird abgelehnt. | Maschinenkonto der BizTalk-Operator-Gruppe zuordnen, Kerberos erneuern, keine direkten DB-Rollen vergeben. |
|
||||
| `Sql/Connectivity` | SQL-Server oder Instanz nicht erreichbar. | Servername, DNS, SQL-Dienst, TCP-Protokoll, Port und Firewall pruefen. |
|
||||
| `Sql/Timeout` | SQL-Verbindung oder Testabfrage laeuft in den Timeout. | Netzwerk und SQL-Auslastung pruefen; Timeout nur begruendet anheben. |
|
||||
| `Sql/Configuration` | WMI-Ziele unvollstaendig oder TLS-, Zertifikats-, SPN-/SSPI-Konfiguration fehlerhaft. | Plattformabfragen beziehungsweise Zertifikatskette, Verschluesselung, SPN und Kerberos gezielt pruefen. |
|
||||
|
||||
Jede Diagnose enthaelt Bereich/Kategorie, betroffene Komponente, eine kurze Ursache, `Massnahme:` und `Technik:` mit Exception-Typ, HRESULT oder SQL-Fehlernummer. Erforderliche Datenquellen besitzen eigene Verfuegbarkeitsflags. Schlaegt beispielsweise `MSBTS_ServiceInstance` fehl, wird `BizTalk Suspended Instances` zwingend `UNKNOWN`; eine leere Ergebnisliste darf nicht als 'keine Suspensions' fehlinterpretiert werden.
|
||||
Jede Diagnose enthaelt Bereich/Kategorie, betroffene Komponente, eine kurze Ursache, `Massnahme:` und `Technik:` mit Exception-Typ, HRESULT oder SQL-Fehlernummer. Bei WMI-Abfragefehlern werden zusaetzlich WQL und Laufzeit bis zum Fehler ausgegeben. Erforderliche Datenquellen besitzen eigene Verfuegbarkeitsflags. Schlaegt beispielsweise `MSBTS_ServiceInstance` fehl, wird `BizTalk Suspended Instances` zwingend `UNKNOWN`; eine leere Ergebnisliste darf nicht als 'keine Suspensions' fehlinterpretiert werden.
|
||||
|
||||
Die Operator-Mitgliedschaft des Computerkontos steht allen auf diesem Server als `LocalSystem` laufenden Diensten fuer Netzwerkzugriffe zur Verfuegung. Falls diese Sicherheitsauswirkung nicht akzeptabel ist, kann ein separater Collector unter einem dedizierten gMSA- oder Dienstkonto mit Operator-Rechten Checkmk-Spooldaten erzeugen. Diese Variante ist noch nicht Bestandteil der aktuellen Implementierung. Der komplette Checkmk-Agent sollte nicht allein fuer dieses Plugin auf eine andere Identitaet umgestellt werden, weil dies alle Agent-Sektionen und Local Checks betrifft.
|
||||
|
||||
@@ -223,9 +225,13 @@ Quellen:
|
||||
|
||||
Das Plugin ist bewusst defensiv gebaut:
|
||||
|
||||
- Der dependency-freie Regressionstest laeuft mit dem gleichen .NET-Framework-/MSBuild-Baseline wie die Anwendung.
|
||||
- WMI-Queries haben ein konfigurierbares Timeout.
|
||||
- WMI-Namespace und Pflichtklassen werden getrennt auf Berechtigung, Konfiguration, Erreichbarkeit und Timeout geprueft.
|
||||
- Management- und MessageBox-Datenbank werden mit der echten Agent-Identitaet getestet.
|
||||
- Management- und Master-MessageBox-Datenbank werden aus einer dokumentierten WMI-Klasse ermittelt und mit der echten Agent-Identitaet getestet.
|
||||
- `InvalidClass`/`InvalidQuery` werden als Schemafehler statt als Berechtigungsproblem ausgewiesen.
|
||||
- Host Instances werden ohne namensabhaengigen WQL-Filter clientseitig auf den ueberwachten Server begrenzt.
|
||||
- Bereits fuer Runtime-Zustaende gelesene Artefakte werden fuer das Best-Effort-Application-Mapping wiederverwendet; doppelte WMI-Abfragen entfallen.
|
||||
- SQL-Fehlernummern werden in Berechtigung, Erreichbarkeit, Timeout oder Providerfehler klassifiziert.
|
||||
- Fehlgeschlagene Pflichtabfragen erzeugen `UNKNOWN` statt irrefuehrender Nullwerte.
|
||||
- Optionale WMI-Klassen erzeugen Diagnosehinweise statt Totalabbruch.
|
||||
@@ -338,6 +344,7 @@ Der Checkmk Windows Agent laeuft normalerweise als LocalSystem. Daher immer auch
|
||||
Die Diagnose im Service-Text nach `Massnahme:` abarbeiten. Wichtige Kategorien:
|
||||
|
||||
- `Wmi/Permission`: lokale Namespace-ACL oder BizTalk-Operator-Zuordnung pruefen.
|
||||
- `Wmi/Schema`: Klasse/WQL gegen das installierte BizTalk-Schema pruefen; `InvalidClass` ist nicht durch zusaetzliche Rechte loesbar.
|
||||
- `Sql/Permission`: Maschinenkonto `DOMAIN\BIZTALKSERVER$` und BizTalk-Operator-Gruppe pruefen.
|
||||
- `Sql/Connectivity`: SQL-Server-/Instanzname, DNS, SQL-Dienst, TCP und Firewall pruefen.
|
||||
- `Sql/Timeout`: SQL- und Netzwerkauslastung pruefen; Timeout nicht als erste Massnahme erhoehen.
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
Der Ansatz ist fuer Umgebungen wie `ACC`, `DEV`, `TST` und `PRD` gedacht, in denen pro Umgebung ein BizTalk Server 2020 und ein SQL Server betrieben werden. SQL Server bleibt beim mitgelieferten Checkmk-MSSQL-Plugin; dieses Projekt ergaenzt die BizTalk-spezifische Sicht.
|
||||
|
||||
Die Plattform- und SQL-Zielermittlung verwendet ausschliesslich die dokumentierte WMI-Klasse `MSBTS_GroupSetting`. Management-Datenbank und Master-MessageBox werden ueber `MgmtDbServerName`/`MgmtDbName` sowie `SubscriptionDBServerName`/`SubscriptionDBName` gelesen. Eine Klasse `MSBTS_MessageBoxSetting` gehoert nicht zum BizTalk-WMI-Schema und wird bewusst nicht abgefragt.
|
||||
|
||||
## Warum Local Check statt serverseitigem Check-Plugin?
|
||||
|
||||
Checkmk 2.4 kann Windows-Agent-Plugins und Local Checks direkt ausfuehren. Fuer diese Umgebung ist ein C#/.NET-Framework-Programm mit `.cmd`-Wrapper die robusteste Variante:
|
||||
@@ -34,7 +36,7 @@ Beispielausgaben stehen in [docs/ExampleOutput.md](docs/ExampleOutput.md).
|
||||
|
||||
Voraussetzungen auf einem Windows-Build-Host:
|
||||
|
||||
- Visual Studio 2022 Build Tools oder Visual Studio
|
||||
- Visual Studio 2019/2022 Build Tools oder Visual Studio
|
||||
- MSBuild im `PATH`
|
||||
- .NET Framework 4.7.2 Developer Pack
|
||||
|
||||
@@ -44,6 +46,12 @@ Build:
|
||||
scripts\build-release.cmd
|
||||
```
|
||||
|
||||
Build plus dependency-free regression tests:
|
||||
|
||||
```cmd
|
||||
scripts\test-release.cmd
|
||||
```
|
||||
|
||||
Deployment-Paket erstellen:
|
||||
|
||||
```cmd
|
||||
@@ -66,6 +74,8 @@ Validierung nach dem Build:
|
||||
artifacts\BizTalkCheckmkPulse-deploy\biztalk_checkmk_pulse.cmd --self-test
|
||||
```
|
||||
|
||||
Der Self-Test fuehrt keine WMI-, SQL- oder Event-Log-Abfrage aus und muss genau sechs `OK`-Zeilen liefern. Der Test-Runner prueft zusaetzlich UNKNOWN-Fallbacks, dynamische Application-Services, FQDN-/Kurznamensvergleich ohne WQL-Namensfilter und den dokumentierten `MSBTS_GroupSetting`-Queryvertrag. Die zusaetzliche lokale Verifikation mit Mono/MSBuild 16 prueft Build und Ausgabeformat, ersetzt aber nicht den Windows-/BizTalk-Test.
|
||||
|
||||
## Installation auf dem BizTalk-Server
|
||||
|
||||
Kopiere den Inhalt von `artifacts\BizTalkCheckmkPulse-deploy` nach:
|
||||
@@ -113,7 +123,7 @@ Wichtige Werte:
|
||||
| `ServicePrefix` | `BizTalk` | Prefix fuer alle Checkmk-Services. |
|
||||
| `EnvironmentName` | leer | Optional `ACC`, `DEV`, `TST` oder `PRD`. |
|
||||
| `QueryTimeoutSeconds` | `25` | WMI-Timeout pro Query. |
|
||||
| `ProbeSqlConnectivity` | `true` | Prueft die integrierte Windows-Anmeldung an den per WMI ermittelten Management- und MessageBox-Datenbanken. |
|
||||
| `ProbeSqlConnectivity` | `true` | Prueft die integrierte Windows-Anmeldung an der per WMI ermittelten Management- und Master-MessageBox-Datenbank. |
|
||||
| `SqlConnectionTimeoutSeconds` | `5` | Timeout je SQL-Ziel fuer Verbindungsaufbau und harmlose Testabfrage. |
|
||||
| `WarnResumableThreshold` | `1` | WARN ab n resumable suspended instances. |
|
||||
| `CritNonResumableThreshold` | `1` | CRIT ab n non-resumable suspended instances. |
|
||||
@@ -202,13 +212,15 @@ Erwartet werden die sechs stabilen `BizTalk ...` Services mit plausiblen Daten.
|
||||
Das Programm prueft Berechtigungen aktiv:
|
||||
|
||||
- Der WMI-Namespace-Verbindungsaufbau und jede erforderliche WMI-Klasse werden getrennt bewertet.
|
||||
- Fehler werden als `Permission`, `Connectivity`, `Timeout`, `Configuration` oder `Provider` klassifiziert.
|
||||
- `BizTalk SQL Access` oeffnet mit integrierter Windows-Authentifizierung eine Verbindung zur ermittelten Management- und MessageBox-Datenbank und fuehrt `SELECT 1` aus.
|
||||
- Fehler werden als `Permission`, `Connectivity`, `Timeout`, `Configuration`, `Schema` oder `Provider` klassifiziert.
|
||||
- `BizTalk SQL Access` oeffnet mit integrierter Windows-Authentifizierung eine Verbindung zur ermittelten Management- und Master-MessageBox-Datenbank und fuehrt `SELECT 1` aus.
|
||||
- Die Service-Ausgabe nennt Ausfuehrungsidentitaet, erwartete Netzwerkidentitaet, betroffene Komponente, technische Ursache und konkrete Massnahme.
|
||||
- Fehlgeschlagene Pflichtabfragen werden nie als leerer, erfolgreicher Datenbestand gewertet. Der betroffene Service wird `UNKNOWN`.
|
||||
|
||||
`Sql/Configuration` weist je nach Detailtext entweder auf eine unvollstaendige WMI-Zielermittlung oder auf TLS-, Zertifikats-, SPN-/SSPI-Probleme hin. Die Diagnose empfiehlt bewusst nicht, SQL-Verschluesselung pauschal abzuschalten.
|
||||
|
||||
`Wmi/Schema` kennzeichnet `InvalidClass` oder `InvalidQuery`. Das ist kein Berechtigungsfehler: Klassen- und Property-Namen muessen gegen das installierte BizTalk-WMI-Schema geprueft werden; eine Rechteerhoehung kann eine nicht vorhandene Klasse nicht erzeugen. Die technischen Details enthalten die ausgefuehrte WQL-Abfrage und deren Laufzeit bis zum Fehler.
|
||||
|
||||
Der SQL-Zugriffstest ersetzt nicht das Checkmk-MSSQL-Plugin auf dem SQL-Server. Er prueft ausschliesslich, ob genau die Identitaet des BizTalk Local Checks die fuer seine Diagnose benoetigten BizTalk-Datenbankziele erreichen kann.
|
||||
|
||||
Da bei fehlenden Rechten jeder Lauf einen abgewiesenen SQL-Login erzeugen kann, wird fuer den produktiven Betrieb eine asynchrone Ausfuehrung mit 300 Sekunden Cache empfohlen. Das begrenzt SQL-Logeintraege und Last, verzoegert einen Zustandswechsel aber um maximal fuenf Minuten. Beispiel in `%ProgramData%\checkmk\agent\check_mk.user.yml`:
|
||||
@@ -240,6 +252,7 @@ Entscheidungsmatrix:
|
||||
| --- | --- |
|
||||
| Alle BizTalk-Services liefern plausible Daten | Keine Berechtigungsaenderung erforderlich. |
|
||||
| Verbindung zu `root\MicrosoftBizTalkServer` scheitert | Namespace, BizTalk-WMI-Provider, WMI-Dienst und Namespace-ACL gezielt pruefen. Keine pauschalen WMI-Rechte vergeben. |
|
||||
| Service meldet `Wmi/Schema` beziehungsweise `InvalidClass`/`InvalidQuery` | WMI-Klasse und Properties gegen das BizTalk-Schema pruefen. Keine Berechtigungen erweitern. |
|
||||
| `BizTalk SQL Access` meldet `Sql/Permission` | Computerkonto `DOMAIN\BIZTALKSERVER$` in die konfigurierte BizTalk-Operator-Gruppe aufnehmen, Kerberos erneuern und erneut testen. Keine direkten BizTalk-DB-Rollen vergeben. |
|
||||
| `BizTalk SQL Access` meldet `Sql/Connectivity` oder `Sql/Timeout` | Server-/Instanzname, DNS, SQL-Dienst, TCP-Protokoll, Port und Firewall aus Sicht des BizTalk-Servers pruefen. |
|
||||
| Nur SQL-gestuetzte BizTalk-Klassen liefern `Access denied` oder `UNKNOWN` | Computerkonto `DOMAIN\BIZTALKSERVER$` zunaechst in `BizTalk Server Operators` aufnehmen. |
|
||||
@@ -270,6 +283,8 @@ Empfohlene Alarmierung:
|
||||
- Checkmk MKP-Pakete: https://docs.checkmk.com/latest/en/mkps.html
|
||||
- Microsoft BizTalk WMI `MSBTS_ServiceInstance`: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-serviceinstance-wmi
|
||||
- Microsoft BizTalk WMI `ServiceStatus`: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-serviceinstance-servicestatus-property-wmi
|
||||
- Microsoft BizTalk WMI `MSBTS_GroupSetting`: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-groupsetting-wmi
|
||||
- Microsoft BizTalk WMI Core Server Classes: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/core-server-classes
|
||||
- Microsoft BizTalk Mindestberechtigungen: https://learn.microsoft.com/en-us/biztalk/core/minimum-security-user-rights
|
||||
- Microsoft BizTalk Access Control: https://learn.microsoft.com/en-us/biztalk/core/access-control-and-data-security
|
||||
- Microsoft LocalSystem und Computerkonten: https://learn.microsoft.com/en-us/entra/architecture/service-accounts-computer
|
||||
|
||||
@@ -6,6 +6,11 @@ set "EXE=%BASE%\BizTalkCheckmkPulse.exe"
|
||||
|
||||
if not exist "%EXE%" (
|
||||
echo 3 "BizTalk Platform" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Deployment-Paket und Dateirechte pruefen und danach cmk-agent-ctl.exe dump erneut ausfuehren.
|
||||
echo 3 "BizTalk SQL Access" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Deployment-Paket und Dateirechte pruefen.
|
||||
echo 3 "BizTalk Suspended Instances" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Deployment-Paket und Dateirechte pruefen.
|
||||
echo 3 "BizTalk Host Instances" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Deployment-Paket und Dateirechte pruefen.
|
||||
echo 3 "BizTalk Runtime Artifacts" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Deployment-Paket und Dateirechte pruefen.
|
||||
echo 3 "BizTalk Event Log" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Deployment-Paket und Dateirechte pruefen.
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
@@ -13,5 +18,10 @@ if not exist "%EXE%" (
|
||||
set "EXITCODE=%ERRORLEVEL%"
|
||||
if not "%EXITCODE%"=="0" (
|
||||
echo 3 "BizTalk Platform" - BizTalkCheckmkPulse.exe konnte nicht erfolgreich gestartet werden, Exitcode=%EXITCODE%. Massnahme: .NET Framework 4.7.2, EXE/Config-Dateien und Windows Application Log pruefen.
|
||||
echo 3 "BizTalk SQL Access" - BizTalkCheckmkPulse.exe endete mit Exitcode=%EXITCODE%. Massnahme: .NET Framework 4.7.2, EXE/Config-Dateien und Windows Application Log pruefen.
|
||||
echo 3 "BizTalk Suspended Instances" - BizTalkCheckmkPulse.exe endete mit Exitcode=%EXITCODE%. Massnahme: Programmstart und Windows Application Log pruefen.
|
||||
echo 3 "BizTalk Host Instances" - BizTalkCheckmkPulse.exe endete mit Exitcode=%EXITCODE%. Massnahme: Programmstart und Windows Application Log pruefen.
|
||||
echo 3 "BizTalk Runtime Artifacts" - BizTalkCheckmkPulse.exe endete mit Exitcode=%EXITCODE%. Massnahme: Programmstart und Windows Application Log pruefen.
|
||||
echo 3 "BizTalk Event Log" - BizTalkCheckmkPulse.exe endete mit Exitcode=%EXITCODE%. Massnahme: Programmstart und Windows Application Log pruefen.
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
@@ -13,14 +13,14 @@ Die Ausgabe ist so gebaut, dass sie auch mit Checkmk 2.4.0p4 kompatibel bleibt:
|
||||
Zweck:
|
||||
|
||||
- Prueft, ob der BizTalk-WMI-Namespace erreichbar ist.
|
||||
- Zeigt Server, Gruppe, Management-DB und MessageBox-DB, soweit WMI diese Werte liefert.
|
||||
- Zeigt Server, Gruppe, Management-DB und Master-MessageBox-DB aus `MSBTS_GroupSetting`.
|
||||
|
||||
Status:
|
||||
|
||||
| Status | Bedingung |
|
||||
| --- | --- |
|
||||
| `OK` | WMI-Verbindung und beide Plattform-Pflichtklassen erfolgreich. |
|
||||
| `UNKNOWN` | Namespace, `MSBTS_GroupSetting`, `MSBTS_MessageBoxSetting` oder Programmstart fehlgeschlagen. |
|
||||
| `OK` | WMI-Verbindung erfolgreich und `MSBTS_GroupSetting` liefert beide vollstaendigen Datenbankziele. |
|
||||
| `UNKNOWN` | Namespace, `MSBTS_GroupSetting`, ein erforderliches Datenbankziel oder der Programmstart fehlgeschlagen. |
|
||||
|
||||
Metriken:
|
||||
|
||||
@@ -30,7 +30,7 @@ Metriken:
|
||||
|
||||
Zweck:
|
||||
|
||||
- Ermittelt Management- und MessageBox-Datenbank ueber BizTalk-WMI.
|
||||
- Ermittelt Management- und Master-MessageBox-Datenbank ueber `MSBTS_GroupSetting`.
|
||||
- Oeffnet mit der Windows-Identitaet des Checkmk-Agentprozesses eine integrierte SQL-Verbindung.
|
||||
- Fuehrt die harmlose Abfrage `SELECT 1` aus und schliesst die Verbindung sofort wieder.
|
||||
- Zeigt lokale Ausfuehrungsidentitaet und erwartete Netzwerkidentitaet an.
|
||||
@@ -93,6 +93,7 @@ Metriken:
|
||||
Zweck:
|
||||
|
||||
- Ueberwacht BizTalk Host Instances auf dem BizTalk-Server.
|
||||
- Filtert die gruppenweite WMI-Auflistung clientseitig auf den konfigurierten BizTalk-Server; Namen mit Sonderzeichen gelangen nicht in WQL.
|
||||
- Nicht gestartete Host Instances sind normalerweise betriebsrelevant.
|
||||
|
||||
Status:
|
||||
@@ -134,7 +135,7 @@ Metriken:
|
||||
|
||||
| Metrik | Bedeutung |
|
||||
| --- | --- |
|
||||
| `biztalk_applications` | Anzahl erkannter BizTalk-Anwendungen. |
|
||||
| `biztalk_applications` | Anzahl der per WMI erkannten Anwendungs-Buckets; `(unknown)` ist moeglich, da die dokumentierten Artefaktklassen keine verlaessliche Application-Property garantieren. |
|
||||
| `biztalk_receive_locations` | Gesamtzahl Receive Locations. |
|
||||
| `biztalk_receive_locations_disabled` | Deaktivierte Receive Locations. |
|
||||
| `biztalk_send_ports` | Gesamtzahl Send Ports. |
|
||||
@@ -171,10 +172,10 @@ Metriken:
|
||||
|
||||
## Optionale per-Application-Services
|
||||
|
||||
Wenn `EmitPerApplicationSuspensionServices=true` gesetzt ist, werden zusaetzliche Services fuer Anwendungen erzeugt, die suspendierte Instanzen haben:
|
||||
Wenn `EmitPerApplicationSuspensionServices=true` gesetzt ist, werden zusaetzliche Services fuer Anwendungen erzeugt, die suspendierte Instanzen haben und deren Anwendung per WMI tatsaechlich zugeordnet werden konnte:
|
||||
|
||||
```text
|
||||
BizTalk Suspended <ApplicationName>
|
||||
```
|
||||
|
||||
Das ist fuer Anwendungsteams hilfreich, kann aber die Service-Anzahl erhoehen. Fuer den Basisausbau wird empfohlen, diese Option deaktiviert zu lassen.
|
||||
Nicht zuordenbare Instanzen bleiben im stabilen Aggregat-Service enthalten; ein irrefuehrender per-Application-Service namens `(unknown)` wird nicht erzeugt. Die Option kann die Service-Anzahl erhoehen und bleibt fuer den Basisausbau deaktiviert.
|
||||
|
||||
+11
-2
@@ -6,13 +6,16 @@
|
||||
0 "BizTalk Platform" - Self test OK. Checkmk local check output is valid.
|
||||
0 "BizTalk SQL Access" biztalk_sql_targets_total=0;;;0 Self test OK. No SQL connection was opened.
|
||||
0 "BizTalk Suspended Instances" biztalk_suspended_total=0;;;0 Self test OK.
|
||||
0 "BizTalk Host Instances" biztalk_host_instances_total=0;;;0 Self test OK. No WMI query was executed.
|
||||
0 "BizTalk Runtime Artifacts" biztalk_applications=0;;;0 Self test OK. No WMI query was executed.
|
||||
0 "BizTalk Event Log" biztalk_eventlog_errors=0;;;0 Self test OK. No event log was read.
|
||||
```
|
||||
|
||||
## Gesunder BizTalk-Server
|
||||
|
||||
```text
|
||||
0 "BizTalk Platform" - BizTalk WMI and platform data reachable, server=BIZTALK01, group=BizTalkMgmtDb, mgmt_db=SQL01\BizTalkMgmtDb, msgbox_db=SQL01\BizTalkMsgBoxDb
|
||||
0 "BizTalk SQL Access" biztalk_sql_targets_total=2;;;0|biztalk_sql_targets_available=2;;;0|biztalk_sql_targets_failed=0;;1;0 SQL access targets=2, available=2, discovery_complete=True, execution_identity=NT AUTHORITY\SYSTEM, network_identity=<DOMAIN>\BIZTALK01$; databases=Management=SQL01\BizTalkMgmtDb:OK, MessageBox=SQL01\BizTalkMsgBoxDb:OK
|
||||
0 "BizTalk SQL Access" biztalk_sql_targets_total=2;;;0|biztalk_sql_targets_available=2;;;0|biztalk_sql_targets_failed=0;;1;0 SQL access targets=2, available=2, discovery_complete=True, execution_identity=NT AUTHORITY\SYSTEM, network_identity=<DOMAIN>\BIZTALK01$; databases=Management=SQL01\BizTalkMgmtDb:OK, MasterMessageBox=SQL01\BizTalkMsgBoxDb:OK
|
||||
0 "BizTalk Suspended Instances" biztalk_suspended_total=0;;;0|biztalk_suspended_resumable=0;1;;0|biztalk_suspended_nonresumable=0;;1;0 No suspended BizTalk service instances found.
|
||||
0 "BizTalk Host Instances" biztalk_host_instances_total=4;;;0|biztalk_host_instances_started=4;;;0|biztalk_host_instances_stopped=0;;1;0|biztalk_host_instances_pending=0;1;;0|biztalk_host_instances_unknown=0;;1;0 Host instances total=4, started=4, stopped=0, pending=0, unknown=0
|
||||
0 "BizTalk Runtime Artifacts" biztalk_applications=8;;;0|biztalk_receive_locations=24;;;0|biztalk_receive_locations_disabled=0;1;;0|biztalk_send_ports=31;;;0|biztalk_send_ports_started=31;;;0|biztalk_send_ports_inactive=0;1;;0|biztalk_send_ports_unknown=0;;1;0|biztalk_orchestrations=12;;;0|biztalk_orchestrations_started=12;;;0|biztalk_orchestrations_inactive=0;1;;0|biztalk_orchestrations_unknown=0;;1;0 Applications=8; receive_locations total=24, disabled=0; send_ports total=31, started=31, inactive=0, unknown=0; orchestrations total=12, started=12, inactive=0, unknown=0
|
||||
@@ -28,7 +31,7 @@
|
||||
## SQL-Berechtigungsfehler
|
||||
|
||||
```text
|
||||
3 "BizTalk SQL Access" biztalk_sql_targets_total=2;;;0|biztalk_sql_targets_available=0;;;0|biztalk_sql_targets_failed=2;;1;0 SQL access targets=2, available=0, discovery_complete=True, execution_identity=NT AUTHORITY\SYSTEM, network_identity=<DOMAIN>\BIZTALK01$; databases=Management=SQL01\BizTalkMgmtDb:FAILED, MessageBox=SQL01\BizTalkMsgBoxDb:FAILED; diagnostics: Sql/Permission [Management SQL01\BizTalkMgmtDb]: SQL Server hat die integrierte Anmeldung abgelehnt. Erwartete Netzwerkidentitaet: <DOMAIN>\BIZTALK01$. Massnahme: <DOMAIN>\BIZTALK01$ in die konfigurierte BizTalk-Operator-Gruppe aufnehmen; keine direkten BizTalk-DB-Rollen vergeben. Danach Kerberos-Tickets erneuern und den Agent-Dump wiederholen. Technik: SqlException Number=18456: Login failed for user
|
||||
3 "BizTalk SQL Access" biztalk_sql_targets_total=2;;;0|biztalk_sql_targets_available=0;;;0|biztalk_sql_targets_failed=2;;1;0 SQL access targets=2, available=0, discovery_complete=True, execution_identity=NT AUTHORITY\SYSTEM, network_identity=<DOMAIN>\BIZTALK01$; databases=Management=SQL01\BizTalkMgmtDb:FAILED, MasterMessageBox=SQL01\BizTalkMsgBoxDb:FAILED; diagnostics: Sql/Permission [Management SQL01\BizTalkMgmtDb]: SQL Server hat die integrierte Anmeldung abgelehnt. Erwartete Netzwerkidentitaet: <DOMAIN>\BIZTALK01$. Massnahme: <DOMAIN>\BIZTALK01$ in die konfigurierte BizTalk-Operator-Gruppe aufnehmen; keine direkten BizTalk-DB-Rollen vergeben. Danach Kerberos-Tickets erneuern und den Agent-Dump wiederholen. Technik: SqlException Number=18456: Login failed for user
|
||||
```
|
||||
|
||||
## WMI-Berechtigungsfehler
|
||||
@@ -37,6 +40,12 @@
|
||||
3 "BizTalk Suspended Instances" - Suspendierte Instanzen konnten nicht verlaesslich gelesen werden. Ein leerer Datenbestand wird nicht als OK gewertet.; diagnostics: Wmi/Permission [MSBTS_ServiceInstance]: Zugriff auf MSBTS_ServiceInstance wurde im LocalSystem-Kontext verweigert. Massnahme: Agent-Dump pruefen und Computerkonto <DOMAIN>\BIZTALK01$ zunaechst der konfigurierten BizTalk-Operator-Gruppe zuordnen. Technik: UnauthorizedAccessException HRESULT=0x80070005: Access is denied
|
||||
```
|
||||
|
||||
## WMI-Schemafehler
|
||||
|
||||
```text
|
||||
3 "BizTalk Runtime Artifacts" - BizTalk Runtime Artifacts konnten nicht vollstaendig gelesen werden. Teilwerte werden nicht als OK gewertet.; diagnostics: Wmi/Schema [MSBTS_Orchestration]: WMI-Klasse oder WQL fuer MSBTS_Orchestration wird vom installierten BizTalk-Provider nicht unterstuetzt. Massnahme: Klassen- und Property-Namen gegen das installierte BizTalk-WMI-Schema und die Microsoft-Dokumentation pruefen. Rechteerhoehung behebt InvalidClass/InvalidQuery nicht. Technik: ManagementException HRESULT=0x80041010: Invalid class WQL={SELECT * FROM MSBTS_Orchestration} DurationMs=18
|
||||
```
|
||||
|
||||
## Fehlendes Deployment
|
||||
|
||||
```text
|
||||
|
||||
+4
-1
@@ -97,7 +97,7 @@ Der direkte Start des Wrappers in einer administrativen Eingabeaufforderung ist
|
||||
|
||||
Die Ausgabe muss die sechs stabilen `BizTalk ...` Services mit plausiblen Daten enthalten. `Access denied`, `UnauthorizedAccessException`, `Sql/Permission` und ein berechtigungsbedingtes `UNKNOWN` weisen auf eine fehlende Rollenzuordnung hin.
|
||||
|
||||
Die EXE klassifiziert WMI- und SQL-Probleme und schreibt zu jeder Diagnose `Massnahme:` und `Technik:`. Eine fehlgeschlagene erforderliche WMI-Abfrage erzeugt beim betroffenen Service immer `UNKNOWN`; fehlende Daten werden nicht als Nullbestand und damit nicht als `OK` ausgegeben.
|
||||
Die EXE klassifiziert WMI- und SQL-Probleme und schreibt zu jeder Diagnose `Massnahme:` und `Technik:`. WMI-Schemafehler (`InvalidClass`/`InvalidQuery`) werden separat als `Wmi/Schema` ausgewiesen und duerfen nicht durch Rechteerweiterungen behandelt werden. Eine fehlgeschlagene erforderliche WMI-Abfrage erzeugt beim betroffenen Service immer `UNKNOWN`; fehlende Daten werden nicht als Nullbestand und damit nicht als `OK` ausgegeben.
|
||||
|
||||
Fuer den produktiven Betrieb wird empfohlen, den Local Check alle 300 Sekunden asynchron auszufuehren. Dadurch erzeugen fehlende SQL-Rechte nicht bei jedem Agent-Abruf neue fehlgeschlagene Logins. In `%ProgramData%\checkmk\agent\check_mk.user.yml`:
|
||||
|
||||
@@ -130,6 +130,7 @@ Entscheidungsmatrix:
|
||||
| --- | --- |
|
||||
| Alle sechs stabilen Services liefern plausible Werte | Keine Aenderung erforderlich. |
|
||||
| `root\MicrosoftBizTalkServer` ist nicht erreichbar | BizTalk-WMI-Provider, WMI-Dienst, Namespace und dessen ACL gezielt pruefen. |
|
||||
| `Wmi/Schema` beziehungsweise `InvalidClass`/`InvalidQuery` | Klassen- und Property-Namen gegen das installierte BizTalk-WMI-Schema pruefen; keine Berechtigungen erweitern. |
|
||||
| `BizTalk SQL Access` meldet `Sql/Permission` | Maschinenkonto in die konfigurierte BizTalk-Operator-Gruppe aufnehmen, Kerberos erneuern und Agent-Dump wiederholen. |
|
||||
| `BizTalk SQL Access` meldet `Sql/Connectivity` | SQL-Server-/Instanzname, DNS, Dienst, TCP-Port und Firewall pruefen. |
|
||||
| `BizTalk SQL Access` meldet `Sql/Timeout` | SQL-/Netzwerkauslastung untersuchen; Timeout nur nach Ursachenanalyse anpassen. |
|
||||
@@ -149,6 +150,8 @@ Quellen:
|
||||
- Checkmk Local Checks: https://docs.checkmk.com/latest/en/localchecks.html
|
||||
- BizTalk Minimum Security User Rights: https://learn.microsoft.com/en-us/biztalk/core/minimum-security-user-rights
|
||||
- BizTalk Access Control and Data Security: https://learn.microsoft.com/en-us/biztalk/core/access-control-and-data-security
|
||||
- BizTalk `MSBTS_GroupSetting`: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-groupsetting-wmi
|
||||
- BizTalk WMI Core Server Classes: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/core-server-classes
|
||||
- Microsoft LocalSystem und Computerkonten: https://learn.microsoft.com/en-us/entra/architecture/service-accounts-computer
|
||||
- Microsoft WMI Namespace Security: https://learn.microsoft.com/en-us/windows/win32/wmisdk/access-to-wmi-namespaces
|
||||
|
||||
|
||||
@@ -7,6 +7,5 @@ if errorlevel 1 (
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
msbuild BizTalkCheckmkPulse.sln /p:Configuration=Release /p:Platform="Any CPU" /m /v:minimal
|
||||
msbuild BizTalkCheckmkPulse.sln /p:Configuration=Release /p:Platform="Any CPU" /m:1 /v:minimal
|
||||
exit /b %ERRORLEVEL%
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
call "%~dp0build-release.cmd"
|
||||
if errorlevel 1 exit /b 1
|
||||
|
||||
"%~dp0..\tests\BizTalkCheckmkPulse.Tests\bin\Release\BizTalkCheckmkPulse.Tests.exe"
|
||||
exit /b %ERRORLEVEL%
|
||||
@@ -40,6 +40,7 @@
|
||||
<Reference Include="System.Management" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="CheckmkLocalFormatter.cs" />
|
||||
<Compile Include="EventLogProbe.cs" />
|
||||
<Compile Include="MonitoringOptions.cs" />
|
||||
|
||||
@@ -62,6 +62,9 @@ namespace BizTalkCheckmkPulse
|
||||
yield return BuildLine(CheckState.Ok, _options.ServiceName("Platform"), "-", "Self test OK. Checkmk local check output is valid.");
|
||||
yield return BuildLine(CheckState.Ok, _options.ServiceName("SQL Access"), "biztalk_sql_targets_total=0;;;0", "Self test OK. No SQL connection was opened.");
|
||||
yield return BuildLine(CheckState.Ok, _options.ServiceName("Suspended Instances"), "biztalk_suspended_total=0;;;0", "Self test OK.");
|
||||
yield return BuildLine(CheckState.Ok, _options.ServiceName("Host Instances"), "biztalk_host_instances_total=0;;;0", "Self test OK. No WMI query was executed.");
|
||||
yield return BuildLine(CheckState.Ok, _options.ServiceName("Runtime Artifacts"), "biztalk_applications=0;;;0", "Self test OK. No WMI query was executed.");
|
||||
yield return BuildLine(CheckState.Ok, _options.ServiceName("Event Log"), "biztalk_eventlog_errors=0;;;0", "Self test OK. No event log was read.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -96,7 +99,7 @@ namespace BizTalkCheckmkPulse
|
||||
AppendOptional(detail, "mgmt_db", JoinDb(result.Platform.ManagementDbServer, result.Platform.ManagementDbName));
|
||||
AppendOptional(detail, "msgbox_db", JoinDb(result.Platform.MessageBoxDbServer, result.Platform.MessageBoxDbName));
|
||||
|
||||
var diagnostics = SelectDiagnostics(result, DiagnosticArea.Wmi, "WMI namespace connection", "MSBTS_GroupSetting", "MSBTS_MessageBoxSetting");
|
||||
var diagnostics = SelectDiagnostics(result, DiagnosticArea.Wmi, "WMI namespace connection", "MSBTS_GroupSetting");
|
||||
return BuildLine(state, _options.ServiceName("Platform"), "-", AppendDiagnostics(detail.ToString(), diagnostics));
|
||||
}
|
||||
|
||||
@@ -334,7 +337,10 @@ namespace BizTalkCheckmkPulse
|
||||
/// <returns>Checkmk-Zeilen je betroffener BizTalk-Anwendung.</returns>
|
||||
private IEnumerable<string> FormatApplicationSuspensions(ProbeResult result)
|
||||
{
|
||||
foreach (var group in result.SuspendedInstances.GroupBy(x => x.ApplicationName).OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase))
|
||||
foreach (var group in result.SuspendedInstances
|
||||
.Where(x => !string.Equals(x.ApplicationName, "(unknown)", StringComparison.OrdinalIgnoreCase))
|
||||
.GroupBy(x => x.ApplicationName)
|
||||
.OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
var resumable = group.Count(x => x.Kind == SuspendedKind.Resumable);
|
||||
var nonresumable = group.Count(x => x.Kind == SuspendedKind.NonResumable);
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace BizTalkCheckmkPulse
|
||||
Connectivity,
|
||||
Timeout,
|
||||
Configuration,
|
||||
Schema,
|
||||
Provider,
|
||||
Unexpected
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("BizTalkCheckmkPulse.Tests")]
|
||||
@@ -35,7 +35,7 @@ namespace BizTalkCheckmkPulse
|
||||
}
|
||||
|
||||
AddTarget(result, "Management", result.Platform.ManagementDbServer, result.Platform.ManagementDbName);
|
||||
AddTarget(result, "MessageBox", result.Platform.MessageBoxDbServer, result.Platform.MessageBoxDbName);
|
||||
AddTarget(result, "MasterMessageBox", result.Platform.MessageBoxDbServer, result.Platform.MessageBoxDbName);
|
||||
|
||||
if (!result.Platform.PlatformDataAvailable)
|
||||
{
|
||||
@@ -46,7 +46,7 @@ namespace BizTalkCheckmkPulse
|
||||
Component = "BizTalk database discovery",
|
||||
Required = true,
|
||||
Summary = "SQL-Zielermittlung ist unvollstaendig, weil nicht alle erforderlichen BizTalk-Plattformklassen gelesen wurden.",
|
||||
Action = "Zuerst den Service 'BizTalk Platform' sowie MSBTS_GroupSetting und MSBTS_MessageBoxSetting pruefen. Danach den Agent-Dump erneut ausfuehren."
|
||||
Action = "Zuerst den Service 'BizTalk Platform' und MSBTS_GroupSetting pruefen. Danach den Agent-Dump erneut ausfuehren."
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -23,6 +24,8 @@ namespace BizTalkCheckmkPulse
|
||||
private const int OrchestrationStopped = 3;
|
||||
private const int OrchestrationStarted = 4;
|
||||
private const int EAccessDenied = unchecked((int)0x80070005);
|
||||
internal const string GroupSettingQuery =
|
||||
"SELECT Name, MgmtDbServerName, MgmtDbName, SubscriptionDBServerName, SubscriptionDBName FROM MSBTS_GroupSetting";
|
||||
private readonly MonitoringOptions _options;
|
||||
|
||||
/// <summary>
|
||||
@@ -53,9 +56,14 @@ namespace BizTalkCheckmkPulse
|
||||
|
||||
result.Platform.WmiConnected = true;
|
||||
result.Platform.PlatformDataAvailable = QueryPlatform(scope, result);
|
||||
result.Platform.HostInstancesDataAvailable = QueryHostInstances(scope, result);
|
||||
var applicationIndex = BuildApplicationIndex(scope, result);
|
||||
result.Platform.RuntimeArtifactsDataAvailable = QueryApplicationRuntime(scope, result);
|
||||
result.Platform.HostInstancesDataAvailable = QueryHostInstances(scope, result, server);
|
||||
var applicationIndex = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
result.Platform.RuntimeArtifactsDataAvailable = QueryApplicationRuntime(scope, result, applicationIndex);
|
||||
if (_options.EmitPerApplicationSuspensionServices)
|
||||
{
|
||||
AddArtifactIndex(scope, result, applicationIndex, "MSBTS_ReceivePort");
|
||||
}
|
||||
|
||||
result.Platform.SuspendedInstancesDataAvailable = QuerySuspendedInstances(scope, result, applicationIndex);
|
||||
}
|
||||
|
||||
@@ -74,7 +82,7 @@ namespace BizTalkCheckmkPulse
|
||||
}
|
||||
catch (Exception ex) when (IsExpectedWmiException(ex))
|
||||
{
|
||||
AddWmiDiagnostic(result, "WMI namespace connection", ex, true, true);
|
||||
AddWmiDiagnostic(result, "WMI namespace connection", ex, true, true, null, null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -88,23 +96,19 @@ namespace BizTalkCheckmkPulse
|
||||
private bool QueryPlatform(ManagementScope scope, ProbeResult result)
|
||||
{
|
||||
var groupFound = false;
|
||||
var groupAvailable = TryQuery(scope, result, "MSBTS_GroupSetting", "SELECT * FROM MSBTS_GroupSetting", item =>
|
||||
var groupAvailable = TryQuery(scope, result, "MSBTS_GroupSetting", GroupSettingQuery, item =>
|
||||
{
|
||||
if (groupFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
groupFound = true;
|
||||
result.Platform.GroupName = FirstNonEmpty(WmiHelpers.GetString(item, "Name"), WmiHelpers.GetString(item, "MgmtDbName"));
|
||||
result.Platform.ManagementDbServer = FirstNonEmpty(WmiHelpers.GetString(item, "MgmtDbServerName"), WmiHelpers.GetString(item, "DBServerName"));
|
||||
result.Platform.ManagementDbName = FirstNonEmpty(WmiHelpers.GetString(item, "MgmtDbName"), WmiHelpers.GetString(item, "DatabaseName"));
|
||||
}, true);
|
||||
|
||||
var messageBoxFound = false;
|
||||
var messageBoxAvailable = TryQuery(scope, result, "MSBTS_MessageBoxSetting", "SELECT * FROM MSBTS_MessageBoxSetting", item =>
|
||||
{
|
||||
messageBoxFound = true;
|
||||
if (string.IsNullOrWhiteSpace(result.Platform.MessageBoxDbServer))
|
||||
{
|
||||
result.Platform.MessageBoxDbServer = FirstNonEmpty(WmiHelpers.GetString(item, "DBServerName"), WmiHelpers.GetString(item, "ServerName"));
|
||||
result.Platform.MessageBoxDbName = FirstNonEmpty(WmiHelpers.GetString(item, "DBName"), WmiHelpers.GetString(item, "DatabaseName"));
|
||||
}
|
||||
result.Platform.ManagementDbServer = WmiHelpers.GetString(item, "MgmtDbServerName");
|
||||
result.Platform.ManagementDbName = WmiHelpers.GetString(item, "MgmtDbName");
|
||||
result.Platform.MessageBoxDbServer = WmiHelpers.GetString(item, "SubscriptionDBServerName");
|
||||
result.Platform.MessageBoxDbName = WmiHelpers.GetString(item, "SubscriptionDBName");
|
||||
}, true);
|
||||
|
||||
if (groupAvailable && !groupFound)
|
||||
@@ -112,32 +116,33 @@ namespace BizTalkCheckmkPulse
|
||||
AddMissingDataDiagnostic(result, "MSBTS_GroupSetting", "Keine BizTalk-Gruppe gefunden.");
|
||||
}
|
||||
|
||||
if (messageBoxAvailable && !messageBoxFound)
|
||||
var managementTargetComplete = HasDatabaseTarget(
|
||||
result.Platform.ManagementDbServer,
|
||||
result.Platform.ManagementDbName);
|
||||
if (groupAvailable && groupFound && !managementTargetComplete)
|
||||
{
|
||||
AddMissingDataDiagnostic(result, "MSBTS_MessageBoxSetting", "Keine BizTalk-MessageBox-Datenbank gefunden.");
|
||||
AddMissingDataDiagnostic(
|
||||
result,
|
||||
"MSBTS_GroupSetting.ManagementDatabase",
|
||||
"BizTalk Management-Datenbankziel ist in MSBTS_GroupSetting unvollstaendig.");
|
||||
}
|
||||
|
||||
return groupAvailable && groupFound && messageBoxAvailable && messageBoxFound;
|
||||
var messageBoxTargetComplete = HasDatabaseTarget(
|
||||
result.Platform.MessageBoxDbServer,
|
||||
result.Platform.MessageBoxDbName);
|
||||
if (groupAvailable && groupFound && !messageBoxTargetComplete)
|
||||
{
|
||||
AddMissingDataDiagnostic(
|
||||
result,
|
||||
"MSBTS_GroupSetting.SubscriptionDatabase",
|
||||
"Master-MessageBox-Datenbankziel ist in MSBTS_GroupSetting unvollstaendig.");
|
||||
}
|
||||
|
||||
return groupAvailable && groupFound && managementTargetComplete && messageBoxTargetComplete;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erstellt einen Index, um suspendierte Instanzen BizTalk-Anwendungen zuzuordnen.
|
||||
/// </summary>
|
||||
/// <param name="scope">Verbundener BizTalk-WMI-Scope.</param>
|
||||
/// <param name="result">Ergebnisobjekt fuer optionale Diagnosen.</param>
|
||||
/// <returns>Index aus moeglichen Artefaktschluesseln und Anwendungsnamen.</returns>
|
||||
private Dictionary<string, string> BuildApplicationIndex(ManagementScope scope, ProbeResult result)
|
||||
{
|
||||
var index = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
AddArtifactIndex(scope, result, index, "MSBTS_SendPort");
|
||||
AddArtifactIndex(scope, result, index, "MSBTS_ReceivePort");
|
||||
AddArtifactIndex(scope, result, index, "MSBTS_ReceiveLocation");
|
||||
AddArtifactIndex(scope, result, index, "MSBTS_Orchestration");
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fuegt die Schluessel einer WMI-Artefaktklasse zum Anwendungsindex hinzu.
|
||||
/// Fuegt die Schluessel einer optionalen WMI-Artefaktklasse zum Anwendungsindex hinzu.
|
||||
/// </summary>
|
||||
/// <param name="scope">Verbundener BizTalk-WMI-Scope.</param>
|
||||
/// <param name="result">Ergebnisobjekt fuer optionale Diagnosen.</param>
|
||||
@@ -147,14 +152,7 @@ namespace BizTalkCheckmkPulse
|
||||
{
|
||||
TryQuery(scope, result, className + " application index", "SELECT * FROM " + className, item =>
|
||||
{
|
||||
var app = GetApplicationName(item);
|
||||
foreach (var key in CandidateKeys(item))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(key) && !index.ContainsKey(key))
|
||||
{
|
||||
index.Add(key, app);
|
||||
}
|
||||
}
|
||||
AddArtifactIndexEntry(index, item);
|
||||
}, false);
|
||||
}
|
||||
|
||||
@@ -163,16 +161,28 @@ namespace BizTalkCheckmkPulse
|
||||
/// </summary>
|
||||
/// <param name="scope">Verbundener BizTalk-WMI-Scope.</param>
|
||||
/// <param name="result">Zu aktualisierendes Ergebnisobjekt.</param>
|
||||
/// <param name="server">BizTalk-Server, dessen Host Instances bewertet werden.</param>
|
||||
/// <returns><c>true</c>, wenn die Pflichtabfrage erfolgreich war.</returns>
|
||||
private bool QueryHostInstances(ManagementScope scope, ProbeResult result)
|
||||
private bool QueryHostInstances(ManagementScope scope, ProbeResult result, string server)
|
||||
{
|
||||
return TryQuery(scope, result, "MSBTS_HostInstance", "SELECT * FROM MSBTS_HostInstance", item =>
|
||||
return TryQuery(
|
||||
scope,
|
||||
result,
|
||||
"MSBTS_HostInstance",
|
||||
"SELECT Name, HostName, RunningServer, ServiceState FROM MSBTS_HostInstance",
|
||||
item =>
|
||||
{
|
||||
var runningServer = WmiHelpers.GetString(item, "RunningServer");
|
||||
if (!string.IsNullOrWhiteSpace(runningServer) && !IsSameServer(runningServer, server))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
result.HostInstances.Add(new HostInstanceState
|
||||
{
|
||||
InstanceName = FirstNonEmpty(WmiHelpers.GetString(item, "InstanceName"), WmiHelpers.GetString(item, "Name")),
|
||||
HostName = WmiHelpers.GetString(item, "HostName"),
|
||||
RunningServer = WmiHelpers.GetString(item, "RunningServer"),
|
||||
RunningServer = runningServer,
|
||||
ServiceState = WmiHelpers.GetInt32(item, "ServiceState", 0)
|
||||
});
|
||||
}, true);
|
||||
@@ -183,13 +193,18 @@ namespace BizTalkCheckmkPulse
|
||||
/// </summary>
|
||||
/// <param name="scope">Verbundener BizTalk-WMI-Scope.</param>
|
||||
/// <param name="result">Zu aktualisierendes Ergebnisobjekt.</param>
|
||||
/// <param name="applicationIndex">Best-Effort-Index fuer Suspensionsdetails.</param>
|
||||
/// <returns><c>true</c>, wenn alle drei Pflichtabfragen erfolgreich waren.</returns>
|
||||
private bool QueryApplicationRuntime(ManagementScope scope, ProbeResult result)
|
||||
private bool QueryApplicationRuntime(
|
||||
ManagementScope scope,
|
||||
ProbeResult result,
|
||||
Dictionary<string, string> applicationIndex)
|
||||
{
|
||||
var apps = new Dictionary<string, ApplicationRuntimeState>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var receiveLocationsAvailable = TryQuery(scope, result, "MSBTS_ReceiveLocation", "SELECT * FROM MSBTS_ReceiveLocation", item =>
|
||||
{
|
||||
AddArtifactIndexEntry(applicationIndex, item);
|
||||
var app = GetApplication(apps, GetApplicationName(item));
|
||||
app.ReceiveLocationTotal++;
|
||||
if (WmiHelpers.GetBoolean(item, "IsDisabled", false))
|
||||
@@ -200,6 +215,7 @@ namespace BizTalkCheckmkPulse
|
||||
|
||||
var sendPortsAvailable = TryQuery(scope, result, "MSBTS_SendPort", "SELECT * FROM MSBTS_SendPort", item =>
|
||||
{
|
||||
AddArtifactIndexEntry(applicationIndex, item);
|
||||
var app = GetApplication(apps, GetApplicationName(item));
|
||||
app.SendPortTotal++;
|
||||
switch (WmiHelpers.GetInt32(item, "Status", 0))
|
||||
@@ -221,6 +237,7 @@ namespace BizTalkCheckmkPulse
|
||||
|
||||
var orchestrationsAvailable = TryQuery(scope, result, "MSBTS_Orchestration", "SELECT * FROM MSBTS_Orchestration", item =>
|
||||
{
|
||||
AddArtifactIndexEntry(applicationIndex, item);
|
||||
var app = GetApplication(apps, GetApplicationName(item));
|
||||
app.OrchestrationTotal++;
|
||||
switch (WmiHelpers.GetInt32(item, "OrchestrationStatus", 0))
|
||||
@@ -285,6 +302,7 @@ namespace BizTalkCheckmkPulse
|
||||
/// <returns><c>true</c>, wenn die Abfrage vollstaendig ausgefuehrt wurde.</returns>
|
||||
private bool TryQuery(ManagementScope scope, ProbeResult result, string label, string queryText, Action<ManagementObject> action, bool required)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
ForEachObject(scope, queryText, action);
|
||||
@@ -292,7 +310,7 @@ namespace BizTalkCheckmkPulse
|
||||
}
|
||||
catch (Exception ex) when (IsExpectedWmiException(ex))
|
||||
{
|
||||
AddWmiDiagnostic(result, label, ex, required, false);
|
||||
AddWmiDiagnostic(result, label, ex, required, false, queryText, stopwatch.Elapsed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -344,6 +362,28 @@ namespace BizTalkCheckmkPulse
|
||||
return app;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Nimmt nur tatsaechlich vorhandene Anwendungszuordnungen in den Best-Effort-Index auf.
|
||||
/// </summary>
|
||||
/// <param name="index">Zu erweiternder Index.</param>
|
||||
/// <param name="item">BizTalk-Artefakt aus WMI.</param>
|
||||
private static void AddArtifactIndexEntry(Dictionary<string, string> index, ManagementBaseObject item)
|
||||
{
|
||||
var app = GetApplicationName(item);
|
||||
if (string.Equals(app, UnknownApplication, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var key in CandidateKeys(item))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(key) && !index.ContainsKey(key))
|
||||
{
|
||||
index.Add(key, app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ermittelt den Anwendungsnamen direkt oder ueber den Artefaktindex.
|
||||
/// </summary>
|
||||
@@ -406,6 +446,32 @@ namespace BizTalkCheckmkPulse
|
||||
return values == null ? null : values.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prueft, ob Server und Datenbank eines SQL-Ziels vorhanden sind.
|
||||
/// </summary>
|
||||
private static bool HasDatabaseTarget(string server, string database)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(server) && !string.IsNullOrWhiteSpace(database);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Vergleicht kurzen Namen und FQDN eines BizTalk-Servers ohne WQL-Namensfilter.
|
||||
/// </summary>
|
||||
internal static bool IsSameServer(string left, string right)
|
||||
{
|
||||
return string.Equals(
|
||||
NormalizeServerName(left),
|
||||
NormalizeServerName(right),
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string NormalizeServerName(string value)
|
||||
{
|
||||
var normalized = (value ?? string.Empty).Trim().TrimStart('\\');
|
||||
var dotIndex = normalized.IndexOf('.');
|
||||
return dotIndex > 0 ? normalized.Substring(0, dotIndex) : normalized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prueft, ob eine Exception aus dem erwarteten WMI-/COM-Fehlerbereich stammt.
|
||||
/// </summary>
|
||||
@@ -426,9 +492,29 @@ namespace BizTalkCheckmkPulse
|
||||
/// <param name="exception">Ausgeloeste Exception.</param>
|
||||
/// <param name="required">Kennzeichen fuer eine Pflichtabfrage.</param>
|
||||
/// <param name="connectionFailure">Kennzeichen fuer Fehler beim Namespace-Verbindungsaufbau.</param>
|
||||
private static void AddWmiDiagnostic(ProbeResult result, string component, Exception exception, bool required, bool connectionFailure)
|
||||
private static void AddWmiDiagnostic(
|
||||
ProbeResult result,
|
||||
string component,
|
||||
Exception exception,
|
||||
bool required,
|
||||
bool connectionFailure,
|
||||
string queryText,
|
||||
TimeSpan? duration)
|
||||
{
|
||||
var category = ClassifyWmiException(exception);
|
||||
var technicalDetails = exception.GetType().Name
|
||||
+ " HRESULT=0x" + exception.HResult.ToString("X8")
|
||||
+ ": " + exception.Message;
|
||||
if (!string.IsNullOrWhiteSpace(queryText))
|
||||
{
|
||||
technicalDetails += " WQL={" + queryText + "}";
|
||||
}
|
||||
|
||||
if (duration.HasValue)
|
||||
{
|
||||
technicalDetails += " DurationMs=" + Math.Round(duration.Value.TotalMilliseconds);
|
||||
}
|
||||
|
||||
result.Diagnostics.Add(new ProbeDiagnostic
|
||||
{
|
||||
Area = DiagnosticArea.Wmi,
|
||||
@@ -437,7 +523,7 @@ namespace BizTalkCheckmkPulse
|
||||
Required = required,
|
||||
Summary = BuildWmiSummary(category, component, connectionFailure),
|
||||
Action = BuildWmiAction(category, connectionFailure),
|
||||
TechnicalDetails = exception.GetType().Name + " HRESULT=0x" + exception.HResult.ToString("X8") + ": " + exception.Message
|
||||
TechnicalDetails = technicalDetails
|
||||
});
|
||||
}
|
||||
|
||||
@@ -477,12 +563,25 @@ namespace BizTalkCheckmkPulse
|
||||
}
|
||||
|
||||
if (managementException != null
|
||||
&& (managementException.ErrorCode == ManagementStatus.InvalidNamespace
|
||||
|| managementException.ErrorCode == ManagementStatus.InvalidClass))
|
||||
&& managementException.ErrorCode == ManagementStatus.InvalidNamespace)
|
||||
{
|
||||
return DiagnosticCategory.Configuration;
|
||||
}
|
||||
|
||||
if (managementException != null
|
||||
&& (managementException.ErrorCode == ManagementStatus.InvalidClass
|
||||
|| managementException.ErrorCode == ManagementStatus.InvalidProperty
|
||||
|| managementException.ErrorCode == ManagementStatus.InvalidQuery
|
||||
|| managementException.ErrorCode == ManagementStatus.UnparsableQuery))
|
||||
{
|
||||
return DiagnosticCategory.Schema;
|
||||
}
|
||||
|
||||
if (managementException != null && managementException.ErrorCode == ManagementStatus.Timedout)
|
||||
{
|
||||
return DiagnosticCategory.Timeout;
|
||||
}
|
||||
|
||||
var message = exception.Message ?? string.Empty;
|
||||
if (ContainsAny(message, "timed out", "timeout", "Zeitlimit"))
|
||||
{
|
||||
@@ -513,7 +612,9 @@ namespace BizTalkCheckmkPulse
|
||||
case DiagnosticCategory.Configuration:
|
||||
return connectionFailure
|
||||
? "BizTalk-WMI-Namespace ist nicht vorhanden oder nicht korrekt registriert."
|
||||
: "BizTalk-WMI-Klasse " + component + " ist nicht vorhanden oder nicht korrekt registriert.";
|
||||
: "BizTalk-WMI-Konfiguration fuer " + component + " ist unvollstaendig.";
|
||||
case DiagnosticCategory.Schema:
|
||||
return "WMI-Klasse oder WQL fuer " + component + " wird vom installierten BizTalk-Provider nicht unterstuetzt.";
|
||||
case DiagnosticCategory.Timeout:
|
||||
return "WMI-Abfrage fuer " + component + " hat das konfigurierte Zeitlimit ueberschritten.";
|
||||
case DiagnosticCategory.Connectivity:
|
||||
@@ -539,6 +640,8 @@ namespace BizTalkCheckmkPulse
|
||||
: "Agent-Dump pruefen und Computerkonto <DOMAIN>\\" + Environment.MachineName + "$ zunaechst der konfigurierten BizTalk-Operator-Gruppe zuordnen.";
|
||||
case DiagnosticCategory.Configuration:
|
||||
return "BizTalk-WMI-Provider/Namespace auf dem BizTalk-Server pruefen und gegebenenfalls mit dem BizTalk-Setup reparieren.";
|
||||
case DiagnosticCategory.Schema:
|
||||
return "Klassen- und Property-Namen gegen das installierte BizTalk-WMI-Schema und die Microsoft-Dokumentation pruefen. Rechteerhoehung behebt InvalidClass/InvalidQuery nicht.";
|
||||
case DiagnosticCategory.Timeout:
|
||||
return "WMI-Dienst, BizTalk-/SQL-Auslastung und Event Logs pruefen; Timeout erst danach erhoehen.";
|
||||
case DiagnosticCategory.Connectivity:
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" Condition="false" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{E219C45A-CC95-4F45-9B6F-8244CBAEE35A}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>BizTalkCheckmkPulse.Tests</RootNamespace>
|
||||
<AssemblyName>BizTalkCheckmkPulse.Tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\BizTalkCheckmkPulse\BizTalkCheckmkPulse.csproj">
|
||||
<Project>{A4D4D050-9EA7-4A71-B510-7D9D699B9F38}</Project>
|
||||
<Name>BizTalkCheckmkPulse</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BizTalkCheckmkPulse.Tests
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
private static readonly List<string> Failures = new List<string>();
|
||||
|
||||
private static int Main()
|
||||
{
|
||||
Run("SelfTestEmitsAllStableServices", SelfTestEmitsAllStableServices);
|
||||
Run("UnavailableSourcesAreUnknown", UnavailableSourcesAreUnknown);
|
||||
Run("UnknownApplicationDoesNotCreateService", UnknownApplicationDoesNotCreateService);
|
||||
Run("KnownApplicationCreatesService", KnownApplicationCreatesService);
|
||||
Run("ServerNamesAreComparedWithoutWql", ServerNamesAreComparedWithoutWql);
|
||||
Run("GroupSettingQueryUsesDocumentedSchema", GroupSettingQueryUsesDocumentedSchema);
|
||||
|
||||
if (Failures.Count == 0)
|
||||
{
|
||||
Console.WriteLine("PASS: 6 tests");
|
||||
return 0;
|
||||
}
|
||||
|
||||
foreach (var failure in Failures)
|
||||
{
|
||||
Console.Error.WriteLine("FAIL: " + failure);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static void SelfTestEmitsAllStableServices()
|
||||
{
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions()).FormatSelfTest().ToArray();
|
||||
AssertEqual(6, lines.Length, "self-test line count");
|
||||
AssertEqual(6, lines.Distinct(StringComparer.Ordinal).Count(), "unique self-test lines");
|
||||
Assert(lines.All(x => x.StartsWith("0 \"BizTalk ", StringComparison.Ordinal)), "every self-test line must be OK");
|
||||
Assert(lines.Any(x => x.Contains("\"BizTalk Event Log\"")), "Event Log service missing");
|
||||
}
|
||||
|
||||
private static void UnavailableSourcesAreUnknown()
|
||||
{
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions()).Format(new ProbeResult()).Take(6).ToArray();
|
||||
AssertEqual(6, lines.Length, "stable service count");
|
||||
Assert(lines.All(x => x.StartsWith("3 \"BizTalk ", StringComparison.Ordinal)), "unavailable sources must be UNKNOWN");
|
||||
}
|
||||
|
||||
private static void UnknownApplicationDoesNotCreateService()
|
||||
{
|
||||
var options = new MonitoringOptions { EmitPerApplicationSuspensionServices = true };
|
||||
var result = CreateSuspensionResult("(unknown)");
|
||||
var lines = new CheckmkLocalFormatter(options).Format(result).ToArray();
|
||||
AssertEqual(6, lines.Length, "unknown application must not create a dynamic service");
|
||||
}
|
||||
|
||||
private static void KnownApplicationCreatesService()
|
||||
{
|
||||
var options = new MonitoringOptions { EmitPerApplicationSuspensionServices = true };
|
||||
var result = CreateSuspensionResult("Orders");
|
||||
var lines = new CheckmkLocalFormatter(options).Format(result).ToArray();
|
||||
AssertEqual(7, lines.Length, "known application should create one dynamic service");
|
||||
Assert(lines.Any(x => x.Contains("\"BizTalk Suspended Orders\"")), "known application service missing");
|
||||
}
|
||||
|
||||
private static void ServerNamesAreComparedWithoutWql()
|
||||
{
|
||||
Assert(WmiBizTalkProbe.IsSameServer("BIZTALK01.corp.example", "biztalk01"), "FQDN and short name must match");
|
||||
Assert(WmiBizTalkProbe.IsSameServer(@"\\BIZ-TALK+01", "biz-talk+01.example"), "special characters must be compared client-side");
|
||||
Assert(!WmiBizTalkProbe.IsSameServer("BIZTALK01", "BIZTALK02"), "different servers must not match");
|
||||
}
|
||||
|
||||
private static void GroupSettingQueryUsesDocumentedSchema()
|
||||
{
|
||||
var query = WmiBizTalkProbe.GroupSettingQuery;
|
||||
Assert(query.Contains("FROM MSBTS_GroupSetting"), "MSBTS_GroupSetting query missing");
|
||||
Assert(query.Contains("SubscriptionDBServerName"), "master MessageBox server property missing");
|
||||
Assert(query.Contains("SubscriptionDBName"), "master MessageBox database property missing");
|
||||
Assert(query.IndexOf("MessageBoxSetting", StringComparison.OrdinalIgnoreCase) < 0, "unsupported MessageBoxSetting class present");
|
||||
}
|
||||
|
||||
private static ProbeResult CreateSuspensionResult(string applicationName)
|
||||
{
|
||||
var result = new ProbeResult();
|
||||
result.Platform.SuspendedInstancesDataAvailable = true;
|
||||
result.SuspendedInstances.Add(new SuspendedInstance
|
||||
{
|
||||
ApplicationName = applicationName,
|
||||
ServiceName = "TestService",
|
||||
Kind = SuspendedKind.Resumable
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void Run(string name, Action test)
|
||||
{
|
||||
try
|
||||
{
|
||||
test();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Failures.Add(name + ": " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Assert(bool condition, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertEqual(int expected, int actual, string label)
|
||||
{
|
||||
if (expected != actual)
|
||||
{
|
||||
throw new InvalidOperationException(label + ": expected " + expected + ", actual " + actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user