· 7 years ago · Dec 11, 2018, 02:12 PM
1<#
2.SYNOPSIS
3 Monitor your DFS Replication Backlog with a graphical history.
4.DESCRIPTION
5 Use the DFSMonitorWithHistory.ps1 script to monitor your DFS backlog. It
6 contains a graphical history chart as well as detailed information from
7 each run. I recommend running from a scheduled task every hour to get the
8 best information.
9
10 Make sure to edit the PARAM section and update it for your environment.
11
12 Graphical history chart is a Google visualization that requires Flash to run
13 (it's the same chart you see on Google Finance). Historical data is saved
14 in an XML file.
15.PARAMETER DFSServers
16 Array of computers to be processed. For backwards compatibility this parameter
17 will also accept a comma seperated list, i.e.: "server1,server2,server3" and
18 will automatically split it into an array. Otherwise you can use the
19 standard array syntax: server1,server2,server3 (without quotes), or
20 "server1","server2","server3"
21.PARAMETER DaysToSaveData
22 How much of a history do you want the script to keep. I recommend no more then 2
23 weeks, but 1 week is probably better for larger DFS implementations.
24.PARAMETER DataLocation
25 The full path, or UNC, to where you want the script to keep the historic
26 data. Logs kept by the script will also be stored here.
27.PARAMETER OutputLocation
28 The full path, or UNC, to where you want the script to save the HTML files
29 the script creates. I recommend you make this the root folder for a web
30 server. For IIS, the default location would be: \\servername\c$\inetpub\wwwroot
31.PARAMETER MaxThreads
32 This scripts uses multithreading to improve performance for gathering the
33 DFS information. You can alter the number of threads that it uses based on
34 the server you have. In testing I found 10 to about the sweet spot for my
35 server (less or more was slower). You should not need to modify this
36 amount.
37.INPUTS
38 Historical data file: $DataLocation\DFSData.XML
39.OUTPUTS
40 Log: $DataLocation\debugMMddyyyyHHmm.log
41 Data File: $DataLocation\DFSData.XML
42 Primary HTML Page (use this): $OutputLocation\DFSMonitorGrid.HTML
43 Placeholder HTML for Detailed Reports (contains iFrame): $OutputLocation\DFSDetailIndex.HTML
44 Detailed Reports (displayed in iFrame): $OutputLocation\DFSDetailsMMddyyyyHHmm.HTML
45.EXAMPLE
46 .\pathtoscript\DFSMonitorWithHistory.PS1
47 Runs the script while accepting all default values. Edit PARAM section
48 of this script to match your environment.
49.EXAMPLE
50 .\pathtoscript\DFSMonitorWithHistory.PS1 -DFSServes server1,server2 -DaysToSave 7 -DataLocation c:\scripts\dfs -OutputLocation \\webserver\c$\inetpub\wwwroot
51 Runs the script using the servers server1 and server2, it will save the data for 7 days and keep
52 the logs and datafile (DFSData.XML) at c:\scripts\dfs. All of the HTML pages will be stored on
53 a server called webserver on the c: drive in \inetpub\wwwroot (the default web root directory
54 on an IIS server). It will accept the default of 10 threads.
55.NOTES
56 Author: Martin Pugh
57 Twitter: @thesurlyadm1n
58 Spiceworks: Martin9700
59 Blog: www.thesurlyadmin.com
60
61 Change Log:
62 2.63 - Improved error reporting in primary script and in multithreaded sub-scripts. Converted
63 sub-script output to object for better data handling, and changed the "All Groups" variable
64 from a string array to hashtable for better performance. Fixed some debug messages that worked
65 fine in ISE but not on command line. Fixed bug with Alerts not showing up on the
66 DFSMonitorGrid.HTML start page.
67 2.62 - Bug when using $DupeCheck to make sure script doesn't check the same group/folder pair
68 more then once. Added Cmdletbinding to support -Verbose output.
69 2.61 - Bug found by Bhopper577 where test-path was still looking for the old CSV data file.
70 2.6 - Changed saving from a CSV file to an XML which will preserve date/time without having
71 to convert in script.
72 2.51 - Increased the # of PING's to 4 so remote server detection over WAN will be a little
73 more reliable. This required a change in the string detection so 0% loss, 25% loss and
74 75% loss would all be acceptable. I fudged on the RegEx a bit so technically 20%, 55%
75 and 70% are OK too, but if you only ping 4 times you can't get those percentages!
76.LINK
77 http://community.spiceworks.com/scripts/show/1536-dfs-monitor-with-history
78.LINK
79 http://www.thesurlyadmin.com/2012/08/03/dfs-replication-monitoring/
80#>
81<INSERT YOUR PARAMS HERE>
82#region Functions
83Function IsAvailable($Server)
84{ If ($GoodServers -contains $Server)
85 { Return $true
86 }
87 Else
88 { If ($BadServers -contains $Server)
89 { Return $false
90 }
91 $Result = PING $Server -n 4
92 Switch -regex ($Result)
93 { "\(0% loss" { $Found = "Yes"; Break }
94 "\([257][50]% loss" { $Found = "Yes"; Break }
95 "% loss" { $Found = "Failed to respond to PING"; Break }
96 "Destination host unreachable" { $Found = "Destination host unreachable"; Break }
97 "could not find host" { $Found = "Server Not Found"; Break }
98 default { $Found = "Unknown Error" }
99 }
100 If ($Found -eq "Yes")
101 { $Running = $true
102 $Service = Get-Service dfsr -ComputerName $Server
103 If ($Service.Status -ne "Running")
104 { $Service = Get-Service dfs -ComputerName $Server
105 If ($Service.Status -ne "Running")
106 { $Running = $false
107 }
108 }
109 If ($Running)
110 { $global:GoodServers += $Server
111 Return $true
112 }
113 Else
114 { Write-Debug "$Server`: DFSr or DFS Service Not Running"
115 $global:BadServers += $Server
116 $global:AlertReport += "$Server`: DFSr or DFS Service Not Running"
117 }
118 }
119 Else
120 { Write-Debug "$Server`: $Found"
121 $global:BadServers += $Server
122 $global:AlertReport += "$Server`: $Found"
123 }
124 }
125}
126
127Function GetWMI
128{ Param ([String]$WMIQuery,
129 [String]$Computer)
130
131 $ErrorCount = 0
132 Do
133 { $WMIObject = Get-WmiObject -computerName $Computer -Namespace "root\MicrosoftDFS" -Query $WMIQuery -Debug
134 If ($WMIObject -eq $null)
135 { $ErrorCount ++
136 }
137 Else
138 { Return $WMIObject
139 }
140 }
141 While ($ErrorCount -le 2)
142 $WMIObject = "WMI Error on $Computer"
143 Return $WMIObject
144}
145#endregion
146
147#Here we go!
148cls
149
150#Setup the environment and start the log file
151$DebugPreference = "Continue"
152
153#Validate Data path exists, since this is where the log file exists we'll use Write-Host to notify.
154If (-not (Test-Path $DataLocation -PathType Container))
155{ Write-Host "Data Path: $DataLocation does not exist! Stopping script." -ForegroundColor Red
156 Exit
157}
158
159#Setup the log
160[DateTime]$ScriptRunDate = (Get-Date).DateTime
161$SaveFormatDate = Get-Date $ScriptRunDate -format yyyyMMddHHmm
162Start-Transcript -Path $DataLocation\debug$SaveFormatDate.log
163
164#Validate Output/Report location exists
165If (-not (Test-Path $OutputLocation -PathType Container))
166{ Write-Debug "Output Path: $OutputLocation does not exist! Stopping script."
167 Exit
168}
169
170#Set some global variables
171$NewData = @()
172$Global:AlertReport = @()
173$Global:GoodServers = @()
174$Global:BadServers = @()
175$AllGroupNames = @{}
176$DupeCheck = @()
177$Servers = @()
178
179#Display parameters
180Write-Debug "Servers to be scanned: $DFSServers"
181Write-Debug "Days to Save Data: $DaysToSaveData"
182Write-Debug "Data Path: $DataLocation"
183Write-Debug "HTML Path: $OutputLocation"
184Write-Debug "Maximum Threads: $MaxThreads"
185
186Write-Debug "Loading data..."
187
188#Parse DFSServers and make a good array
189ForEach ($Item in $DFSServers)
190{ If ($Item.Contains(","))
191 { $Servers += $Item.Split(",")
192 }
193 Else
194 { $Servers += $Item
195 }
196}
197[DateTime]$SaveDate = (Get-Date).Date.AddDays(-$DaysToSaveData)
198
199#Check if Data file exists, if so import it, if not create it.
200If ((Test-Path $DataLocation\DFSData.xml) -eq $False)
201{ $Data = @()
202}
203Else
204{ $Data = Import-Clixml $DataLocation\DFSData.xml
205 If ($Data.BacklogCount -gt 0)
206 { $Data = $Data | Where {$_.RunDate -ge $SaveDate}
207 }
208 Else
209 { $Data = @()
210 }
211}
212
213#Start the main loop.
214ForEach ($FileServer in $Servers)
215{ Write-Debug "Now working on $FileServer..."
216 $FileServer = $FileServer.ToUpper()
217 If (-not (IsAvailable $FileServer))
218 { Continue
219 }
220
221 $WMIQuery = "SELECT * FROM DfsrReplicationGroupConfig"
222 $GroupGUIDs = GetWMI -WMIQuery $WMIQuery -Computer $FileServer
223 If ($GroupGUIDs -like "*WMI Error*")
224 { $AlertReport += $GroupGUIDs
225 Continue
226 }
227
228 $WMIQuery = "SELECT * FROM DfsrConnectionConfig WHERE InBound=True"
229 $RGConnections = GetWMI -WMIQuery $WMIQuery -Computer $FileServer
230 If ($RGConnections -like "*WMI Error*")
231 { $AlertReport += $RGConnections
232 Continue
233 }
234
235 $WMIQuery = "SELECT * FROM DfsrReplicatedFolderConfig"
236 $RGFolders = GetWMI -WMIQuery $WMIQuery -Computer $FileServer
237 If ($RGFolders -like "*WMI Error*")
238 { $AlertReport += $RGFolders
239 Continue
240 }
241
242 ForEach ($Group in $GroupGUIDs)
243 { #If (($AllGroupNames -match $Group.ReplicationGroupGuid).count -eq 0 -or $AllGroupNames.Count -eq 0)
244 If (-not $AllGroupNames.ContainsKey($Group.ReplicationGroupGuid))
245 { #$temp = $Group.ReplicationGroupGUID + ":" + $Group.ReplicationGroupName
246 #$AllGroupNames += $temp
247 $AllGroupNames.Add($Group.ReplicationGroupGUID,$Group.ReplicationGroupName)
248 }
249 $GFolders = $RGFolders | Where {$_.ReplicationGroupGUID -eq $Group.ReplicationGroupGUID}
250 ForEach ($Folder in $GFolders)
251 { $GConnection = $RGConnections | Where {$_.ReplicationGroupGUID -eq $Group.ReplicationGroupGUID}
252 ForEach ($Connection in $GConnection)
253 { $InServer = $FileServer
254 $OutServer = $Connection.PartnerName
255
256 #Check if we've already done this
257 $Found = "No"
258 ForEach ($Line in $DupeCheck)
259 { If ($Line[0].ToUpper() -eq $Group.ReplicationGroupName.ToUpper() -and
260 $Line[1].ToUpper() -eq $Folder.ReplicatedFolderName.ToUpper() -and
261 $Line[2].ToUpper() -eq $InServer.ToUpper() -and
262 $Line[3].ToUpper() -eq $OutServer.ToUpper())
263 { $Found = "Yes"
264 Break
265 }
266 }
267 If ($Found -eq "No")
268 { $DupeCheck += ,@($Group.ReplicationGroupName,$Folder.ReplicatedFolderName,$InServer,$OutServer)
269 $DupeCheck += ,@($Group.ReplicationGroupName,$Folder.ReplicatedFolderName,$OutServer,$InServer)
270 }
271 Else
272 { Continue
273 }
274
275 #Now check if partner server is available
276 If (-not (IsAvailable $OutServer))
277 { Continue
278 }
279
280 For ($i = 1; $i -le 2; $i++)
281 { While ($(Get-Job -state "Running").count -ge $MaxThreads)
282 { Write-Debug "Thread count hit max of $MaxThreads, waiting for threads to finish..."
283 Start-Sleep -Milliseconds 5000
284 }
285 Start-Job -ArgumentList $InServer,$OutServer,$Group,$Folder.ReplicatedFolderName -ScriptBlock {
286 Param (
287 [string]$InServer,
288 [string]$OutServer,
289 [object]$Group,
290 [string]$ReplicationFolder
291 )
292
293 Function GetWMI
294 { Param (
295 [String]$WMIQuery,
296 [String]$Computer
297 )
298
299 $ErrorCount = 0
300 While ($ErrorCount -le 2)
301 { $WMIObject = Get-WmiObject -Namespace "root\MicrosoftDFS" -Query $WMIQuery -ComputerName $Computer -Debug
302 If ($WMIObject)
303 { $Status = "Success"
304 $ErrorDetail = ""
305 Break
306 }
307 Else
308 { $ErrorCount ++
309 $Status = "Error"
310 $ErrorDetail = $Error[0]
311 }
312 }
313 New-Object PSObject -Property @{
314 Status = $Status
315 Object = $WMIObject
316 Error = $ErrorDetail
317 }
318 }
319 $ErrorCount = 0
320 $BacklogConnCount = 0
321 $ErrorDetail = ""
322
323 $WMIQuery = "SELECT * FROM DfsrReplicatedFolderConfig WHERE ReplicationGroupGUID = '" + $Group.ReplicationGroupGUID + "' AND ReplicatedFolderName = '" + $ReplicationFolder + "'"
324 $WMIObject = GetWMI -WMIQuery $WMIQuery -Computer $InServer
325 If ($WMIObject.Status -eq "Error")
326 { $Status = "Error"
327 $BacklogFiles = "WMI Error"
328 $ErrorReport = "WMI Error on $InServer"
329 $ErrorDetail = $WMIObject.Error
330 }
331 ElseIf ($WMIObject.Object.Enabled)
332 { $WMIQuery = "SELECT * FROM DfsrReplicatedFolderConfig WHERE ReplicationGroupGUID = '" + $Group.ReplicationGroupGUID + "' AND ReplicatedFolderName = '" + $ReplicationFolder + "'"
333 $WMIObject = GetWMI -WMIQuery $WMIQuery -Computer $OutServer
334 If ($WMIObject.Status -eq "Error")
335 { $Status = "Error"
336 $BacklogFiles = "WMI Error"
337 $ErrorReport = "WMI Error on $OutServer"
338 $ErrorDetail = $WMIObject.Error
339 }
340 ElseIf ($WMIObject.Object.Enabled)
341 { #Get the version vector of the partner
342 $WMIQuery = "SELECT * FROM DfsrReplicatedFolderInfo WHERE ReplicationGroupGUID = '" + $Group.ReplicationGroupGUID + "' AND ReplicatedFolderName = '" + $ReplicationFolder + "'"
343 $WMIObject = GetWMI -WMIQuery $WMIQuery -Computer $OutServer
344 If ($WMIObject.Status -eq "Error")
345 { $Status = "Error"
346 $BacklogFiles = "WMI Error"
347 $ErrorReport = "WMI Error occurred on $OutServer"
348 $ErrorDetail = $WMIObject.Error
349 }
350 Else
351 { $Vv = $WMIObject.Object.GetVersionVector().VersionVector
352 #Get the backlog count from the partner
353 $WMIQuery = "SELECT * FROM DfsrReplicatedFolderInfo WHERE ReplicationGroupGUID = '" + $Group.ReplicationGroupGUID + "' AND ReplicatedFolderName = '" + $ReplicationFolder + "'"
354 $WMIObject = GetWMI -WMIQuery $WMIQuery -Computer $InServer
355 If ($WMIObject.Status -eq "Error")
356 { $Status = "WMI Error"
357 $BacklogFiles = "WMI Error"
358 $ErrorReport = "WMI Error occurred on $OutServer"
359 $ErrorDetail = $WMIObject.Error
360 }
361 Else
362 { $BacklogConnCount = $WMIObject.Object.GetOutboundBacklogFileCount($Vv).BacklogFileCount
363 $arrFiles = $WMIObject.Object.GetOutboundBacklogFileIDRecords($Vv).BacklogIdRecords
364 If ($BacklogConnCount -eq 0)
365 { $Files = " "
366 }
367 Else
368 { $Files = ""
369 ForEach ($FileLine in $arrFiles)
370 { $Files += $FileLine.FileName + "<br>"
371 }
372 }
373 $Status = "Success"
374 $BacklogFiles = $Files
375 $ErrorReport = ""
376 }
377 }
378 }
379 Else
380 { $Status = "Disabled"
381 $BacklogFiles = "Disabled"
382 $ErrorReport = "Folder $($Group.ReplicationGroupName)/$ReplicationFolder disabled on $OutServer"
383 }
384 }
385 Else
386 { $Status = "Disabled"
387 $BacklogFiles = "Disabled"
388 $ErrorReport = "Folder $($Group.ReplicationGroupName)/$ReplicationFolder disabled on $InServer"
389 }
390 New-Object PSObject -Property @{
391 Status = $Status
392 BacklogFiles = $BacklogFiles
393 ErrorReport = $ErrorReport
394 ErrorDetail = $ErrorDetail
395 GroupObject = $Group.ReplicationGroupGUID
396 Folder = $ReplicationFolder
397 InServer = $InServer
398 OutServer = $OutServer
399 BacklogCount = $BacklogConnCount
400 }
401 } | Out-Null
402 $InServer = $Connection.PartnerName
403 $OutServer = $FileServer
404 }
405 }
406 }
407 }
408}
409
410#Wait for all the jobs to finish
411While (@(Get-Job -State "Running").count -gt 0)
412{ Write-Debug "All threads submitted, waiting for them to finish..."
413 Start-Sleep -Milliseconds 5000
414}
415
416#Now read the job data into data
417Write-Debug "All threads completed. Threads run: $(@(Get-Job).Count)"
418$Output = @()
419ForEach ($Job in Get-Job)
420{ $ErrorCount = 0
421 Do
422 { Write-Debug "Receiving job number: $($Job.Id)"
423 $Result = Receive-Job $Job
424 If ($Result -eq $null)
425 { $ErrorCount ++
426 If ($ErrorCount -eq 4)
427 { Write-Debug "Unable to retrieve job: $($Job.id)"
428 $Result = "Fail"
429 }
430 Else
431 { Write-Debug "Problem retrieving job: $($Job.id), Retry: $ErrorCount"
432 Start-Sleep -Seconds 3
433 }
434 }
435 Else
436 { $ErrorCount = 4
437 }
438 } While ($ErrorCount -lt 4)
439 If ($Result -eq "Fail")
440 { Remove-Job $Job
441 Continue
442 }
443 #$GroupName = ($AllGroupNames | Where {$_ -match $Result.Group}).Split(":")
444 $NewData += ,@($AllGroupNames[$Result.GroupObject],$Result.Folder,$Result.InServer,$Result.OutServer,$Result.BacklogCount,$Result.BacklogFiles)
445 $Output += New-Object PSCustomObject -Property @{
446 GroupName = $AllGroupNames[$Result.GroupObject]
447 GroupGUID = $Result.GroupObject
448 Folder = $Result.Folder
449 InServer = $Result.InServer
450 OutServer = $Result.OutServer
451 Backlog = $Result.BacklogCount
452 BackLogFiles = $Result.BacklogFiles
453 }
454 If ($Result.Status -ne "Success")
455 { If ($AlertReport -notcontains $Result.ErrorReport)
456 { $AlertReport += $Result.ErrorReport
457 Write-Debug $Result.ErrorDetail
458 }
459 }
460 Remove-Job $Job
461}
462
463#Now add the new data
464#ForEach ($GroupName in $AllGroupNames.Values){
465#$GroupName = ($Group.Split(":"))[1]
466$GroupName = $AllGroupNames.Values
467 $UniqueReplFolders = $Output | Where {$_.GroupName -eq $GroupName} | Select Folder -Unique
468 #ForEach ($Folder in $UniqueReplFolders){
469 $Folder = $UniqueReplFolders.Folder
470 $BacklogCount = ($Output | Where {$_.GroupName -eq $GroupName -and $_.Folder -eq $Folder} | Measure-Object Backlog -sum).Sum
471 $NewRGName = $Folder + ":" + $GroupName
472 $NEWXMLDATA = New-Object PSCustomObject -Property @{
473 RFName = $Folder
474 RGGUID = $NewRGName
475 BacklogCount = $BacklogCount
476 RunDate = $ScriptRunDate
477 }
478 $Data = $data,$NEWXMLDATA
479 #}
480#}
481
482If ($Data -eq $Null)
483{ #Something went horribly wrong!
484 Write-Debug "No data found!"
485 Throw
486}
487Else
488{ #Delete oldest detail and debug files
489 Get-ChildItem $OutputLocation\dfsdetails*.html | Where {$_.CreationTime -lt $SaveDate} | Remove-Item
490 Get-ChildItem $DataLocation\debug*.log | Where {$_.CreationTime -lt $SaveDate} | Remove-Item
491
492 ##
493 ## Now build the detailed DFS monitor page
494 ##
495 Write-Debug "--Creating detailed monitoring page..."
496 $html = @()
497 $html = "<html><head>`n"
498 $html += "<style type='text/css'>`n"
499 $html += "table, th, td { border:1px solid black;border-collapse:collapse;padding-left:5px;padding-right:5px;}`n"
500 $html += "table { width:95%;}`n"
501 $html += "th { background-color:#000080;color:#FFFFFF;font:bold 18px arial,sans-serif;}`n"
502 $html += "tr.d0 {background-color:#4682b4;color:black;font:15px arial,sans-serif;}`n"
503 $html += "tr.d1 {background-color:#B0C4DE;color:black;font:15px arial,sans-serif;}`n"
504 $html += "</style></head><body>`n"
505 $html += "<div style=""width:95%;text-align:right;"">Report Date: $ScriptRunDate</div><br>"
506 If ($AlertReport)
507 { $html += "<B>Alerts:</B><br>`n"
508 ForEach ($Line in $AlertReport)
509 { $html += "<img src=""error_event.png"">" + $Line + "<br>`n"
510 }
511 }
512 $html += "<table border=""1"">`n"
513 $html += "<th>Replication Group</th><th>Replication Folder</th><th>Sending Partner</th><th>Receiving Partner</th><th>Backlog</th><th>Files</th>`n"
514 $TRDomain = "d1"
515 $NewData = $NewData | Sort
516 ForEach ($Line in $NewData)
517 { If ($TRDomain -eq "d1")
518 {$TRDomain = "d0"
519 }
520 Else
521 { $TRDomain = "d1"
522 }
523 $html += "<tr class=""$TRdomain"">"
524 $html += "<td>" + $Line[0] + "</td>"
525 $html += "<td>" + $Line[1] + "</td>"
526 $html += "<td>" + $Line[2] + "</td>"
527 $html += "<td>" + $Line[3] + "</td>"
528 If ($Line[4] -eq 0)
529 { $html += "<td>0</td>"
530 }
531 Else
532 { $html += "<td style=""background-color:red"">" + $Line[4] + "</td>"
533 }
534 If ($Line[5] -like "*Disabled*" -or $Line[5] -like "*WMI Error*")
535 { $html += "<td style=""background-color:red"">" + $Line[5] + "</td>"
536 }
537 Else
538 { $html += "<td>" + $Line[5] + "</td>"
539 }
540 $html += "</tr>`n"
541 }
542 $html += "</table></body></html>"
543 $html | Out-File $OutputLocation\DFSDetails$SaveFormatDate.html
544
545 # Now create the detail launch page
546 $html = @()
547 $html = "<html>`n"
548 $html += "<head><title>DFS Replication Details</title>`n"
549 $html += "<script type=""text/javascript"">`n"
550 $html += "function open_win() `n"
551 $html += "{ var myEle=document.getElementById('gopage');`n"
552 $html += " var myiFrame=document.getElementById('iframe');`n"
553 $html += " var myPage=myEle.options[myEle.selectedIndex].value;`n"
554 $html += " if (myPage != '') `n"
555 $html += " { myiFrame.src=myPage;`n"
556 $html += " }`n"
557 $html += "}`n"
558 $html += "</script></head>`n"
559 $html += "<body>`n"
560 $html += "<iframe id=""iframe"" width=""95%"" height=""95%"" src=""DFSDetails$SaveFormatDate.html""></iframe>`n"
561 $html += "<br>Show Report from: <select id=""gopage"" onChange=""open_win()"">`n"
562 $html += " <option value="""" selected></option>`n"
563 $Files = Get-ChildItem $OutputLocation\dfsdetails*.html | Sort CreationTime -Descending
564 ForEach ($File in $Files)
565 { $FileName = $File.Name.Substring(14,2) + "/" + $File.Name.Substring(16,2) + "/" + $File.Name.Substring(10,4) + " " + $File.Name.Substring(18,2) + ":" + $File.Name.Substring(20,2)
566 $html += " <option value=""" + $File.Name + """>" + $FileName +"</option>`n"
567 }
568 $html += "</select></body></html>`n"
569 $html | Out-File $OutputLocation\DFSDetailIndex.html
570
571 ##
572 ## Now create the Google visualization
573 ##
574 Write-Debug "--Now for the Annotated Timeline..."
575 $html = "<!DOCTYPE html>'n"
576 $html += "<html>`n"
577 $html += "<head>`n"
578 $html += "<script type='text/javascript' src='http://www.google.com/jsapi'></script>`n"
579 $html += "<script type='text/javascript'>`n"
580 $html += "google.load('visualization', '1', {'packages':['annotatedtimeline']});`n"
581 $html += "google.setOnLoadCallback(drawChart);`n"
582 $html += "function drawChart() {`n"
583 $html += "var data = new google.visualization.DataTable();`n"
584 $html += "data.addColumn('datetime', 'Date');`n"
585
586 #Define the columns
587 $UniqueReplGroups = $Data | Select RGGUID -Unique | Sort -Property RGGUID
588 $UniqueReplGroups = $UniqueReplGroups | Where {$_.RFName -ne ""}
589 ForEach ($Group in $UniqueReplGroups)
590 { $Data | Where {$_.RGGUID -eq $Group.RGGUID} | Select RFName -First 1 | ForEach {
591 $html += "data.addColumn('number', '" + $_.RFName + "');`n"
592 $html += "data.addColumn('string', '" + $_.RFName + "Status');`n"
593 $html += "data.addColumn('string', '" + $_.RFName + "ErrorMsg');`n"
594 }
595 }
596 $html += "data.addRows([`n"
597
598 $UniqueRunDates = $Data | Select RunDate -Unique | Sort -Property RunDate
599 $rec = 0
600 ForEach ($distDate in $UniqueRunDates)
601 { $NewLine = "[new Date(" + $distDate.RunDate.Year + "," + (($distDate.RunDate.Month) - 1) + "," + $distDate.RunDate.Day + "," + $distDate.RunDate.Hour + "," + $distDate.RunDate.Minute + "," + $distDate.RunDate.Second + ")"
602 $DatabyRunDate = $Data | Where {$_.RunDate -eq $distDate.RunDate}
603 ForEach ($Group in $UniqueReplGroups)
604 { $Line = $DatabyRunDate | Where {$_.RGGUID -eq $Group.RGGUID}
605 If ($Line.RFName)
606 { $NewLine += ", " + $Line.BacklogCount + ",undefined, undefined"
607 }
608 Else
609 { $NewLine += ", 0,undefined, undefined"
610 }
611 }
612 If ($rec -ge ($UniqueRunDates.Count - 1))
613 { $html += $NewLine + "]`n"
614 }
615 Else
616 { $html += $NewLine + "],`n"
617 $rec ++
618 }
619 }
620 $html += "]);`n"
621 $html += "var chart = new google.visualization.AnnotatedTimeLine(document.getElementById('chart_div'));`n"
622 $html += "chart.draw(data, {displayAnnotations: false, legendPosition: 'newRow'});`n"
623 $html += "}`n"
624 $html += "</script></head>`n"
625 $html += "<META HTTP-EQUIV=""REFRESH"" CONTENT=""1800"">`n"
626 $html += "<body>"
627 If ($AlertReport)
628 { $html += "<B>Alerts:</B><br>`n"
629 ForEach ($Line in $AlertReport)
630 { $html += "<img src=""error_event.png"">" + $Line + "<br>`n"
631 }
632 }
633 $html += "<div id=""chart_div"" style=""height: 400px;width:95%;""></div>`n"
634 $html += "<a href=""DFSDetailIndex.html"" target=""_blank"">Details from last run ($ScriptRunDate)</a>`n"
635 $html += "</body></html>"
636 $html | Out-File $OutputLocation\DFSMonitorGrid.html
637}
638
639#And Save the data
640Write-Debug "--Saving the data..."
641$Data = $Data | Where {$_.RFName -ne ""}
642$Data | Export-Clixml $DataLocation\DFSData.xml -Force
643
644#All done!
645Write-Debug "Done!"
646Stop-Transcript