I use NetPath in a number of ways, but the foremost is to validate that connections to SaaS applications are within tolerances all around my environment. Having that information in a summary is very helpful.
NetPath already does that, but having a filter would be nice to only show a specific service. What would be more awesome would be being able to give me summary information over time instead of just showing the most recent.
With that in mind, I started digging into the API to find if I could get a better summary of this information. Much to my pleasure, I was able to find the information, but it was strewn across a few tables. So I had to dust off some old SQL tactics in my brain to get the information. (I'm terrible at hand-writing join queries and almost as bad when doing GROUP BY for calculating minimums/averages/minimums).
The two "tables" that I need to use are Orion.NetPath.ServiceAssignments and Orion.NetPath.Tests.
What I came up with was a little bit of SQL magic, a little bit of SWQL magic, and exactly what I needed in a resource. I've added some comments in the script to show what I'm doing. Without further ado, here's the SWQL script:
SELECT [SA].ProbeName AS [Source] , [SA].ServiceName AS [Destination] , [SA].DetailsUrl AS [_LinkFor_Source] , CONCAT('/Orion/images/StatusIcons/Small-', [SI].IconPostfix, '.gif') AS [_IconFor_Source] -- This is the status for the most recent poll only , ROUND([Tests].MinLatency, 2) AS [Min Latency (ms)] , ROUND([Tests].AvgLatency, 2) AS [Avg Latency (ms)] , ROUND([Tests].MaxLatency, 2) AS [Max Latency (ms)] , CONCAT(ROUND([Tests].MinLatency, 2), ' / ', ROUND([Tests].AvgLatency, 2), ' / ', ROUND([Tests].MaxLatency, 2) ) AS [Min/Avg/Max Latency (ms)] , ROUND([Tests].MinPacketLoss, 2) AS [Min Packet Loss (%)] , ROUND([Tests].AvgPacketLoss, 2) AS [Avg Packet Loss (%)] , ROUND([Tests].MaxPacketLoss, 2) AS [Max Packet Loss (%)] , CONCAT(ROUND([Tests].MinPacketLoss, 2), ' / ', ROUND([Tests].AvgPacketLoss, 2), ' / ', ROUND([Tests].MaxPacketLoss, 2) ) AS [Min/Avg/Max Packet Loss (%)]
FROM Orion.NetPath.ServiceAssignments AS [SA]
INNER JOIN Orion.StatusInfo AS [SI] ON [SA].Status = [SI].StatusID
INNER JOIN ( SELECT EndpointServiceID , ProbeID , MIN(Rtt) AS MinLatency , AVG(Rtt) AS AvgLatency , MAX(Rtt) AS MaxLatency , MIN(PacketLoss) AS MinPacketLoss , AVG(PacketLoss) AS AvgPacketLoss , MAX(PacketLoss) AS MaxPacketLoss FROM Orion.NetPath.Tests WHERE ExecutedAt >= GETUTCDATE() - 1 -- ExecutedAt is stored in UTC, so we use 'GETUTCDATE() - 1' to get last 24 hours only GROUP BY EndpointServiceID, ProbeID
) AS [Tests]
ON [Tests].ProbeID = [SA].ProbeID
AND [Tests].EndpointServiceID = [SA].EndpointServiceID
WHERE [SA].ServiceName = 'Office 365' -- This is the NetPath Service Name as displayed on your NetPath summary page AND [SA].Enabled = 'True'
ORDER BY [SA].ProbeName
Let me break it down a little bit. First off, we are joining up three tables here. The one that I didn't mention earlier is Orion.ServiceInfo. This table has the status "names" based on the numbers. I use them for pulling in the icons. This leads to a bit of SWQL magic.
SWQL has the ability to recognize icons and links, but only if they are formatted properly. This is where lines 3 and 4 from the above script come into play. If you have an element in a custom query called "Stuff", then you can give it a URL named "_IconFor_Stuff" and it will be displayed to the left of the label. Similarly, you can use the "_LinkFor_Stuff" to provide a clickable link. As a note for the future, many tables provide a "DetailsUrl" field that's very useful for this.
I'm also using some specific SQL-like constructs - specifically joining a table to a table and joining a table to a query (which returns table-like content). In lines 14 and 15, I'm joining in the Orion.StatusInfo table which provides the IconPostfix needed for line 4.
Later in lines 16 through 28 I'm making a query from Orion.NetPath.Tests where I summarize information over the last 24 hours. Note that in line 26 I'm using a comparison to only pull back minimums, averages, and maximums over the last 24 hours. You can change this, but be aware that it will take more time to execute the query. On a 10 minute probe (the default), each probe provides 60 minutes / 10 minutes per test * 24 hours = 144 tests / probe. Multiple this by the number of probes (4 in my environment) means the query has to summarize 576 entries into six total statistics.
Thankfully SWQL is highly optimized. As an example me running this full query takes 0.023 seconds. If I change the number of days on line 26 to 7 (show me the last week), then it runs in 0.59 seconds. Like I said, the SWQL is highly optimized.
So what's all this look like? Just edit a page and add a Custom Query widget and paste in the above query (changing line 31 to match one of your own NetPath Services) and you should get something very pretty.
I've elected to show Min/Avg/Max in multiple ways, but in a production environment, I probably only care about average. To get just the average to display, just comment out (or delete) the unneeded elements. In SQL (and SWQL) the double-dash indicates a comment.
SELECT [SA].ProbeName AS [Source] , [SA].ServiceName AS [Destination] , [SA].DetailsUrl AS [_LinkFor_Source] , CONCAT('/Orion/images/StatusIcons/Small-', [SI].IconPostfix, '.gif') AS [_IconFor_Source] -- This is the status for the most recent poll only
-- , ROUND([Tests].MinLatency, 2) AS [Min Latency (ms)]
-- , ROUND([Tests].AvgLatency, 2) AS [Avg Latency (ms)]
-- , ROUND([Tests].MaxLatency, 2) AS [Max Latency (ms)] , CONCAT(ROUND([Tests].MinLatency, 2), ' / ', ROUND([Tests].AvgLatency, 2), ' / ', ROUND([Tests].MaxLatency, 2) ) AS [Min/Avg/Max Latency (ms)]
-- , ROUND([Tests].MinPacketLoss, 2) AS [Min Packet Loss (%)]
-- , ROUND([Tests].AvgPacketLoss, 2) AS [Avg Packet Loss (%)]
-- , ROUND([Tests].MaxPacketLoss, 2) AS [Max Packet Loss (%)] , CONCAT(ROUND([Tests].MinPacketLoss, 2), ' / ', ROUND([Tests].AvgPacketLoss, 2), ' / ', ROUND([Tests].MaxPacketLoss, 2) ) AS [Min/Avg/Max Packet Loss (%)]
FROM Orion.NetPath.ServiceAssignments AS [SA]
INNER JOIN Orion.StatusInfo AS [SI] ON [SA].Status = [SI].StatusID
INNER JOIN ( SELECT EndpointServiceID , ProbeID , MIN(Rtt) AS MinLatency , AVG(Rtt) AS AvgLatency , MAX(Rtt) AS MaxLatency , MIN(PacketLoss) AS MinPacketLoss , AVG(PacketLoss) AS AvgPacketLoss , MAX(PacketLoss) AS MaxPacketLoss FROM Orion.NetPath.Tests WHERE ExecutedAt >= GETUTCDATE() - 1 -- ExecutedAt is stored in UTC, so we use 'GETUTCDATE() - 1' to get last 24 hours only GROUP BY EndpointServiceID, ProbeID
) AS [Tests]
ON [Tests].ProbeID = [SA].ProbeID
AND [Tests].EndpointServiceID = [SA].EndpointServiceID
WHERE [SA].ServiceName = 'Office 365' -- This is the NetPath Service Name as displayed on your NetPath summary page AND [SA].Enabled = 'True'
ORDER BY [SA].ProbeName
So the above returns:
Hopefully this has helped a few of you with getting your feet wet within SWQL and the custom query. Now go forth and script around a bit.