Repository navigation
Expand file tree
/
Copy pathPRTG.Dsls.psm1
More file actions
217 lines (181 loc) · 6.6 KB
/
Copy pathPRTG.Dsls.psm1
File metadata and controls
217 lines (181 loc) · 6.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
<#
.SYNOPSIS
Gemeinsame Hilfsfunktionen fuer die DSLS-Sensoren (Dassault Systemes License Server).
.DESCRIPTION
Buendelt das, was alle DSLS-Sensoren brauchen: Erreichbarkeitspruefung,
Aufruf von DSLicSrv.exe im Admin-Modus und das Zusammenbauen der
PRTG-XML-Antwort inklusive Escaping.
Das Modul liegt bewusst im selben Ordner wie die Sensoren. PRTG fuehrt
Custom-EXEXML-Sensoren aus ihrem Verzeichnis aus, deshalb funktioniert
der Import ueber $PSScriptRoot. Beim Ausrollen den kompletten Ordner nach
"Custom Sensors\EXEXML\" kopieren, nicht nur die einzelne .ps1.
DSLicSrv.exe muss dabei NICHT auf der PRTG-Probe installiert sein:
Invoke-DslsAdmin fuehrt es per PowerShell-Remoting direkt auf dem
Lizenzserver aus und verbindet sich dort lokal ("c localhost <port>").
Das erfordert PSRemoting auf dem Lizenzserver und einen Sicherheitskontext
mit Remoting-Rechten dort - dafuer keine DSLS-Installation auf der Probe
und kein offenes TCP <port> von der Probe zum Lizenzserver.
.NOTES
Autor: IT-Administration
#>
Set-StrictMode -Version Latest
function Test-DslsServer {
<#
.SYNOPSIS
Prueft, ob der Lizenzserver per ICMP erreichbar ist.
#>
[CmdletBinding()]
[OutputType([bool])]
param(
[Parameter(Mandatory)]
[string]$TargetServer
)
Test-Connection -ComputerName $TargetServer -Count 1 -Quiet
}
function Invoke-DslsAdmin {
<#
.SYNOPSIS
Fuehrt eine Befehlsfolge im Admin-Modus von DSLicSrv.exe auf dem Lizenzserver aus.
.DESCRIPTION
Fuehrt DSLicSrv.exe per PowerShell-Remoting DIREKT auf $TargetServer aus
und verbindet sich dort lokal ("c localhost $Port; ...; d; quit"). $DslsExe
ist deshalb ein Pfad auf dem Lizenzserver, nicht auf der PRTG-Probe. Ist
$TargetServer die lokale Maschine, entfaellt Invoke-Command.
.PARAMETER Command
Die Befehlsfolge ohne das fuehrende "c localhost <port>;" und ohne
das abschliessende "d; quit" - beides wird hier ergaenzt.
.EXAMPLE
Invoke-DslsAdmin -TargetServer 'licsrv01.contoso.local' -DslsExe $exe -Command 'gli; glu -all'
#>
[CmdletBinding()]
[OutputType([string[]])]
param(
[Parameter(Mandatory)]
[string]$TargetServer,
[Parameter(Mandatory)]
[string]$DslsExe,
[Parameter(Mandatory)]
[string]$Command,
[ValidateRange(1, 65535)]
[int]$Port = 4084
)
$remoteBlock = {
param([string]$Exe, [int]$DsPort, [string]$Cmd)
if (-not (Test-Path -LiteralPath $Exe)) {
throw "DSLicSrv.exe nicht gefunden: $Exe"
}
& $Exe -admin -run "c localhost $DsPort; $Cmd; d; quit" 2>&1 | ForEach-Object { [string]$_ }
}
$argList = @($DslsExe, $Port, $Command)
$isLocal = $TargetServer -in @('localhost', '.', '127.0.0.1', $env:COMPUTERNAME)
if ($isLocal) {
& $remoteBlock @argList
}
else {
$sessionOption = New-PSSessionOption -OpenTimeout 15000 -OperationTimeout 45000
Invoke-Command -ComputerName $TargetServer -SessionOption $sessionOption `
-ScriptBlock $remoteBlock -ArgumentList $argList
}
}
function ConvertTo-PrtgText {
<#
.SYNOPSIS
Maskiert Zeichen, die die PRTG-XML-Antwort sonst zerlegen wuerden.
.DESCRIPTION
Kanalnamen und Meldungstexte kommen teilweise direkt aus dem
DSLS-Output. Ein '&' oder '<' darin macht die Antwort ungueltig und
der Sensor faellt mit "XML-Parsingfehler" aus, ohne dass der Grund
ersichtlich waere.
#>
[CmdletBinding()]
[OutputType([string])]
param(
[AllowNull()]
[AllowEmptyString()]
[string]$Text
)
if ([string]::IsNullOrEmpty($Text)) { return '' }
$Text.Replace('&', '&').
Replace('<', '<').
Replace('>', '>').
Replace('"', '"').
Replace("'", ''')
}
function New-PrtgResult {
<#
.SYNOPSIS
Erzeugt ein einzelnes <result>-Element fuer die PRTG-Antwort.
.PARAMETER Unit
PRTG-Einheit. 'Custom' zusammen mit -CustomUnit fuer eigene Beschriftungen.
#>
[CmdletBinding()]
[OutputType([string])]
param(
[Parameter(Mandatory)]
[string]$Channel,
[Parameter(Mandatory)]
[int]$Value,
[string]$Unit = 'Count',
[string]$CustomUnit,
[Nullable[int]]$LimitMinWarning,
[Nullable[int]]$LimitMinError,
[Nullable[int]]$LimitMaxWarning,
[Nullable[int]]$LimitMaxError
)
$sb = [System.Text.StringBuilder]::new()
[void]$sb.Append('<result>')
[void]$sb.Append("<channel>$(ConvertTo-PrtgText $Channel)</channel>")
[void]$sb.Append("<value>$Value</value>")
[void]$sb.Append("<unit>$Unit</unit>")
if ($CustomUnit) {
[void]$sb.Append("<customunit>$(ConvertTo-PrtgText $CustomUnit)</customunit>")
}
# limitmode nur setzen, wenn tatsaechlich eine Grenze definiert ist -
# sonst zeigt PRTG den Kanal dauerhaft als "Limits aktiv, aber keine".
$limits = @{
limitminwarning = $LimitMinWarning
limitminerror = $LimitMinError
limitmaxwarning = $LimitMaxWarning
limitmaxerror = $LimitMaxError
}
if ($limits.Values.Where({ $null -ne $_ }).Count -gt 0) {
[void]$sb.Append('<limitmode>1</limitmode>')
foreach ($name in 'limitminwarning', 'limitminerror', 'limitmaxwarning', 'limitmaxerror') {
if ($null -ne $limits[$name]) {
[void]$sb.Append("<$name>$($limits[$name])</$name>")
}
}
}
[void]$sb.Append('</result>')
$sb.ToString()
}
function Write-PrtgResponse {
<#
.SYNOPSIS
Gibt die vollstaendige PRTG-Antwort aus und beendet das Skript.
.DESCRIPTION
PRTG wertet ausschliesslich stdout aus. Der Exitcode wird ignoriert,
Fehler muessen ueber <error>1</error> gemeldet werden - deshalb gibt
es hier -ErrorText statt eines throw.
#>
[CmdletBinding()]
param(
[string[]]$Result,
[string]$Text,
[string]$ErrorText
)
$sb = [System.Text.StringBuilder]::new()
[void]$sb.Append('<prtg>')
if ($ErrorText) {
[void]$sb.Append('<error>1</error>')
[void]$sb.Append("<text>$(ConvertTo-PrtgText $ErrorText)</text>")
}
else {
foreach ($r in $Result) { [void]$sb.Append($r) }
if ($Text) { [void]$sb.Append("<text>$(ConvertTo-PrtgText $Text)</text>") }
}
[void]$sb.Append('</prtg>')
Write-Output $sb.ToString()
}
Export-ModuleMember -Function Test-DslsServer, Invoke-DslsAdmin,
ConvertTo-PrtgText, New-PrtgResult, Write-PrtgResponse